|
|||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | ||||||||
java.lang.Objectorg.opensubsystems.core.persist.db.DatabaseTransactionFactoryImpl
org.opensubsystems.core.persist.db.transaction.SimpleLocalTransactionFactoryImpl
public class SimpleLocalTransactionFactoryImpl
Simplified transaction management implementation based on database connection using following assumptions: Assumptions: ------------ 1. Most applications use/access only single database, therefore there is no need for distributed transaction implementation. 2. Applications consists of threads and each thread is using/needing only single database connection at a time. Most threads will never need two connections at the same time to the same database. 3. At most one transaction is in progress at any time in one thread. Therefore if an application is accessing only a single database and threads of the application use only single connection at a time and start at most one transaction at a time the application can use this implementation of transaction manager. How it works: ------------- 1. Thread is using JDBC database connection to access the database. 2. If thread starts an transaction using UserTransaction.begin then the connection used to access the database in this transaction has to have autocommit set to false and the thread cannot issue commit or rollback on the transaction. Then when thread ends transaction using UserTransaction.commit or UserTransaction.rollback then the transaction manager commits or rollbacks connections which was used to access the database within this transaction. 3. If thread is accessing the database outside of transaction, it can do whatever it wants with the connection including setting autocommit to true and calling commit and rollback on the connection. There are 4 basic and 4 combined scenarios possible: ---------------------------------------------------- 1. Connection is requested before the transaction and returned after the transaction DatabaseConnectionFactory.requestConnection UserTransaction.begin UserTransaction.commit/rollback DatabaseConnectionFactory.returnConnection 2. Connection is requested in the transaction and returned after the transaction UserTransaction.begin DatabaseConnectionFactory.requestConnection UserTransaction.commit/rollback DatabaseConnectionFactory.returnConnection 3. Connection is requested before the transaction and returned in the transaction DatabaseConnectionFactory.requestConnection UserTransaction.begin DatabaseConnectionFactory.returnConnection UserTransaction.commit/rollback 4. Connection is requested in the transaction and returned in the transaction UserTransaction.begin DatabaseConnectionFactory.requestConnection DatabaseConnectionFactory.returnConnection UserTransaction.commit/rollback 5. Connection is requested before the transaction and returned in the transaction and then connection is requested again ithe transaction and returned in the transaction DatabaseConnectionFactory.requestConnection UserTransaction.begin DatabaseConnectionFactory.returnConnection DatabaseConnectionFactory.requestConnection DatabaseConnectionFactory.returnConnection UserTransaction.commit/rollback 6. Connection is requested before the transaction and returned in the transaction and then connection is requested again ithe transaction and returned after the transaction DatabaseConnectionFactory.requestConnection UserTransaction.begin DatabaseConnectionFactory.returnConnection DatabaseConnectionFactory.requestConnection UserTransaction.commit/rollback DatabaseConnectionFactory.returnConnection 7. Connection is requested in the transaction and returned in the transaction and then connection is requested again ithe transaction and returned after the transaction UserTransaction.begin DatabaseConnectionFactory.requestConnection DatabaseConnectionFactory.returnConnection DatabaseConnectionFactory.requestConnection UserTransaction.commit/rollback DatabaseConnectionFactory.returnConnection 8. Connection is requested in the transaction and returned in the transaction and then connection is requested again ithe transaction and returned in the transaction UserTransaction.begin DatabaseConnectionFactory.requestConnection DatabaseConnectionFactory.returnConnection DatabaseConnectionFactory.requestConnection DatabaseConnectionFactory.returnConnection UserTransaction.commit/rollback What are the issues: -------------------- A. If thread acquires connection before the transaction is started then at the time when the transaction is started we have to associate the connection with the transaction. Affects: 1,3,5,6 A.1 If the connection wasn't used within the transaction the the commit or rollback on transaction should be a no op. B. If the thread acquired connection during the transaction then this connection should be automatically associated with the transaction Affects: 2,4,5,6,7,8 C. If thread returns the connection before the transaction is finished then the transaction manager has to keep the connection open until the thread ends the transaction. Affects: 3,4,5,8 D: If the thread keeps the connection after the transaction is finished then after the transaction is finished the connection should be fully usable as any other JDBC connection including autocommit and commit/rollback but in the transaction the autocommit/commit/rollback should be disabled. Affects: 1,2,6,7 E: If the thread requests and returns connection multiple times during the same transaction then the same connection has to be given to it since the transaction is responsible for doing commit/rollback on the connection and our assumption is that the thread is using only single connection. Affects: 5,6,7,8 What are the solutions: ----------------------- A: The DatabaseConnectionFactory has to keep track if there is a connection issued to the calling thread so that when the transaction is started then this connection can be associated to the transaction. This can be done using ThreadLocal storing the requested connection. Then when the transaction is started, the UserTransaction has to check if there is an already requested connection and associate it with the transaction. This can be done using ThreadLocal storing the transactional connection. A.1 This can be done be creating wrapper around Connection. The DatabaseConnectionFactory will then on request and return create this wrapper which delegates all calls into underlying connection. When any method is called, it marks the connection as used. The transaction manager can then reset the used flag when the connection is first time associated with transaction or check it once commit or rollback were issued. B: The DatabaseConnectionFactory has to be aware of the started transaction and when an connection is requested then the connection is associated with the transaction. This can be done using ThreadLocal storing a flag if the transaction is in progress or not. If the transaction is in progress then the returned connection will be stored in the transactional connection. C: The UserTransaction has to keep track of the connection which was used in the transaction and don't allow to return it until the transaction is finished. Then, once the transaction is finished it has to return the connection. This can be done using ThreadLocal storing a flag for the transactional connection if the connection was returned in the transaction or not. D: The connection should be aware if there is a transaction in progress or not and based on that allow autocommit and commit or rollback or not. This can be done be creating wrapper around Connection. The DatabaseConnectionFactory will then on request and return create this wrapper which delegates all calls into underlying connection and intercepts and checks autocommit and commit and rollback. E. The DatabaseConnectionFactory has to be aware of the started transaction and when a connection is requested then the connection already associated with the transaction should be returned. What are the implications: -------------------------- 1. If the connection is associated with transaction it is not returned to the factory and not available for others. This is natural since there are some operations pending on that connection and even though the thread said it no longer needs it, it cannot be reused until the operations are flushed with commit/rollback. Implementation: --------------- 1. D. requires to create wrapper around connection. We will implement TransactionalConnection class as wrapper around JDBC Connection class, which will delegate all operations directly except setautocommit/commit/rollback, which will be delegated only after check for pending transaction. 2. The previous step required us to create integration between DatabaseTransactionFactory and DatabaseConnectionFactory, so that the transaction factory can act as a proxy for connection factory and get the real connection and wrap it with a wrapper. This will be implemented as a delegator pattern when DatabaseConnectionFactoryImpl will be delegating calls to DatabaseTransactionFactoryImpl and vice versa. The requestConnection call will - check if there is an transaction in progress, if there is and it has already associated connection then the connection associated with the transaction will be returned. - if there is no transaction in progress and there is already connection issued then return the same connection since this would represent the situation (such as subprocedure call) DatabaseConnectionFactory.requestConnection DatabaseConnectionFactory.requestConnection DatabaseConnectionFactory.returnConnection DatabaseConnectionFactory.returnConnection - if there is no transaction in progress and no already issued connection then get connection from underlying connection factory, create wrapper and remember the connection as issued using ThreadLocal. This will solve A, B and E The returnConnection call will - check if there is an transaction in progress, if there is then the connection will not be returned to the factory and will be returned when the transaction is done - if there is no transaction in progress then the underlying connection will be returned to the factory This will solve C 3. We will provide implementation of UserTransaction. The begin call will - check if there was connection issued and if it was, it will associate it with this transaction. It will also tell the connection that it is part of the transaction to ignore the setautocommit/commit/rollback calls. The commit call will - if there is connection associated with this transaction, it will commit the connection. If the connection was returned to the factory in the transaction then this time it will be really returned to the factory otherwise it will be just disassociated from this transaction so that the setautocommit/commit/rollback calls are no longer ignored. The rollback call will - if there is connection associated with this transaction, it will rollback the connection. If the connection was returned to the factory in the transaction then this time it will be really returned to the factory otherwise it will be just disassociated from this transaction so that the setautocommit/commit/rollback calls are no longer ignored. TransactionalConnection To distinguish if the TransactionalConnection was associated in transaction it will have inTransaction flag. To distinguish if the TransactionalConnection was used during the transaction it will have used flag which will be false initially and set to true if any method on connection is called. To distinguish if the TransactionalConnection was returned to factory or it is still used by application it will have active counter which will be incremented when the connection is requested from factory and decremented when it is returned.
| Field Summary | |
|---|---|
static java.lang.Integer |
STATUS_ACTIVE_OBJ
A transaction is associated with the target object and it is in the active state. |
static java.lang.Integer |
STATUS_COMMITTED_OBJ
A transaction is associated with the target object and it has been committed. |
static java.lang.Integer |
STATUS_COMMITTING_OBJ
A transaction is associated with the target object and it is in the process of committing. |
static java.lang.Integer |
STATUS_MARKED_ROLLBACK_OBJ
A transaction is associated with the target object and it has been marked for rollback, perhaps as a result of a setRollbackOnly operation. |
static java.lang.Integer |
STATUS_NO_TRANSACTION_OBJ
No transaction is currently associated with the target object. |
static java.lang.Integer |
STATUS_PREPARED_OBJ
A transaction is associated with the target object and it has been prepared, i.e. |
static java.lang.Integer |
STATUS_PREPARING_OBJ
A transaction is associated with the target object and it is in the process of preparing. |
static java.lang.Integer |
STATUS_ROLLEDBACK_OBJ
A transaction is associated with the target object and the outcome has been determined as rollback. |
static java.lang.Integer |
STATUS_ROLLING_BACK_OBJ
A transaction is associated with the target object and it is in the process of rolling back. |
static java.lang.Integer |
STATUS_UNKNOWN_OBJ
A transaction is associated with the target object but its current status cannot be determined. |
| Fields inherited from class org.opensubsystems.core.persist.db.DatabaseTransactionFactoryImpl |
|---|
s_bTransactionMonitor, s_iTransactionTimeout, TRANSACTION_MONITOR, TRANSACTION_MONITOR_DEFAULT, TRANSACTION_TIMEOUT, TRANSACTION_TIMEOUT_DEFAULT |
| Constructor Summary | |
|---|---|
SimpleLocalTransactionFactoryImpl()
Default constructor using default database connection factory. |
|
| Method Summary | |
|---|---|
void |
begin()
|
void |
commit()
|
protected void |
endTransaction(boolean bCommit)
End active transaction by commit or rollback. |
int |
getStatus()
|
javax.transaction.TransactionManager |
getTransactionManager()
Get transaction manager for this factory. |
javax.transaction.UserTransaction |
requestTransaction()
Get transaction object which we can use to begin/commit/rollback transactions. |
protected java.sql.Connection |
requestTransactionalConnection(boolean bAutoCommit,
java.lang.String strDataSourceName,
java.lang.String strUser,
java.lang.String strPassword,
DatabaseConnectionFactoryImpl connectionFactory)
Check if there is an transaction in progress, if there is and it has already associated connection then the connection associated with the transaction will be returned. |
void |
reset()
This method is here mainly for testing and it should reset the transaction manager to initial status to that tests can start from known environment instead of being influenced by other tests. |
protected void |
returnTransactionalConnection(java.sql.Connection cntDBConnection,
DatabaseConnectionFactoryImpl connectionFactory)
This method should be exclusively used by DatabaseConnectionFactoryImpl to get a transaction aware version of a connection. |
void |
rollback()
|
void |
setRollbackOnly()
|
void |
setTransactionTimeout(int arg0)
|
void |
stop()
Stop the transaction factory. |
| Methods inherited from class org.opensubsystems.core.persist.db.DatabaseTransactionFactoryImpl |
|---|
commitTransaction, getInstance, isTransactionInProgress, isTransactionMonitored, rollbackTransaction, setInstance |
| Methods inherited from class java.lang.Object |
|---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
| Field Detail |
|---|
public static final java.lang.Integer STATUS_ACTIVE_OBJ
public static final java.lang.Integer STATUS_COMMITTED_OBJ
public static final java.lang.Integer STATUS_COMMITTING_OBJ
public static final java.lang.Integer STATUS_MARKED_ROLLBACK_OBJ
public static final java.lang.Integer STATUS_NO_TRANSACTION_OBJ
public static final java.lang.Integer STATUS_PREPARED_OBJ
public static final java.lang.Integer STATUS_PREPARING_OBJ
public static final java.lang.Integer STATUS_ROLLEDBACK_OBJ
public static final java.lang.Integer STATUS_ROLLING_BACK_OBJ
public static final java.lang.Integer STATUS_UNKNOWN_OBJ
| Constructor Detail |
|---|
public SimpleLocalTransactionFactoryImpl()
throws OSSException
OSSException - - an error has occured| Method Detail |
|---|
public javax.transaction.UserTransaction requestTransaction()
requestTransaction in interface TransactionFactory
public void reset()
throws OSSException
reset in interface TransactionFactoryOSSException - - an error has occured during reset
public void stop()
throws OSSException
stop in interface TransactionFactoryOSSException - - problem stoping transaction factory.public javax.transaction.TransactionManager getTransactionManager()
getTransactionManager in interface TransactionFactory
public void begin()
throws javax.transaction.NotSupportedException,
javax.transaction.SystemException
begin in interface javax.transaction.UserTransactionjavax.transaction.NotSupportedException
javax.transaction.SystemException
public void commit()
throws javax.transaction.RollbackException,
javax.transaction.HeuristicMixedException,
javax.transaction.HeuristicRollbackException,
java.lang.SecurityException,
java.lang.IllegalStateException,
javax.transaction.SystemException
commit in interface javax.transaction.UserTransactionjavax.transaction.RollbackException
javax.transaction.HeuristicMixedException
javax.transaction.HeuristicRollbackException
java.lang.SecurityException
java.lang.IllegalStateException
javax.transaction.SystemException
public int getStatus()
throws javax.transaction.SystemException
getStatus in interface javax.transaction.UserTransactionjavax.transaction.SystemException
public void rollback()
throws java.lang.IllegalStateException,
java.lang.SecurityException,
javax.transaction.SystemException
rollback in interface javax.transaction.UserTransactionjava.lang.IllegalStateException
java.lang.SecurityException
javax.transaction.SystemException
public void setRollbackOnly()
throws java.lang.IllegalStateException,
javax.transaction.SystemException
setRollbackOnly in interface javax.transaction.UserTransactionjava.lang.IllegalStateException
javax.transaction.SystemException
public void setTransactionTimeout(int arg0)
throws javax.transaction.SystemException
setTransactionTimeout in interface javax.transaction.UserTransactionjavax.transaction.SystemException
protected java.sql.Connection requestTransactionalConnection(boolean bAutoCommit,
java.lang.String strDataSourceName,
java.lang.String strUser,
java.lang.String strPassword,
DatabaseConnectionFactoryImpl connectionFactory)
throws OSSDatabaseAccessException
requestTransactionalConnection in class DatabaseTransactionFactoryImplbAutoCommit - - The desired autocommit state of the connection. If
this connection is invoked in global (JTA) transaction
then the autocommit is false regardless of what
value is specified here. Use true here if the client
only reads the data and false if the client also
modifies the data.strDataSourceName - - datasource for which the connection was requested,
if null then it is requested for default data sourcestrUser - - user for which the connection was requested, if null
then it is requested for default userstrPassword - - password for which the connection was requested, if
null then it is requested for default passwoedconnectionFactory - - connection factory which is requesting the connection
OSSDatabaseAccessException - - an error has occured
protected void returnTransactionalConnection(java.sql.Connection cntDBConnection,
DatabaseConnectionFactoryImpl connectionFactory)
returnTransactionalConnection in class DatabaseTransactionFactoryImplcntDBConnection - - connection to return, can be nullconnectionFactory - - connection factory to which the connection should
be returned
protected void endTransaction(boolean bCommit)
throws javax.transaction.SystemException
bCommit - - if true then transaction will be commited otherwise
it will be rollbacked.
javax.transaction.SystemException - - and error has occured during commit/rollback
|
|||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | ||||||||