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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19302196/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 15:51:51  来源:igfitidea点击:

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

javaspringhibernatejpatransactions

提问by Vojtěch

I am having issues with committing a transaction within my @Transactional method:

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

methodA() {
    methodB()
}

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

When I call methodB() from methodA(), the method passes successfuly and I can see "OK" in my logs. But then I get

当我从 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. The context of methodB is completely missing in the exception - which is okay I suppose?
  2. Something within the methodB() marked the transaction as rollback only? How can I find it out? Is there for instance a way to check something like getCurrentTransaction().isRollbackOnly()?- like this I could step through the method and find the cause.
  1. 异常中完全缺少 methodB 的上下文 - 我想这可以吗?
  2. methodB() 中的某些内容将事务标记为仅回滚?我怎样才能找到它?例如,有没有一种方法可以检查类似的东西getCurrentTransaction().isRollbackOnly()?- 像这样我可以逐步完成该方法并找到原因。

采纳答案by Vojtěch

I finally understood the problem:

我终于明白了问题所在:

methodA() {
    methodB()
}

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

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

What happens is that even though the methodBhas the right annotation, the methodCdoes not. When the exception is thrown, the second @Transactionalmarks the first transaction as Rollback only anyway.

发生的情况是,即使methodB有正确的注释,methodC也没有。当抛出异常时,第二个@Transactional事务只将第一个事务标记为回滚。

回答by Mareen

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.

查找在...代码部分中抛出和捕获的异常。运行时和回滚应用程序异常会在从业务方法中抛出时导致回滚,即使在其他地方捕获。

You can use context to find out whether the transaction is marked for rollback.

您可以使用上下文来确定事务是否标记为回滚。

@Resource
private SessionContext context;

context.getRollbackOnly();

回答by Ean V

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 @Transactionalannotation to prevent it of rolling back like:

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

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

回答by rémy

disable the transactionmanager in your Bean.xml

禁用 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>

comment out these lines, and you'll see the exception causing the rollback ;)

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

回答by FelixJongleur42

To quickly fetch the causing exception without the need to re-code or rebuild, set a breakpoint on

要快速获取导致异常而无需重新编码或重建,请在上设置断点

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

and go up in the stack, usually to some Interceptor. There you can read the causing exception from some catch block.

并在堆栈中上升,通常到某个拦截器。在那里您可以从某个 catch 块中读取导致异常的信息。

回答by Kumaresan Perumal

I struggled with this exception while running my application.

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

Finally the problem was on the sql query. i mean that the query is wrong.

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

please verify your query. This is my suggestion

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

回答by aquajach

Found a good explanation with solutions: https://vcfvct.wordpress.com/2016/12/15/spring-nested-transactional-rollback-only/

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

1) remove the @Transacional from the nested method if it does not really require transaction control. So even it has exception, it just bubbles up and does not affect transactional stuff.

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

OR:

或者:

2) if nested method does need transaction control, make it as REQUIRE_NEW for the propagation policy that way even if throws exception and marked as rollback only, the caller will not be affected.

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

回答by Asif Raza

apply the below code in productRepository

在 productRepository 中应用以下代码

@Query("update Product set prodName=:name where prodId=:id ") @Transactional @Modifying int updateMyData(@Param("name")String name, @Param("id") Integer id);

@Query("update Product set prodName=:name where prodId=:id ") @Transactional @Modifying int updateMyData(@Param("name")String name, @Param("id") Integer id);

while in junit test apply below code

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

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

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

}

it is working fine for my code

它适用于我的代码