First, let me apologize for the length of this post, but I thought that giving detailed information was the only way anybody might be able to help me. This actually is a very simplified test case.
I'm having trouble getting proper transaction behavior using the Spring JTA transaction manager in conjunction with JOTM standalone, using XAPool's datasource, and oracle's jdbc driver on a 10g database.
As far as I understand from reading J2EE w/o EJB and the reference guide and forums, I should be able to use JOTM standalone with the JtaTransactionManager, and when using an XADataSource (XAPool) that is wired to the JtaTransactionManager, the JdbcTemplate should participate in jta-managed transactions automatically.
My logs seem fine to me, and there is every indication that synchronization is occurring correctly and the the transaction gets rolled back before the connection is closed, but the deleted rows that should have been rolled back do not get rolled back.
Here are the relevant bean definitions I'm using:
The test class that throws the exception contains the following method:Code:<bean id="appXaDataSource" class="org.enhydra.jdbc.oracle.OracleXADataSource" singleton="false" destroy-method="shutdown"> <property name="transactionManager"> <ref local="jotm"/> </property> </bean> <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/> <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="userTransaction"> <ref local="jotm"/> </property> <property name="transactionManager"> <ref local="jotm"/> </property> </bean> <bean id="txTestProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="target"><bean class="com.thatone.archie.transactions.TransactionTestor"/></property> <property name="transactionManager"><ref bean="transactionManager"/></property> <property name="transactionAttributes"> <props> <prop key="delete*">PROPAGATION_REQUIRED,-RuntimeException</prop> </props> </property> <property name="proxyTargetClass"><value>true</value></property> </bean>
The JdbcTemplate that I pass in is created using the datasource constructor, and the datasource object that is passed in is the bean above where id="appXaDataSource". I get it from the context, set its properties, then create the JdbcTemplate, which is then used for the test above.Code:public void deleteWithFailure(JdbcTemplate template, String table, String idColumnName, Object[] ids) { final String query = QueryUtils.createDeleteQuery(table, new Object[] { ids[0] }, idColumnName); // delete only the first id, then throw an exception int rowsAffected = template.update(query); System.out.println("\t\tDeleted " + rowsAffected + " rows."); assert rowsAffected == 1 : "1 row should have been deleted, not " + rowsAffected; System.out.println("\t\tThrowing exception to check rollback."); throw new RuntimeException("Throwing exception to check rollback."); }
The unittest is very simple. It just gets the transaction proxy ("txTestProxy") from the context, counts the rows in a table in 1 transaction, calls deleteWithFailure in a 2nd transaction (passing in the JdbcTemplate and other parameters), and then counts the rows in a 3rd transaction. The result is that 1 row is deleted, even though the logs show that a rollback occurred on the transaction that deleted the row.
At the risk of giving too much information, here are the log messages I see from when the transaction proxy gets the transaction object until the completion of the transaction in question:
(First line in each is the message, second line is class and method that logged the message)
I'm hoping somebody who has worked with JOTM/JTA and Spring (best of all would be Oracle too) could sanity check my setup above and see if anything obvious is wrong.Code:Getting transaction for method 'deleteWithFailure' in class [com.thatone.archie.transactions.TransactionTestor] org.springframework.transaction.interceptor.TransactionAspectSupport#getTransaction Current.getStatus() org.objectweb.jotm.Current#getStatus Creating new transaction org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction Beginning JTA transaction org.springframework.transaction.jta.JtaTransactionManager#doBegin begin transaction org.objectweb.jotm.Current#begin serverName=, ipAddr=0 org.objectweb.jotm.XidImpl#<init> new Xid (uuid= 403646f851002) org.objectweb.jotm.XidImpl#<init> xid=bb14111130343033363436663835313030325f305f...0, timeout=60 org.objectweb.jotm.TransactionImpl#<init> myXid=bb14111130343033363436663835313030325f305f...0 org.objectweb.jotm.TransactionImpl#getXid tx=bb14111130343033363436663835313030325f305f...0 org.objectweb.jotm.Current#begin flag=TMJOIN org.objectweb.jotm.TransactionImpl#doAttach number of enlisted=0 org.objectweb.jotm.TransactionImpl#doAttach Associate tx to xid (xid=bb14111130343033363436663835313030325f305f...0) org.objectweb.jotm.Current#putTxXid myXid=bb14111130343033363436663835313030325f305f...0 org.objectweb.jotm.TransactionImpl#getXid set timer for tx (timer=org.objectweb.jotm.TimerEvent@8d5485, tx=bb14111130343033363436663835313030325f305f...0) org.objectweb.jotm.TransactionImpl#setTimer Initializing transaction synchronization org.springframework.transaction.support.TransactionSynchronizationManager#initSynchronization Executing SQL update [DELETE FROM order_item_details WHERE id IN ('00000000000000000000000000000047')] org.springframework.jdbc.core.JdbcTemplate#update Opening JDBC connection org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection StandardDataSource:getConnection Connection from DriverManager is returned org.enhydra.jdbc.util.Logger#debug Registering transaction synchronization for JDBC connection org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection Bound value [org.springframework.jdbc.datasource.ConnectionHolder@193a6a5] for key [[driver=oracle.jdbc.OracleDriver@126f816, url=jdbc:oracle:thin:@//localhost:1521/archie, user=testDb1]] to thread [main] org.springframework.transaction.support.TransactionSynchronizationManager#bindResource Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@193a6a5] for key [[driver=oracle.jdbc.OracleDriver@126f816, url=jdbc:oracle:thin:@//localhost:1521/archie, user=testDb1]] bound to thread [main] org.springframework.transaction.support.TransactionSynchronizationManager#getResource SQL update affected 1 rows org.springframework.jdbc.core.JdbcTemplate$1UpdateStatementCallback#doInStatement Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@193a6a5] for key [[driver=oracle.jdbc.OracleDriver@126f816, url=jdbc:oracle:thin:@//localhost:1521/archie, user=testDb1]] bound to thread [main] org.springframework.transaction.support.TransactionSynchronizationManager#getResource Applying rules to determine whether transaction should rollback on java.lang.RuntimeException: Throwing exception to check rollback. org.springframework.transaction.interceptor.RuleBasedTransactionAttribute#rollbackOn Winning rollback rule is: RollbackRule with pattern [RuntimeException] org.springframework.transaction.interceptor.RuleBasedTransactionAttribute#rollbackOn Invoking rollback for transaction on method 'deleteWithFailure' in class [com.thatone.archie.transactions.TransactionTestor] due to throwable [java.lang.RuntimeException: Throwing exception to check rollback.] org.springframework.transaction.interceptor.TransactionAspectSupport#doCloseTransactionAfterThrowing Triggering beforeCompletion synchronization org.springframework.transaction.support.AbstractPlatformTransactionManager#triggerBeforeCompletion Removed value [org.springframework.jdbc.datasource.ConnectionHolder@193a6a5] for key [[driver=oracle.jdbc.OracleDriver@126f816, url=jdbc:oracle:thin:@//localhost:1521/archie, user=testDb1]] from thread [main] org.springframework.transaction.support.TransactionSynchronizationManager#unbindResource Closing JDBC connection org.springframework.jdbc.datasource.DataSourceUtils#doCloseConnectionIfNecessary Initiating transaction rollback org.springframework.transaction.support.AbstractPlatformTransactionManager#rollback Rolling back JTA transaction org.springframework.transaction.jta.JtaTransactionManager#doRollback Current.rollback() org.objectweb.jotm.Current#rollback TransactionImpl.rollback() org.objectweb.jotm.TransactionImpl#rollback myXid=bb14111130343033363436663835313030325f305f...0 org.objectweb.jotm.TransactionImpl#getXid unset timer for tx (timer=org.objectweb.jotm.TimerEvent@8d5485, tx=bb14111130343033363436663835313030325f305f...0) org.objectweb.jotm.TransactionImpl#unsetTimer Triggering afterCompletion synchronization org.springframework.transaction.support.AbstractPlatformTransactionManager#triggerAfterCompletion Clearing transaction synchronization org.springframework.transaction.support.TransactionSynchronizationManager#clearSynchronization
Thanks very much for any pointers.
-calvin


Reply With Quote
