Transaction marked as rollback only: How do I find the cause

我在@Transactional 方法中提交事务时遇到了问题:

methodA() {
methodB()
}


@Transactional
methodB() {
...
em.persist();
...
em.flush();
log("OK");
}

当我从 methodA ()调用 methodB ()时,方法成功传递,我可以在日志中看到“ OK”。然后我得到了

Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at methodA()...
  1. 异常中完全没有 method B 的上下文——我想这没什么问题吧?
  2. MethodB ()中的某些内容将事务标记为仅回滚?我怎么才能知道?例如,是否有一种方法来检查像 getCurrentTransaction().isRollbackOnly()?这样的东西-像这样,我可以逐步通过该方法并找到原因。
301135 次浏览

Look for exceptions being thrown and caught in the ... sections of your code. Runtime and rollbacking application exceptions cause rollback when thrown out of a business method even if caught on some other place.

您可以使用上下文查找是否将事务标记为回滚。

@Resource
private SessionContext context;


context.getRollbackOnly();

When you mark your method as @Transactional, occurrence of any exception inside your method will mark the surrounding TX as roll-back only (even if you catch them). You can use other attributes of @Transactional annotation to prevent it of rolling back like:

@Transactional(rollbackFor=MyException.class, noRollbackFor=MyException2.class)

我终于明白了问题所在:

methodA() {
methodB()
}


@Transactional(noRollbackFor = Exception.class)
methodB() {
...
try {
methodC()
} catch (...) {...}
log("OK");
}


@Transactional
methodC() {
throw new ...();
}

发生的情况是,尽管 methodB有正确的注释,但是 methodC没有。当引发异常时,第二个 @Transactional将第一个事务标记为 Rollback。

禁用 Bean.xml 中的事务管理器

<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>

注释掉这些行,您将看到导致回滚的异常;)

若要快速获取引发异常的 而不需要重新编码或重建,请将断点设置为

org.hibernate.ejb.TransactionImpl.setRollbackOnly() // Hibernate < 4.3, or
org.hibernate.jpa.internal.TransactionImpl() // as of Hibernate 4.3

然后进入堆栈,通常是拦截机。在那里,您可以从一些 catch 块中读取导致异常的内容。

I struggled with this exception while running my application.

最后 问题出现在 sql 查询 上。我的意思是查询是错误的。

请证实你的问题。这是我的建议

找到了一个很好的解释和解决方案: https://vcfvct.wordpress.com/2016/12/15/spring-nested-transactional-rollback-only/

1)从嵌套方法中删除@Transacional,如果它不需要事务控制的话。所以即使它有例外,它也只是冒泡,并不影响事务性的东西。

或者:

2)如果嵌套方法确实需要事务控制,那么对于传播策略,将其设置为 REQUIRE _ NEW,这样即使抛出异常并标记为只回滚,也不会影响调用方。

在 productRepository 中应用下面的代码

@ Query (“ update Product set prodName = : name where prodId = : id”) @ Transactional @ 修改 Int updateMyData (@Param (“ name”) String name,@Param (“ id”) Integer id) ;

而在 junit 测试中应用于下面的代码

@Test
public void updateData()
{
int i=productRepository.updateMyData("Iphone",102);


System.out.println("successfully updated ... ");
assertTrue(i!=0);


}

在我的代码中运行良好

嵌套方法回滚总是有原因的。如果您没有看到原因,您需要更改您的日志记录器级别以进行调试,在这里您将看到事务失败的更多详细信息。我通过添加

<logger name="org.springframework.transaction" level="debug"/>
<logger name="org.springframework.orm.jpa" level="debug"/>

然后我在日志里写了这么一行:

Participating transaction failed - marking existing transaction as rollback-only

So I just stepped through my code to see where this line is generated and found that there is a catch block which did not throw anything.

private Student add(Student s) {
try {
Student retval = studentRepository.save(s);
return retval;
} catch (Exception e) {
            

}
return null;
}