Spring JTA Transaction无法回滚

时间:2014-03-06 21:54:55

标签: java spring jdbc transactions jta

我需要帮助来解释为什么我的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()
}

0 个答案:

没有答案