事务标记为仅回滚:如何找到原因

问题描述 投票:0回答:10

我在 @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. 异常中完全缺少 methodB 的上下文 - 我想这没关系吗?
  2. methodB() 中的某些内容将事务标记为仅回滚?我怎样才能找到它?例如,有没有一种方法可以检查类似
    getCurrentTransaction().isRollbackOnly()?
    之类的内容 - 像这样我可以逐步完成该方法并找到原因。
java spring hibernate jpa transactions
10个回答
128
投票

当您将方法标记为

@Transactional
时,方法内发生的任何异常都会将周围的 TX 标记为仅回滚(即使您捕获了它们)。您可以使用
@Transactional
注释的其他属性来防止其回滚,例如:

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

86
投票

我终于明白问题所在了:

methodA() {
    methodB()
}

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

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

发生的情况是,即使

methodB
具有正确的注释,但
methodC
却没有。当抛出异常时,第二个
@Transactional
将第一个事务标记为仅回滚。


55
投票

要快速获取导致异常的而无需重新编码或重建,请在

上设置断点
org.hibernate.ejb.TransactionImpl.setRollbackOnly() // Hibernate < 4.3, or
org.hibernate.jpa.internal.TransactionImpl() // as of Hibernate 4.3

并在堆栈中向上,通常是某个拦截器。在那里您可以从某些 catch 块中读取引起的异常。


13
投票

我在运行我的应用程序时遇到了这个异常。

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

请验证您的查询。这是我的建议


10
投票

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

1)如果不需要事务控制,请从嵌套方法中删除@Transacional。所以即使有异常,它也只是冒泡,不会影响交易。

或:

2)如果嵌套方法确实需要事务控制,请将其传播策略设置为REQUIRE_NEW,这样即使抛出异常并标记为仅回滚,调用者也不会受到影响。


8
投票

查找代码的

...
部分中引发和捕获的异常。运行时和回滚应用程序异常在从业务方法中抛出时会导致回滚,即使在其他地方捕获也是如此。

您可以使用上下文来查明事务是否被标记为回滚。

@Resource
private SessionContext context;

context.getRollbackOnly();

6
投票

嵌套方法回滚总是有原因的。如果您没有看到原因,则需要将记录器级别更改为调试,您将在其中看到事务失败的更多详细信息。我通过添加更改了 logback.xml

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

然后我在日志中得到了这一行:

Participating transaction failed - marking existing transaction as rollback-only

所以我只是单步执行我的代码,看看这一行是在哪里生成的,发现有一个 catch 块没有抛出任何东西。

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

1
投票

在 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>

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


0
投票

在productRepository中应用以下代码

@Query("update Product set prodName=:name where prodId=:id ")
     @Transactional
     @Modifying
     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);

}

它对我的代码运行良好


0
投票

之前已经提供了答案,但是@Transactional 接口中的设置发生了变化。

在 jakarta.transaction-api-1.3.3 中,您可以使用 rollbackOndontRollbackOn 属性来指示异常。

© www.soinside.com 2019 - 2024. All rights reserved.