ChatGPT解决这个技术问题 Extra ChatGPT

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

我在 @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()...

异常中完全缺少methodB的上下文-我想这可以吗? methodB() 中的某些内容仅将事务标记为回滚?我怎样才能找到它?例如,有没有办法检查 getCurrentTransaction().isRollbackOnly() 之类的东西? - 像这样我可以逐步完成该方法并找到原因。

值得注意的是,如果您的数据库表不存在,有时也会显示此错误。

E
Ean V

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

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

好吧,我尝试使用 noRollbackFor=Exception.class,但它似乎没有效果——它是否适用于继承的异常?
是的,它确实。查看您自己的答案,这是正确的(您没有在第一篇文章中提供 methodC)。 methodBmethodC 都使用相同的 TX,并且始终使用最具体的 @Transactional 注释,因此当 methodC 抛出异常时,周围的 TX 将被标记为仅回滚。您还可以使用不同的传播标记来防止这种情况。
@lolotron @Ean 我可以确认它确实适用于只读事务。我的方法在只读事务上引发了 EmptyResultDataAccessException 异常,我得到了同样的错误。将我的注释更改为 @Transactional(readOnly = true, noRollbackFor = EmptyResultDataAccessException.class) 解决了这个问题。
这个答案是错误的。 Spring 只知道通过 @Transactional 代理包装器的异常,即 uncaught。有关完整故事,请参阅 Vojtěch 的其他答案。可能有嵌套的 @Transactional 方法可以将您的事务标记为仅回滚。
noRollbackFor 仅在 globalRollbackOnParticipationFailure=false 时有效
V
Vojtěch

我终于明白了这个问题:

methodA() {
    methodB()
}

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

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

发生的情况是,即使 methodB 具有正确的注释,methodC 也没有。当抛出异常时,第二个 @Transactional 无论如何都将第一个事务标记为 Rollback。


事务的状态存储在线程局部变量中。当 spring 拦截 methodC 并将标志设置为 rollback 时,您的事务已经标记为回滚。任何进一步抑制异常都无济于事,因为当最终提交发生时,您将收到错误消息
@Vojtěch 假设如果 methodCpropagation=requires_new 那么 methodB 不会回滚?
methodC 必须在不同的 Spring bean/服务中,或者通过 Spring 代理以某种方式访问。否则 Spring 将不可能知道您的异常。只有通过 @Transactional 注释的异常才能将事务标记为仅回滚。
那不是解决方案。这只是对Spring Transaction AOP机制的误解和错误使用。仅当您确定在该位置应用的操作需要单独的事务上下文或传播时,才应使用事务注释。在任何其他情况下,您都可以正确设置事务传播。 -1
标记第一笔交易。没有第一笔或第二笔交易——那里只有一个。因为默认情况下 @Transactional 的传播是 REQUIRED (被视为“在现有事务中执行,如果有的话”)
F
FelixJongleur42

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

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

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


在 Hibernate 4.3.11 中,它是 org.hibernate.jpa.internal.TransactionImpl
非常好我的朋友!
谢谢!在较新版本的 Hibernate (5.4.17) 中,类是 org.hibernate.engine.transaction.internal.TransactionImpl,方法是 setRollbackOnly
org.hibernate.jpa.internal.TransactionImpl.setRollbackOnly Hibernate 5.0.12 的方法
K
Kumaresan Perumal

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

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

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


澄清一下:如果您 1. sql 语法有错误 2. 设置回滚异常 3. 有 readOnly 事务,您将收到此错误,因为 sql 语法会导致触发回滚的异常,因为您在“只读”模式。
a
aquajach

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

1) 如果它不需要事务控制,则从嵌套方法中删除 @Transacional。因此,即使它有异常,它也会冒泡并且不会影响事务性内容。

或者:

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


谢谢!在嵌套方法中要求新事务是我的问题的解决方案
M
Mareen

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

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

@Resource
private SessionContext context;

context.getRollbackOnly();

在我看来,我找到了原因,但我不明白为什么会这样。内部方法会引发异常,我会捕获、记录并忽略该异常。但无论如何,事务仅被标记为回滚。我该如何预防?我不希望事务受到我正确捕获的异常的影响。
SessionContext 是 Spring 中的标准类吗?在我看来,它更像是 EJB3,它不包含在我的 Spring 应用程序中。
糟糕的是,我错过了关于春天的事实。无论如何,应该有类似 TransactionAspectSupport.currentTransactionStatus().isRollbackOnly() 的东西可用。
J
Janet

嵌套方法回滚总是有原因的。如果您没有看到原因,您需要将您的记录器级别更改为调试,您将在其中看到事务失败的更多详细信息。我通过添加更改了我的 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;
    }

谢谢!这是“我如何找到原因?”这个问题的唯一答案。
r
rémy

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

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


A
Asif Raza

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

}

它适用于我的代码


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅