【问题标题】:@Transactional annotation does not rollback RuntimeException even if @EnableTransactionManagement is provided即使提供了@EnableTransactionManagement,@Transactional 注释也不会回滚 RuntimeException
【发布时间】:2020-10-30 14:33:59
【问题描述】:

我有以下应用程序设置:

@SpringBootApplication
@EnableTransactionManagement
public class MyApp extends SpringBootServletInitializer {
    ...
}

使用具有以下内容的类:

public class DoStaff {

    public void doStaffOnAll(List<MyObject> myObjects) {
        for (int i=0; i<myObjects.size(); i++) {
            try {
                doStaffOnSingle(myObjects.get(i), i);
            } catch (Exception e) {
                e.printStrackTrace();
            }
        }
    }

    @Transactional
    public void doStaffOnSingle(MyObject myObject, int i) {
        repository.save(myObject);
        if (i%2==0) {
            throw new RuntimeException();
        }
    }
    
}

因此,如果我使用 MyObjects 列表调用 DoStaff.doStaffOnAll,代码会保存列表中的所有元素,但还会为每个第二个元素引发运行时异常。

由于doStaffOnSingle@Transactional 注释,我希望每隔一个元素都会回滚。 但是如果我运行这段代码,每个元素都会成功保存在数据库中。这是为什么?我做错了什么?

【问题讨论】:

  • @Transactional 不适用于类内部的方法调用,仅当从外部调用@Transactional 方法时才适用。将 doStaffOnAll() 移动到不同的 Spring 组件,它就可以工作了。

标签: java spring-boot exception transactions rollback


【解决方案1】:

引用Spring Documentation:

proxy 模式下(默认),只有通过代理传入的外部方法调用会被拦截。这意味着自调用(实际上,目标对象中的一个方法调用目标对象的另一个方法)不会导致运行时的实际事务,即使被调用的方法标有@Transactional。此外,代理必须完全初始化以提供预期的行为,因此您不应在初始化代码中依赖此功能(即@PostConstruct)。

doStaffOnAll() 移动到不同的 Spring 组件,它会起作用。

或者改成aspectj模式。

我建议移动方法,并设计代码使事务边界清晰明确,即类上的所有公共方法都启动事务,或者类上没有方法启动事务。

应该始终非常清楚您的交易边界在哪里,例如在分层设计中,您通常会将@Service 层也作为事务层,即从更高层到服务层的任何调用都是原子事务。

【讨论】:

    【解决方案2】:

    @Transactional 注释能够发挥神奇作用,因为它有一个代理对象。

    由于您直接调用该方法,因此您不会获得那种魔力。在doStaffOnAll 方法中,您直接调用doStaffOnSingle 方法。因此,不会添加任何事务行为。

    尝试使用自调用来调用方法。

    @Service
    public class DoStaff {
        @Autowired
        private DoStaff doStaff;
    
        public void doStaffOnAll(List<MyObject> myObjects) {
            for (int i=0; i<myObjects.size(); i++) {
               doStaff.doStaffOnSingle(..) // invoke like this
            }
        }
    
        @Transactional
        public void doStaffOnSingle(MyObject myObject, int i) {
           
        }
    }
    

    由于 doStaffOnSingle 有 @Transactional 注释,我会 预计每隔一个元素就会回滚一次。

    默认事务模式将提交所有内容或不提交任何内容。我想你会想要使用 REQUIRES_NEW 传播。

    在此处查看支持的传播类型。

    https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/Propagation.html#REQUIRED

    【讨论】:

      猜你喜欢
      • 2019-12-12
      • 2015-03-07
      • 2012-08-25
      • 2016-05-03
      • 1970-01-01
      • 2016-01-04
      • 2015-10-07
      • 1970-01-01
      • 2019-06-15
      相关资源
      最近更新 更多