spring - Spring Boot + Atomikos:try-catch 内的事务 JMS 侦听器中引发的异常立即导致回滚
问题描述
我正在使用Spring Boot
1.5.3 和Atomikos
4.0.6。我有一个DefaultMessageListenerContainer
设置来监听 JMS 队列并使用JTATransactionManager
配置为使用的 Spring 处理事务中的传入消息Atomikos
。消息处理程序调用一个事务服务,该服务尝试在 a 中处理消息,try-catch-block
并且在出现异常时调用其他事务方法来进行日志记录。这个想法是,只有在所有内容都被记录并且遇到的 RuntimeException 从catch-block
.
@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-block 内的数据库中读取数据时,会抛出异常,因为该事务已被标记为仅回滚:
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;
}
这又是从一个用 注释的类中调用的@Transactional(noRollbackFor = NoResultException.class)
。在一个相当正常的用例中引发了异常 - 我感到困扰的是为什么事务在我可以处理异常之前回滚?
解决方案
听起来您确实在后台使用了 JPA(基于您的调试日志)。这是一种典型的行为,然后您会查询单个结果。
无结果异常:
当对查询执行 Query.getSingleResult() 或 TypedQuery.getSingleResult() 并且没有返回结果时,由持久性提供程序抛出。此异常不会导致当前事务(如果处于活动状态)被标记为回滚。
为了避免这种行为,您将查询结果作为List
.
推荐阅读
- java - 如何在 while 循环内返回用户输入值?
- flutter - 颤振检查字符串是否以 RegExp 开头
- mysql - 如何选择满足条件的X个连续行数
- android - java.lang.IllegalArgumentException:ByteBuffer 不是有效的平面缓冲区模型
- node.js - 发布请求后快递nodejs正文为空
- r - 将标签添加到注释矩形ggplot
- javascript - 我的 google 脚本执行速度很慢,但它已经从 107 行压缩到了 1 行。我将如何使它更具响应性?
- javascript - NodeJS setTimeout 循环
- android - Kotlin:如何从 Button 类中获取背景颜色?
- pandas - 如何确保 Pandas 列中字符串的 HH:MM:SS 时间格式