Data Synchronization with BlazeDS: Handling AUTOINCREMENT

Upcoming release of the ClearDataBuilder supports AUTOINCREMENTed property in the DataCollection items populated from databases based on autogenerated values like MSSqlServer/ Sybase identity, or Oracle’s sequence, and so forth. All you need to do is add identity parameter to the CDB annotation
[quickcode:noclick] /**
* @daoflex:sql
* sql=select * from employee
* transferType=EmployeeDTO[]
* keyColumns=id
* identity=id
* updateTable=employee
*/

public abstract List getEmployees();[/quickcode]
and allow server changes to reflect back on the DataCollection:
[quickcode:noclick]dataCollection.roundTripSync = true; [/quickcode]


Sample Use Case

Imagine a MSSqlServer table “employee”, with IDENTITY column named “id”. Here is the abstract Java class you would need to write:

package com.farata.test;
import java.util.List;
/**
 * @daoflex:webservice
 *   pool=jdbc/test
 */
public abstract class Employee
{
    /**
    * @daoflex:sql
    *  sql=select * from employee
    *  transferType=EmployeeDTO[]
    *  keyColumns=id
    *  identity=id
    *  updateTable=employee
    */

    public abstract List getEmployees();
}

A Clean CDB build, as usual, will generate concrete implementation of the Assembler, already registered for Flex remoting, ActionScript DTO classes matching the Java counterparties and so forth.

Importantly, the specifics of the identity annotation will force CDB to enquire the value of the autogenerated id column along with the execution of the executeUpdate() and send the modified record back to the Flex client.


Two-Way Syncronization: DataCollection – BlazeDS Assembler

You have to allow the echo of the server-side changes back to the client. You have two options.

  • dataCollection.roundTripSync=true; – to allow Flex process the immediate result of “your own” dataCollection.sync() call
  • dataCollection.autoSyncEnabled=true; – to allow Flex consume server-born asyncronous messages with changes inflicted by other clients. This option requires design-time autoSyncEnabled=true CDB annotation as well.

    Here is the explanation. During the sync() operation changes originated in the DataCollection are remoted to method(s) of a server Assembler as Array of com.farata.remoting.ChangeObject elements. All of these methods are also returning the Array of changes, including, if any, changes originated by the server. The value of the roundTripSync determines whether these changes effect DataCollection when the synchronization is complete. Default value is false


    Pluggable Identity Factory

    No two databases are born equal. The default setting of the CDB is to handle identity compliant with MSSqlServer, i.e. to use SELECT SCOPE_IDENTITY() as the cleanest possible computation of the identity. If you are running Sybase, you would have to modify the default daoflex.identity.factory in the daoflex-build/daoflex-build-custom.properties:

    daoflex.identity.factory=com.farata.daoflex.SybaseIdentityFactory
    

    It gets worse if you are an Oracle sequence guy. Plugging a foo.bar.OracleIdentityFactory class that has to implement com.farata.daoflex.IIdentityFactory interface is not any different from the Sybase, but CDB does not know upfront which sequence to use. So roll up your sleeves and create something like following:
    package foo.bar;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.util.Map;
    
    import com.farata.daoflex.DAOException;
    import com.farata.daoflex.IIdentityFactory;
    
    public class OracleIdentityFactory implements IIdentityFactory {
      public long getIdentity(Map<String, Object> properties) {
        long identity = 0;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
        
          Connection conn = (Connection) properties.get(CONNECTION);
          String table = (String) properties.get(TABLE);
          String sequenceName="UNKNOWN_SEQUENCE";  
          // Your custom logic to determine the sequence give the table goes here    
          if (table==table) 
               sequenceName = "MY_SEQUENCE";      
          stmt = conn.prepareStatement("SELECT " + sequenceName + ".NEXTVAL AS identity FROM DUAL");
          rs = stmt.executeQuery();
          while( rs.next() ) {
            identity = rs.getLong("identity");
          }
          return identity;
        } catch (Throwable te) {
          te.printStackTrace();
          throw new DAOException("Failed creating identity", te);
        }
        finally {
          try {rs.close(); rs = null;} catch (Exception e){}
          try {stmt.close(); stmt = null;} catch (Exception e){}
        }
      }
    }
    

    Victor Rasputnis