Previous Index Next

1 Getting Started with AuDAO

In this chapter we would like to show the basic constructs of the XML AuDAO configuration source file.

1.1 Create the Source XML

The first step when using AuDAO is to create the source configuration XML which describes the tables/entities and therefore tells AuDAO how to generate the SQLs and Java DAO classes.

The description of XML elements is covered by this documentation, or you can consult the XSD documentation or directly the AuDAO XSD.

1.1.1 Basic Example XML

The following example shows an elementary example - BTW this table is also used by this application to remember registered users (Source XML):

<?xml version="1.0" encoding="utf-8"?>
<database xmlns="http://www.spoledge.com/audao" version="1.0">

  <config>
    <factory>
      <create-params default="false" direct="true"/>
    </factory>
  </config>

  <tables>

    <table name="registered_users">
      <edit-mode>column</edit-mode>
      <auto-find/>
      <columns>
        <column name="user_id">
          <type>long</type>
          <auto/>
          <pk/>
        </column>
        <column name="gae_user_id">
          <type max-length="500">String</type>
          <not-null/>
        </column>
        <column name="user_name">
          <type max-length="500">String</type>
          <not-null/>
        </column>
        <column name="created_date">
          <type>Date</type>
          <auto/>
          <not-null/>
        </column>
        <column name="last_login_date">
          <type>Timestamp</type>
          <auto/>
          <not-null/>
          <edit/>
        </column>
      </columns>
      <indexes>
        <index name="inx_user_gaeid">
          <unique/>
          <columns>
            <column name="gae_user_id"/>
          </columns>
        </index>
      </indexes>
    </table>

  </tables>
</database>

The table name is registered_users which will be automatically transformed to Java names RegisteredUser (DTO), RegisteredUserDao (abstract DAO) and RegisteredUserDaoImpl (DAO implementation). Developers can force the generator to use a special Java name by setting optional attribute <table name="registered_users" java="MyRegisteredUser">.

The table defines 5 columns. The column names are again transformed to Java names if not explicitly set. For example the first column name user_id will be transformed into Java name userId and the DTO will have getter and setter:

public Long getUserId() {...}
public void setUserId( Long userId ) {...}
Note that primitive types are transformed into object types (long to Long) - this allows to get/set null values.

1.1.2 Definition of Columns

The definition of table's (or entity's) columns within the tag <columns> is the first task when creating the AuDAO XML. It tells AuDAO how the datastore structure looks like and allows to generate basic SQLs and Java DAO.

Let's look closer at the definition of the first column:

        <column name="user_id">
          <type>long</type>
          <auto/>
          <pk/>
        </column>
Except the column's type we also declare that this is the primary key (<pk/>) and thus it cannot be null. AuDAO will generate these SQL constraints automatically.

The existence of a primary key and the flag <auto-find/> tells AuDAO to automatically generate this method:

public RegisteredUser findByPrimaryKey( long userId )
in the DAO layer.

The <auto/> flag indicates, that the field is automatically filled by the DAO implementation when a new record is inserted. And since this table is not read-only (no <read-only/> flag is present), then AuDAO will also generate the following method:

public long insert( RegisteredUser dto ) throws DaoException;

Let's continue to the second column:

        <column name="gae_user_id">
          <type max-length="500">String</type>
          <not-null/>
        </column>
Since this type is a String, we must declare the maximum length of the string. The DAO layer will check the length of the stored data for this field and will throw an exception with a readable description of the problem and the column name. So you will never get anonymous errors like "ORA-01401: inserted value too large for column".

The DAO implementation for GAE also automatically converts java.lang.String and com.google.appengine.api.datastore.Text when the max-length exceeds number 500.

The <not-null/> flag indicates that we cannot store null values to the field. DAO also checks this constraint for you. Even better - for string type you can specify also the minimum characters by the min-length attribute.

Now skip one column and go to the last one which is more interested:

        <column name="last_login_date">
          <type>Timestamp</type>
          <auto/>
          <not-null/>
          <edit/>
        </column>

This is a Timestamp column - it contains both date and time. As you can see, you can define the <auto/> flag also for Date and Timestamp columns. In this example, DAO automatically sets the column's value by the current date/time when a new record is inserted. So you can leave it null before calling the insert method.

Let's focus on the <edit/> flag now. It indicates, that this column is editable. In conjunction with the table's option <edit-mode> - which value is column the DAO will generate this method:

public boolean updateLastLoginDate( long userId, Timestamp lastLoginDate ) throws DaoException;

1.1.3 Definition of Indexes

After you define table's columns, you should define indexes. There are three important reasons why to do it:

  1. AuDAO will generate the indexes in SQL sources
  2. AuDAO can automatically generate finder methods using these indexes
  3. You can reference these indexes later when specifying conditions for explicit finder methods or other types of methods (count, update, delete...)
This is important, because developers using such DAO methods can rely on existing indexes in datastore, so the underlying queries will not perform a database full-scan.

Let's look at our example - we define only one index:

        <index name="inx_user_gaeid">
          <unique/>
          <columns>
            <column name="gae_user_id"/>
          </columns>
        </index>
Every index has its name - it it used in the SQL definition of the index and it is also used when you need to reference it later in the source XML.

This index is <unique/> and indexes only one column gae_user_id. So the SQL definition for Oracle will look as follows:

CREATE UNIQUE INDEX inx_user_gaeid ON registered_users (
	gae_user_id
);

Finally the definition of this index in conjuction with the table's flag <auto-find/> tells AuDAO to automatically generate this finder method:

public RegisteredUser findByGaeUserId( String gaeUserId );
which returns at most one object (the index is unique).

1.1.4 Definition of Methods

The optional next step is to define additional expicit methods. You will find that for simple entities you can skip this step - as we in our example.

To show this feature we will add the following XMLlet after the <indexes>..</indexes> section:

      <methods>

        <delete>
          <pk/>
        </delete>

        <find name="Expired">
          <condition>
            <query><![CDATA[last_login_date > ?]]></query>
            <query dbtype="gae"><![CDATA[lastLoginDate > :1]]></query>
            <query dbtype="gaejdo"><![CDATA[lastLoginDate > :_lastLoginDate]]></query>
            <query><![CDATA[last_login_date > ?]]></query>
            <params>
              <column name="last_login_date"/>
            </params>
          </condition>
        </delete>

      </methods>

This will create two more methods in the DAO layer:

public boolean deleteByPrimaryKey( long userId ) throws DaoException;
public RegisteredUser[] findExpired( Timestamp lastLoginDate );

The name of the delete method is generated automatically, because all methods which take primary key argument - <pk/> - are called "<method-type>ByPrimaryKey".

The second method is a finder which uses explicit condition. You can see that you can define different conditions for different datastore types ("gae" = Google App Engine, "gaejdo" = GAE/JDO) using native queries (SQL, GQL or JDOQL). This allows you to develop code portable between different datastores.

The finder method does not use any defined index. Developers should avoid such situation. In this case we just wanted to show explicit conditions which can be useful when doing more complex queries.

1.2 Generate SQLs and Java Sources

Once you have created you source configuration XML for AuDAO, you can go ahead and let AuDAO work for you !

The generator - either the online or the standalone one - must know the following parameters for generating the code:

  • DB or datastore type: currently it can be either MySQL (mysql), Oracle (oracle), Google App Engine (gae) or Google App Engine using JDO (gaejdo).
  • Java package name to generate the code into: for example if the package name is com.foo, then the following subpackages are generated:
    • com.foo.dto which contains all DTO classes
    • com.foo.dto.gae - only for Google App Engine using JDO db-type - which contains all GAE DTO implementation classes
    • com.foo.dao which contains all DAO classes
    • com.foo.dao.db-type which contains all DAO implementation classes for db-type (mysql, oracle, gae or gaejdo)
And of course you must also provide the source configuration XML.

1.2.1 Using the Online Tool

The AuDAO online generator allows you to quickly generate your code. Just fill the required fields and click the button "Generate".

If everything goes ok, then a download dialog should appear and you can save the zipped SQL and Java source to your local disk:

Otherwise you will get an error like:

The source XML is not valid
line 7 column 15 after 'auto-find': cvc-complex-type.2.4.a: Invalid content was found starting with element 'edit-mode'. One of '{"http://www.spoledge.com/audao":columns, "http://www.spoledge.com/audao":indexes, "http://www.spoledge.com/audao":methods, "http://www.spoledge.com/audao":data}' is expected.

If it is a XML syntax error (as shown above), please consult the AuDAO XML Schema Documentation or directly the AuDAO XSD.

If you get the following error message:

The source configuration XML file is too large.
then it means that you reach the limits of the uploaded source XML file which are:
  • 2kB for anonymous users
  • 8kB for users logged in using their Google Account
So if you are not logged in, just log in. If you are already logged in, then probably you need to use a standalone AuDAO tool.

1.2.2 AuDAO Standalone - Ant Tools

The standalone AuDAO tool contains Apache Ant build tools (see the Apache Ant project). The next example shows how easy is to generate DAO, compile it and make a JAR:

<?xml version="1.0"?>
<project name="test" basedir="." default="dist">

  <property name="audao.home" location="/usr/local/audao-1.0"/>

  <import file="/tools/build-audao.xml"/>

  <target name="dist">
    <antcall target="audao-jar">
      <param name="audao-xml" location="src/my-config.xml"/>
      <param name="audao-gen-dir" location="build/audao"/>
      <param name="audao-dbtype" value="oracle"/>
      <param name="audao-package" value="com.foo"/>
      <param name="audao-jar" location="dist/test-db-oracle.jar"/>
    </antcall>
  </target>

</project> 
Instead of the target audao-jar, you can call audao-compile to generate and compile classes or audao-generate to only generate the classes.

Using of predefined Ant targets and tasks is the preferred way how to invoke AuDAO.

You can also call the AuDAO Java classes or Ant tasks directly, but there exist some traps with passing correct classpath to it.

See also: Generator Tools

1.3 Use the Generated Java DAO

You've created the source XML and AuDAO generated a set of classes for you. Let's look how to use them.

1.3.1 Compiling DAO Sources

The first thing you need to do is to compile the Java sources. If you used the Ant's target audao-compile or audo-jar, then you have already the classes compiled ! Otherwise the only thing you must know is the library dependency of the generated sources.

Currently AuDAO generates code which always uses Apache Commons Logging library. If you generate code for standard databases - MySQL or Oracle, then you do not need anything else to compile the sources.

If you generate code for Google App Engine (gae), then you also need the GAE libraries which are located in the GAE SDK lib directory.

If you generate code for Google App Engine using JDO (gaejdo), then you also need the JDO2 libraries which are located in the GAE SDK lib directory.

1.3.2 Using the DAO

First of all a DAO class must be obtained. Depending on your config file and target database type you must either provide an instance of java.sql.Connection (SQL DB), com.google.appengine.api.datastore.DatastoreService (GAE) or javax.jdo.PersistenceManager (GAE/JDO) to the DaoFactory methods or either let the DaoFactory to obtain the instance itself.

The following example shows using the DAO layer (over GAE) - we pass DatastoreService to DaoFactory - as we defined it in config/factory/create-params section of the config file:

import com.foo.dao.DaoFactory;
import com.foo.dao.RegisteredUserDao;
import com.foo.dto.RegisteredUser;

import com.spoledge.audao.db.dao.DaoException;
...

/**
 * Gets or creates a new user.
 * @param gaeId the Google App Engine unique user id
 * @param name a name of the user (full name / email / nick )
 */
public RegisteredUser getOrCreateUserByGaeId( String gaeId, String name )
      throws DaoException {

    RegisteredUserDao dao = DaoFactory.createRegisteredUserDao( getDatastoreService());
    RegisteredUser ret =  dao.findByGaeUserId( gaeId );

    if (ret == null) {
        ret = new RegisteredUser();
        ret.setGaeUserId( gaeId );
        ret.setUserName( name );

        dao.insert( ret );
    }
    else {
        dao.updateLastLoginDate( ret.getUserId(), now());
    }

    return ret;
}

The other option is to define the default DaoFactory methods - no parameters are passed to them:

  <config>
    <factory>
      <create-params default="true"/>
    </factory>
  </config> 
You obtain DAO as shown in the next example:
    RegisteredUserDao dao = DaoFactory.createRegisteredUserDao(); 

Then for Google App Engine you must install a DatastoreServiceProvider (API) into the DaoFactory implementation - only once before any DAO is used (the similar approach is used for standard databases - use ConnectionProvider (API) - or for GAE/JDO use PersistenceManagerProvider (API)). The following example shows a thread-safe approach for both non-transactional and transactional (with retrying) calls:

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Transaction;

import com.spoledge.audao.db.dao.gae.DatastoreServiceProvider;

import com.foo.dao.gae.DaoFactoryImpl;

public class Foo {

    /**
     * Our per Thread context.
     */
    private static class ThreadContext {
        DatastoreService ds;
    }

    /**
     * All contexts are stored here.
     */
    private static final ThreadLocal<ThreadContext> contexts = new ThreadLocal<ThreadContext>() {
        @Override
        protected ThreadContext initialValue() {
            return new ThreadContext();
        }
    };

    static {
        // inject the provider of DatastoreService into the DAO implementation:
        DaoFactoryImpl.setDatastoreServiceProvider( new DatastoreServiceProvider() {
            public DatastoreService getDatastoreService() {
                return getDS();
            }
        });
    }

    /**
     * Returns the thread's local DatastoreService.
     * @return the existing DatastoreService or creates a new one
     */
    private DatastoreService getDS() {
        ThreadContext ctx = contexts.get();
        if (ctx.ds == null) ctx.ds = DatastoreServiceFactory.getDatastoreService();

        return ctx.ds;
    }


    /**
     * Example of usage.
     */
    public void useIt() {
        try {
            // call dao methods here
        }
        finally {
            // release reference
            contexts.get().ds = null;
        }
    }


    /**
     * Example of usage - all DAO calls in one Transaction.
     * If commit fails, then it retries it.
     */
    public void useItInTransaction() {
        // max attempts when retrying
        int MAX_ATTEMPTS = 3;

        for (int i=0; i < MAX_ATTEMPTS; i++) {
            Transaction tx = null;
            boolean success = false;

            try {
                tx = getDS().beginTransaction();

                // call dao methods here

                success = true;
            }
            catch (java.util.ConcurrentModificationException e) {
                if (i+1 == MAX_ATTEMPTS) throw e;
            }
            finally {
                // release reference
                contexts.get().ds = null;

                if (success) {
                    try {
                        tx.commit();
                        return;
                    } catch (Throwable t) {}
                }
                else if (tx != null) {
                    tx.rollback();
                }
            }
        }
    }
} 

We recommend to use Java proxy mechanism to make the code more readable. The proxy can start and commit/rollback transactions for annotated methods and/or release resources (Connection, PersistenceManager) for all methods.

Previous Index Next
Free Online Tool

Try our free online DAO generator.

Upload the configuration XML file and get the generated SQL scripts and Java classes for free !

Free GQL Parser

Download source or binary of our open-source GQL parser.

Query q =
 new GqlDynamic()
  .parseQuery(
   "SELECT * FROM Ent");
All GQL features are supported. More...