Spring 弹簧靴&x2B;Atomikos:try-catch内部事务JMS侦听器中引发的异常会立即导致回滚

Spring 弹簧靴&x2B;Atomikos:try-catch内部事务JMS侦听器中引发的异常会立即导致回滚,spring,spring-boot,spring-transactions,spring-jms,atomikos,Spring,Spring Boot,Spring Transactions,Spring Jms,Atomikos,我使用的是springboot1.5.3和Atomikos4.0.6。我有一个DefaultMessageListenerContainer设置来监听JMS队列,并使用配置为使用Atomikos的SpringJTATransactionManager处理事务中的传入消息。消息处理程序调用一个事务性服务,该服务尝试在try catch块内处理消息,如果出现异常,则调用其他事务性方法进行日志记录。其思想是,只有在记录了所有内容并从catch块抛出遇到的RuntimeException之后,才应该回滚

我使用的是
springboot
1.5.3和
Atomikos
4.0.6。我有一个
DefaultMessageListenerContainer
设置来监听JMS队列,并使用配置为使用
Atomikos
的Spring
JTATransactionManager处理事务中的传入消息。消息处理程序调用一个事务性服务,该服务尝试在
try catch块
内处理消息,如果出现异常,则调用其他事务性方法进行日志记录。其思想是,只有在记录了所有内容并从
catch块抛出遇到的RuntimeException之后,才应该回滚事务

@Transactional
public void handleMessage(UnmarshalledMessage message) {
    try {
        Thing thing = repository.find(message.getId());
        ...
    }
    catch (Exception e) {
        // NoResultException translated into EmptyResultDataAccessException
        logger.logUsingTransactions(e.getMessage());
        throw e;
    }
}
但是,如果事务在最初抛出到repository.find()中后立即回滚,会发生什么呢。尝试从catch块内的数据库读取时,会引发异常,因为事务已标记为仅回滚:

c.atomikos.jdbc.AtomikosSQLException - Transaction is marked for rollback only or has timed out
com.atomikos.datasource.xa.session.InvalidSessionHandleStateException: Transaction is marked for rollback only or has timed out
    at com.atomikos.datasource.xa.session.NotInBranchStateHandler.checkEnlistBeforeUse(NotInBranchStateHandler.java:39)
    at com.atomikos.datasource.xa.session.TransactionContext.checkEnlistBeforeUse(TransactionContext.java:70)
    at com.atomikos.datasource.xa.session.SessionHandleState.notifyBeforeUse(SessionHandleState.java:160)
    at com.atomikos.jdbc.AtomikosConnectionProxy.enlist(AtomikosConnectionProxy.java:207)
    at com.atomikos.jdbc.AtomikosConnectionProxy.invoke(AtomikosConnectionProxy.java:122)
    at com.sun.proxy.$Proxy129.prepareStatement(Unknown Source)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.prepareStatement(DatabaseAccessor.java:1565)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.prepareStatement(DatabaseAccessor.java:1514)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseCall.prepareStatement(DatabaseCall.java:778)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:621)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:560)
    at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:2055)
    at org.eclipse.persistence.sessions.server.ServerSession.executeCall(ServerSession.java:570)
    at org.eclipse.persistence.sessions.server.ClientSession.executeCall(ClientSession.java:258)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:242)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.selectOneRow(DatasourceCallQueryMechanism.java:714)
    at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectOneRowFromTable(ExpressionQueryMechanism.java:2803)
    at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectOneRow(ExpressionQueryMechanism.java:2756)
    at org.eclipse.persistence.queries.ReadObjectQuery.executeObjectLevelReadQuery(ReadObjectQuery.java:555)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1175)
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:904)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1134)
    at org.eclipse.persistence.queries.ReadObjectQuery.execute(ReadObjectQuery.java:441)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1222)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1857)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1839)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1790)
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.executeQuery(EntityManagerImpl.java:911)
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.findInternal(EntityManagerImpl.java:854)
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.find(EntityManagerImpl.java:730)
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.find(EntityManagerImpl.java:599)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
我想知道是什么导致了这种行为,以及如何解决这个问题。请注意,在Weblogic中运行时,此安装程序工作正常。有关其他信息,这里是在存储库方法中首次遇到异常时的事务跟踪日志

DEBUG o.s.t.jta.JtaTransactionManager - Initiating transaction commit
DEBUG o.s.t.jta.JtaTransactionManager - Creating new transaction with name [myMessageListenerContainer]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG o.s.t.jta.JtaTransactionManager - Participating in existing transaction
TRACE o.s.t.i.TransactionInterceptor - Getting transaction for [my.transactional.messagehandler.handleMessage]
DEBUG o.s.t.jta.JtaTransactionManager - Participating in existing transaction
TRACE o.s.t.i.TransactionInterceptor - Getting transaction for [my.repository.class.method]
TRACE o.s.t.i.TransactionInterceptor - Completing transaction for [my.repository.class.method] after exception: org.springframework.dao.EmptyResultDataAccessException: ProcessableMessage with id 443e73e7-0905-416b-9e03-4aaa2bbf09fb; nested exception is javax.persistence.NoResultException: ProcessableMessage with id [message-id]
TRACE o.s.t.i.RuleBasedTransactionAttribute - Applying rules to determine whether transaction should rollback on org.springframework.dao.EmptyResultDataAccessException: ProcessableMessage with id 443e73e7-0905-416b-9e03-4aaa2bbf09fb; nested exception is javax.persistence.NoResultException: ProcessableMessage with id [message-id]
TRACE o.s.t.i.RuleBasedTransactionAttribute - Winning rollback rule is: null
TRACE o.s.t.i.RuleBasedTransactionAttribute - No relevant rollback rule found: applying default rules
DEBUG o.s.t.jta.JtaTransactionManager - Participating transaction failed - marking existing transaction as rollback-only
DEBUG o.s.t.jta.JtaTransactionManager - Setting JTA transaction rollback-only
编辑:

我使用的是
JPA
,并且
NoResultException
最初以以下方式抛出:

public static <T> T mandatoryFind(EntityManager em, Class<T> type, Object id) throws NoResultException {
        T found = em.find(type, id);
        if (found == null) {
            throw new NoResultException(type.getSimpleName() +" with id "+ id);
        }
        return found;
}
publicstatictmandatoryfind(EntityManager em,类类型,对象id)抛出NoResultException{
T found=em.find(类型,id);
if(found==null){
抛出新的NoResultException(类型为.getSimpleName()+,id为“+id”);
}
发现退货;
}

然后从带有
@Transactional(norollboor=NoResultException.class)
注释的类调用。异常是在一个相当正常的用例中引发的-我所关心的是为什么在我能够处理异常之前事务被回滚?

听起来您确实在幕后使用了JPA(基于调试日志)。 这是一种典型的行为,然后您可以查询单个结果

NoResultException:

当对查询执行Query.getSingleResult()或TypedQuery.getSingleResult()时,持久性提供程序抛出,并且没有要返回的结果。如果当前事务处于活动状态,此异常不会导致标记为回滚


为了避免这种行为,您需要将查询结果作为一个
列表

,我从问题中省略了这一点,因为我认为它不相关,但我确实在使用JPA,并且NoResultException被显式抛出到一个带有@Transactional注释的类中(noRollbackFor=NoResultException.class)。让我感到不安的是,在我处理异常之前,事务被回滚。它可能是ExceptionTranslation-我一度也这么认为,但禁用异常转换会导致相同的行为。此外,我认为只要处理了异常,不管回滚规则如何,事务都不应该回滚——只要运行时异常没有抛出事务边界之外。