DODS - Release Notes - Version 4.0
dods.gif (11357 bytes)Here's what's new in DODS 4.0:

Changes to the dods.conf file:

  1. More robust mapping of JDBC data types to specific database vendor data types.
  2. Added support for InstantDB.
  3. Fixed incorrect database types (for PostgreSQL, etc.)

Changes to the .doml files:

Formerly, the dbType for an attribute was the actual database type for a particular vendor. This required the GUI be used to change the vendor. Now, the dbType is a JDBC type, as listed in the dods.conf file. Now, to switch to a different database vendor, just change the line

 <database database="VENDOR">

where VENDOR is one of the vendors with entries in dods.conf.

New generated DOI intefaces:

  1. Each DO class implements a DOI interface.
  2. A business class (BO) that wraps a DO class can also implement the DOI.

Changes to the generated DO classes:

  1. DO classes now implement their corresponding DOI interface.
  2. Fixed a bug in which many-to-many relationships (i.e. a DO containing 2 references to other DOs) sometimes caused the DO class to refer to nonexistent methods.
  3. Javadoc is now generated for the RDBColumn named PrimaryKey in each DO.

Changes to the generated Query classes:

  1. setQueryXxx methods now use the static RDBColumn members declared in the DO class. This facilitated change #2.
  2. setQueryXxx methods now accept a QueryBuilder comparison operator. So to retrieve the persons older than 18, your application can do this:
    		PersonQuery xq = new PersonQuery();
    		xq.setQueryAge( 18, QueryBuilder.GREATER_THAN );
    		PersonDO[] adults = xq.getDOArray();
    

    instead of doing this:

    		PersonQuery xq = new PersonQuery();
    		QueryBuilder qb = xq.getQueryBuilder();
    		qb.addWhere( PersonDO.Age, 18, QueryBuilder.GREATER_THAN );
    		PersonDO[] adults = xq.getDOArray();
    
  3. Fixed typo in openParen() and closeParen() methods. See javadoc for details.
  4. Query classes are no longer 'final'.
  5. Maximum row count parameter can be passed to getDOArray().

Changes to QueryBuilder:

  1. Querying dates now uses the JDBC setDate() method instead of formatting the date to a vendor-specific string format. (Thanks to Victor Brilon.)
  2. All addWhereClause methods taking 5 arguments have been removed. These methods were formerly used by the generated Query classes (see above).
  3. All addWhereClause methods in which the first argument is a String containing a column name have been deprecated. Use the addWhere() methods in which the first argument is an RDBColumn object.
  4. New string comparison operators: Here are a series of calls to a Query class method setQueryName, and the where-clauses produced by QueryBuilder:
    	CALL:	PersonQuery pq = new PersonQuery();
    
    	CALL:	pq.setQueryName( "MaryJo", EQUAL );
    	WHERE clause:		name    =   'MaryJo'
    
    	CALL:	pq.setQueryName( "MaryJo", CASE_INSENSITIVE_EQUAL );
    	WHERE clause:	LOWER(	name )  =   'maryjo'
    		
    	CALL:	pq.setQueryName( "MaryJo", CASE_SENSITIVE_CONTAINS );
    	WHERE clause:		name   LIKE '%MaryJo%'
    
    	CALL:	pq.setQueryName( "MaryJo", CASE_INSENSITIVE_CONTAINS );
    	WHERE clause:	LOWER(	name ) LIKE '%maryjo%'
    
    	CALL:	pq.setQueryName( "MaryJo", CASE_SENSITIVE_STARTS_WITH );
    	WHERE clause:		name   LIKE 'MaryJo%'
    
    	CALL:	pq.setQueryName( "MaryJo", CASE_INSENSITIVE_STARTS_WITH	);
    	WHERE clause:	LOWER(	name ) LIKE 'maryjo%'
    
    	CALL:	pq.setQueryName( "MaryJo", CASE_SENSITIVE_ENDS_WITH );
    	WHERE clause:		name   LIKE '%MaryJo'
    
    	CALL:	pq.setQueryName( "MaryJo", CASE_INSENSITIVE_ENDS_WITH );
    	WHERE clause:	LOWER(	name ) LIKE '%maryjo'
    
  5. New IS_NULL and IS_NOT_NULL comparison operators:
    	CALL:	pq.setQueryName( null, QueryBuilder.IS_NULL );
    	WHERE clause:	        name IS NULL
    
    	CALL:	pq.setQueryName( null, QueryBuilder.IS_NOT_NULL );
    	WHERE clause:	        name IS NOT NULL
    
  6. New QueryBuilder.compare() methods: QueryBuilder has 3 new static methods that compare two values using the QueryBuilder comparison operators:
    	    static public boolean QueryBuilder.compare( 
    		    boolean a, boolean b, String comparison_operator )
    
    	    static public boolean QueryBuilder.compare( 
    		    double  a, double  b, String comparison_operator )
    
    	    static public boolean QueryBuilder.compare( 
    		    Object  a, Object  b, String comparison_operator )
    

    For a cache-enabled XxxDO class generated by DODS, the corresponding XxxQuery.setQueryYyy() methods use the new QueryBuilder.compare() methods to perform cache filtering. This allows the XxxQuery classes to perform more elaborate queries against the XxxDO cache. (Previously, only EQUALS comparisons were supported.) The QueryBuilder.compare() methods can be handy whereever an application must compare values.

  7. Simpler standalone use of QueryBuilder: Let's say you just want to generate a report containing the Name and Age of everyone in the Person table. You could use DO's, like so:
    		PersonQuery pq = new PersonQuery();
    		PersonDO[] people = pq.getDOArray();
                    for ( int i = 0; i < people.length; i++ ) {
                        print( people[i].getName() );
                        print( people[i].getAge() );
                    }
    

    But if an array of PersonDO's would eat too much memory, you could use just the QueryBuilder to get this data:

    		QueryBuilder qb = new QueryBuilder();
    		qb.select( PersonDO.Name );
    		qb.select( PersonDO.Age );
    		RDBRow row; 
    		while ( null != ( row = qb.getNextRow() ) ) {
    		    print( row.get( PersonDO.Name ).getString() );
    		    print( row.get( PersonDO.Age  ).getInt() );
    		}
    

    Let's say you also have an Address table, and each Person has a reference to an Address. You could list the names of cities containing people over 100 years old, like so:

    		QueryBuilder qb = new QueryBuilder();
    		qb.select( AddressDO.Name );
    		qb.addWhere( PersonDO.Age, 100, QueryBuilder.GREATER_THAN );
    		qb.addWhere( PersonDO.Address, AddressDO.PrimaryKey );
    		RDBRow row; 
    		while ( null != ( row = qb.getNextRow() ) ) {
    		    print( row.get( CityDO.Name ).getString() );
    		}
    

    QueryBuilder can also be used to read data from a legacy database. Let's say the Person table was _not_ created by DODS, and so there is no PersonQuery and PersonDO class.

    		RDBTable  person = new RDBTable( "Person" );
    		RDBColumn name   = new RDBColumn( person, "Name" );
    		RDBColumn age    = new RDBColumn( person, "Age" );
    		QueryBuilder qb = new QueryBuilder();
    		qb.select( name );
    		qb.select( age );
    		RDBRow row; 
    		while ( null != ( row = qb.getNextRow() ) ) {
    		    print( row.get( name ).getString() );
    		    print( row.get( age  ).getInt() );
    		}
    

Changes to stdrules.mk:

  1. Added rules to run DODS. Recommended application structure (with sample Makefile fragments):
    	    myApp/
    
    		config.mk
    			ENHYDRA_DIR=...
    			include $(ENHYDRA_DIR)/stdrules.mk
    
    		Makefile
    			SUBDIRS=database business presentation
    
    	    myApp/
    		database/
    
    			myApp.doml	(DODS 'sourcecode' for myApp data layer)
    
    			Makefile	(see notes below)
    				ROOT=..
    				DODS_DOML=$(ROOT)/myApp/database/myApp.doml
    				DODS_TARGET_DIR=$(ROOT)/myApp/data
    				include $(ROOT)/config.mk
    
    	    myApp/
    		data/		(target directory for DODS)
    
    	    myApp/
    		business/	(source for business classes)
    
    	    myApp/
    		presentation/	(source for presentation classes)
    

    New rules in stdrules.mk analyze the .doml file specified by DODS_DOML to determine whether DODS needs to be run to generate the DO.java and Query.java files in the DODS_TARGET_DIR directory.

Changes to DODS builder:

  1. Fixed problems with mixed path separator characters when generating code on Windows systems.
  2. BDO generator is turned off.
  3. New warnings are issued if the package hierarchy does not mesh with the target directory.
    1. In this example, the target directory and package hierarchy "mesh":
      	    Target dir:	/home/joe/projectX/com/projectx/data
      	    DO:		com.projectx.data.PersonDO
      	    Generates:	/home/joe/projectX/com/projectx/data/PersonDO.java
      
    2. But in this example, the package hierarchy has an extra level "foo" which causes it to not align with the target directory hierarchy, and DODS will issue warnings:
      	    Target dir:	/home/joe/projectX/com/projectx/data
      	    DO:		com.projectx.foo.data.PersonDO
      	    Generates:	/home/joe/projectX/com/projectx/data/
      				com/projectx/foo/data/PersonDO.java
      
    3. And in this example, the target directory contains the name "projx" where the package hierarchy has "projectx", so again the the target directory and package hierarchy do not "mesh" and DODS will issue warnings:
      	    Target dir:	/home/joe/projectX/com/projx/data
      	    DO:		com.projectx.foo.data.PersonDO
      	    Generates:	/home/joe/projectX/com/projx/data/
      				com/projectx/foo/data/PersonDO.java
      

    In examples #2 and #3, DODS places the entire package hierarchy underneath the target directory, which is almost never what you want. In such situations, the generated code will probably not compile because the generated Makefile will have an incorrect value for ROOT.

For all the latest information on DODS, please refer to http://dods.enhydra.org
Questions, comments, feedback? Let us know...