Typical DODS application deals with Transaction and Query objects. The later type covers R of the CRUD acronym, while the former is responsible for C, U & D operations. Grouping of multiple operations together is done via explicit usage of DBTransaction objects, and that basically covers everything DODS application has to care about:
DBTransaction dbt; try { dbt = DODS.getDatabaseManager().createTransation(); DiscQuery qry = new DiscQuery(dbt); qry.setQueryOwner(person); qry.addOrderByArtist(); DiscDO[] arr = qry.getDOArray(); // ... dbt.commit(); } catch (Exception e) { dbt.rollback(); } finally { dbt.release(); }
DODS was extracted out of Enhydra Server, and for the quite same time it tended to be complete self sufficient solution for O/R mapping. DODS has it’s own connection pooling, transaction control, cache implementations,… Since Enhydra was stripped off of the multi-server part, and recent versions can be plugged into various servlet containers, DODS had to follow the lead. It learned to recognize and use DataSources, their pools also, but remained its own boss with DBTransaction control, oblivious of JTA standard.
JTA introduces lots of features, two phase commit being the most visible, but API usage remains rather simple: begin an UserTransaction, get connections out of a DataSource, do work on those connections and close them, then either commit or roll back instance of UserTransaction. Transaction manager will keep you from getting the second UserTransaction active in the same thread, you cannot reuse transaction object until beginning it again.
UserTransaction ut; try { ut = ((UserTransaction) new InitialContext() .lookup("java:comp/UserTransaction")); DataSource ds = ((DataSource)new InitialContext() .lookup("java:comp/datasource/app1")); Connection conn = ds.getConnection(); conn.executeUpdate(“DELETE FROM TEMPTABLE”); // ... conn.close(); ut.commit(); } catch (Exception e) { ut.rollback(); }
After some lessons learned on the XA path, DODS 7 introduces compatibility mode with the JTA compliant environments:
generate layer with dirtyDO=”Compatible”,
use the Transaction and Query objects like before,
forget about DBTransaction, since it’s not in charge anymore, there is
UserTransaction to control transaction's behavior.
UserTransaction ut; try { ut = ((UserTransaction) new InitialContext() .lookup("java:comp/UserTransaction")); DiscQuery qry = new DiscQuery(); qry.setQueryOwner(person); qry.addOrderByArtist(); DiscDO[] arr = qry.getDOArray(); // ... ut.commit(); } catch (Exception e) { ut.rollback(); }
set parmeters in configuration file to appropriate values (explanation is following),
behind the scenes, generated layer will keep creating transactions the old style, but set of configuration parameters and DODS' transaction factory will make things work. Mapping of active UserTransaction to DODS' DBTransactions, factory will choose whether to really construct DBTransaction anew, or to serve back one previously created.
To use JTA in DODS, the following appicatation parameters are important (in configuration file):
JTA - must be set to true, in order to use JTA scenario in DODS.
Example:
DatabaseManager.defaults.JTA=true
TransactionFactory - must be set to class SyncDBTransactionFactory, which provides DBTransaction objects that are aware of JTA environment, and also obey TransactionManager.
Instances returned by this factory implement both javax.transaction.Synchronization and DBTransaction, thus solve hierachy problem between DODS and XADataSource connections.
Example:
DatabaseManager.defaults.TransactionFactory=org.enhydra.dods.jta.SyncDBTransactionFactory
XATransactionManagerLookupName
Example:
DatabaseManager.defaults.XATransactionManagerLookupName="java:comp/UserTransaction"
ConnectionAllocator - must be set to a class that implements com.lutris.appserver.server.sql.ExtendedConnectionAllocator interface, and uses DataSource (javax.sql.DataSource) as a connection source.
Example:
DatabaseManager.defaults.ConnectionAllocator="com.lutris.appserver.server.sql.datasource.DataSourceConnectionAllocator"
DisableConnectionPool - must be set to true if parameter ConnectionAllocator is set to value com.lutris.appserver.server.sql.datasource.DataSourceConnectionAllocator or to value com.lutris.appserver.server.sql.datasource.SimpleDataSourceConnectionAllocator.
Example:
DatabaseManager.DB.<LogicalDatabaseName>.Connection.DisableConnectionPool=true
ClassName - must be set to JTAObjectIdAllocator implementation of ObjectIdAllocator interface.
Example:
DatabaseManager.DB.<LogicalDatabaseName>.ObjectId.ClassName=org.enhydra.dods.jta.JTAObjectIdAllocator
ConnectionFactory - at the moment, it is set to com.lutris.appserver.server.sql.DataSourceDBConnectionFactory class.
Example:
DatabaseManager.DB.<LogicalDatabaseName>.Connection.ConnectionFactory="com.lutris.appserver.server.sql.datasource.DataSourceConnectionAllocator"
DataSourceName - must be to JNDI name (in "jndi:<dataSourceName>" formath ) of externally defined DataSource that can be used by DODS to establish connection to database.
Example:
DatabaseManager.DB.<LogicalDatabaseName>.Connection.DataSourceName="jndi:java:comp/datasource/discRackdb"
XAWrappedTransImplFactory - this parameter is optional. It defines factory class of wrapped transaction (transaction encapsulated inside UserTransaction). If not defined, the default value is StandardDBTransactionFactory).
Example:
DatabaseManager.defaults.XAWrappedTransImplFactory="org.enhydra.dods.dbtransaction.ExtendedTxFactory"