我需要帮助来解释为什么我的JTA事务无法真正回滚,尽管在日志中看到了回滚消息。 我的设置有一个MySQL数据库和一个Microsoft SQLserver数据库。我为两个数据库创建了两个JNDI(tomcat-7)资源,用于事务DataSource(参见我的META-INF / context.xml)。
我打电话给com.my.AService.create(obj)。这是一个引发异常以测试回滚是否有效的方法。在测试结束时,我可以在日志中看到有回滚消息。但是当我检查数据库时,我看到应该已经回滚的已提交记录。 (见我的截断日志)
为什么回滚不起作用?我该如何解决?
我正在使用: Apache Tomcat 7.0.50 jtom Spring框架3.2.3 RELEASE
Trucated Log,我看到Null指针异常的回滚消息:
2014-03-06 11:42:06,103 DEBUG [org.springframework.transaction.interceptor.RuleBasedTransactionAttribute] - <Applying rules to determine whether transaction should rollback on java.lang.NullPointerException>
2014-03-06 11:42:06,103 DEBUG [org.springframework.transaction.interceptor.RuleBasedTransactionAttribute] - <Winning rollback rule is: RollbackRuleAttribute with pattern [Exception]>
...
2014-03-06 11:42:06,104 DEBUG [org.springframework.transaction.jta.JtaTransactionManager] - <Initiating transaction rollback>
2014-03-06 11:42:06,105 DEBUG [org.springframework.transaction.jta.JtaTransactionManager] - <Triggering afterCompletion synchronization>
2014-03-06 11:42:06,105 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - <Clearing transaction synchronization>
片段形成我的项目:
webapp / META-INF / context.xml:
<Context>
<Resource
name="TransactionSynchronizationRegistry"
auth="Container"
type="javax.transaction.TransactionSynchronizationRegistry"
factory="org.objectweb.jotm.TransactionSynchronizationRegistryFactory"/>
<Transaction
factory="org.objectweb.jotm.UserTransactionFactory"
jotm.timeout="60"/>
<!-- -->
<!-- DO I NEED defaultAutoCommit="false" in the following Reources ???? -->
<!-- -->
<Resource driverClassName="com.mysql.jdbc.Driver"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
name="jdbc/mysqldb"
password="secret"
type="javax.sql.DataSource"
url="jdbc:mysql://mysqlserver:3306/mysqldb1"
username="myuser"/>
<ResourceLink global="jdbc/mysqldb" name="jdbc/mysqldb" type="javax.sql.DataSource"/>
<Resource driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
name="jdbc/mssqldb"
password="secret"
type="javax.sql.DataSource"
url="jdbc:sqlserver://sqlserver;portNumber=1433;databaseName=mssqldb1;"
username="myuser" />
<ResourceLink global="jdbc/mssqldb" name="jdbc/mssqldb" type="javax.sql.DataSource"/>
</Context>
在我的WEB-INF / web.xml中:
<resource-env-ref>
<description>forum db connection</description>
<resource-env-ref-name>jdbc/mysqldb</resource-env-ref-name>
<resource-env-ref-type>javax.sql.DataSource</resource-env-ref-type>
</resource-env-ref>
<resource-env-ref>
<description>xdg db connection</description>
<resource-env-ref-name>jdbc/mssqldb</resource-env-ref-name>
<resource-env-ref-type>javax.sql.DataSource</resource-env-ref-type>
</resource-env-ref>
<resource-env-ref>
<description>JTA transaction manager</description>
<resource-env-ref-name>jta/UserTransaction</resource-env-ref-name>
<resource-env-ref-type>javax.transaction.UserTransaction</resource-env-ref-type>
</resource-env-ref>
<resource-env-ref>
<description>JTA transaction synchronization registry</description>
<resource-env-ref-name>TransactionSynchronizationRegistry</resource-env-ref-name>
<resource-env-ref-type>javax.transaction.TransactionSynchronizationRegistry</resource-env-ref-type>
</resource-env-ref>
我将以下内容复制到/tomcat7.0.50/lib:
carol-iiop-delegate.jar
carol-interceptors.jar
carol.jar
carol.properties
commons-logging-api.jar
jotm-core.jar
jotm-datasource.jar
log4j.jar
mysql-connector-java-5.1.25-bin.jar
ow2-connector-1.5-spec.jar
ow2-jta-1.1-spec.jar
sqljdbc4.jar
xapool.jar
我的WEB-INF / config / root-context.xml中的Spring配置:
<jee:jndi-lookup id="mysqlDataSourceTx" jndi-name="java:comp/env/jdbc/mysqldb" expected-type="javax.sql.DataSource"/>
<jee:jndi-lookup id="dataSourceTx" jndi-name="java:comp/env/jdbc/mssqldb" expected-type="javax.sql.DataSource"/>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionSynchronizationRegistryName" value="java:comp/env/TransactionSynchronizationRegistry"/>
<property name="transactionManagerName" value="java:comp/UserTransaction"/>
</bean>
<bean id="adDao" class="com.my.dao.ADDaoJdbcImpl">
<property name="dataSource">
<ref bean="dataSourceTx"/>
</property>
</bean>
<bean id="amDao" class="com.my.dao.AMDaoJdbcImpl">
<property name="dataSource">
<ref bean="dataSourceTx"/>
</property>
</bean>
<bean id="aDao" class="com.my.dao.AJdbcImpl">
<property name="dataSource">
<ref bean="dataSourceTx"/>
</property>
</bean>
<bean id="fService" class="com.my.service.FService">
<property name="dataSource">
<ref bean="mysqlDataSourceTx"/>
</property>
</bean>
<bean id="bDao" class="com.my.dao.BDaoJdbcImpl">
<property name="dataSource">
<ref bean="dataSourceTx"/>
</property>
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="read*" read-only="true"/>
<tx:method name="create*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<tx:advice id="txDaoAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="read*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<tx:advice id="txGeneralAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<!-- use proxy-target-class to force use of CGLIB -->
<aop:config proxy-target-class="true">
<aop:pointcut id="aServiceOperation" expression="execution(* com.my.service.AService.*(..))" />
<aop:pointcut id="daoOperation3" expression="execution(* com.my.dao.AJdbcImpl.*(..))" />
<aop:pointcut id="daoOperation4" expression="execution(* com.my.dao.BDaoJdbcImpl.*(..))" />
<aop:pointcut id="fServiceOperation" expression="execution(* com.my.service.FService.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="aServiceOperation"/>
<aop:advisor advice-ref="txDaoAdvice" pointcut-ref="daoOperation3"/>
<aop:advisor advice-ref="txDaoAdvice" pointcut-ref="daoOperation4"/>
<aop:advisor advice-ref="txGeneralAdvice" pointcut-ref="fServiceOperation"/>
</aop:config>
<!-- NO tx:annotation-driven -->
<bean id="aService" class="com.my.service.AService">
</bean>
com.my.service.AService的部分代码:
package com.my.service;
// omitted import statements
public class AService implements IAService {
@Autowired AJdbcImpl aDao;
@Autowired BDaoJdbcImpl bDao;
@Autowired FService fService;
public AService() {
// empty
}
@Override
public List<ARecord> readAll() {
return aDao.findAll();
}
@Override
public Asset create(ARecord obj) {
String name = bDao.getNameFromId(obj.getBRecordId());
// create my_sql record
int new_mysql_id = fService.createTRecord(name);
obj.setTRecordId(new_mysql_id);
// create MS Sqlserver record
int new_sqlserver_id = aDao.create(obj);
obj.setId(new_sqlserver_id);
// raise an exception to test rollback
String nullString = null;
int BLOW_UP_HERE_WITH_NULL_POINTER_REFERENCING = nullString.length();
return obj;
}
//
// omitted update() , and delete() code
//
}
com.my.service.IAService的部分代码:
package com.my.service;
// omitted import statements
public interface IAService {
List<ARecord> readAll();
Asset create(ARecord obj);
//
// omitted update() , and delete()
}