【问题标题】:UnexpectedRollBackException in Spring inner transactionSpring内部事务中的UnexpectedRollBackException
【发布时间】:2017-10-11 08:13:25
【问题描述】:

我有两个班级:

@Service
@Transaction
class A {
    public void method1() {
        private B;

        try {
            save1()
            b.method2()
        } catch (SqlException e) {
            doSomeThing();
        }

       @Autowired
       public setB(){
         this.B = B;
       }
    }
}

@Service
class B {

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void method2(){
        save2()
        throw new SqlException();
    }

}

正如预期的那样,我得到了一个SqlException,但也得到了一个UnexpectedRollBackException,程序停止了。 我想知道为什么save2()持久化的数据没有回滚?

外部事务有问题吗?

更新:我尝试在 A 类中捕获 UnexpectedRollBackException 并且一切正常。但我仍然需要某种解释为什么我会得到例外?我想当内部事务开始时外部事务应该被挂起,那么为什么外部事务的回滚是意外的?

谢谢。

【问题讨论】:

  • 阅读 Ilya Dyoshin 的回答。因为B 不是托管bean,所以只有一个事务在两个方法调用上运行,因此没有拦截器应用于B#method2,其中包括事务拦截器。

标签: java spring transactions


【解决方案1】:

首先:您不是通过 spring 将 B 的实例注入到 A 类中。即你的 b 不是由 spring 管理的 => 这会导致行为:Spring 忽略了方法上的 @Transactional 注释。

其次,如果您不想回滚SqlException,则必须指定noRollbackFor=SqlException.class

更新:经过澄清后,调用发生在托管 bean 上。但是开箱即用的事务管理系统通常不支持预期行为 - 事务内部事务的问题。 https://docs.spring.io/spring/docs/4.3.11.RELEASE/javadoc-api/org/springframework/transaction/annotation/Propagation.html#REQUIRES_NEW

除非提供有关该系统的详细信息,否则无法向前迈进。如果内容不足,则 NESTED 事务传播可能会更好,然后是 REQUIRES_NEW,因为它正在为外部事务创建一个回滚点并开始新的事务。

【讨论】:

  • 抱歉,B 类是用@Service 注释的,所以它是一个托管 bean
  • @brest1007 但你没有注入它——你是在运行时创建它的一个实例,因此没有弹簧的“魔法”适用。即所有注释都被忽略。
  • 好吧,你很好。但实际上我对B使用了setter注入,还是不行。当我写这个问题时,“新”是一个错误
  • 您能否提供有关您的事务管理器的信息。 Spring 团队表示,事务暂停(来自 REQUIRES_NEW 传播)在运行时的支持有限:实际的事务暂停不会在所有事务管理器上开箱即用。这尤其适用于 JtaTransactionManager,它要求 javax.transaction.TransactionManager 对其可用(在标准 Java EE 中是特定于服务器的)。
  • 所以我建议尝试嵌套事务传播。嵌套事务的实际创建仅适用于特定的事务管理器。开箱即用,这仅适用于在 JDBC 3.0 驱动程序上工作时的 JDBC DataSourceTransactionManager。一些 JTA 提供者可能也支持嵌套事务。
【解决方案2】:

我有一个类似的场景,并使用第二个method 调用的propagation = Propagation.NESTED 解决了它。改写下面的代码试试。

@Service
class A {
@Transactional(rollbackFor = { HibernateException.class}, propagation = Propagation.NESTED)
    public void method1() {
        private B;

        try {
            save1()
            b.method2()
        } catch (SqlException e) {
            doSomeThing();
        }

       @Autowired
       public setB(){
         this.B = B;
       }
    }
}

注意:Transactional 用于 class Apropagation nested 的方法级别。 Class B 如下所示。

@Service
class B {

   @Transactional(rollbackFor = {Exception.class}, propagation = Propagation.REQUIRED)
    public void method2(){
        save2()
        throw new SqlException();
    }

}

这解决了我的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-11
    • 1970-01-01
    • 1970-01-01
    • 2016-11-10
    相关资源
    最近更新 更多