Spring transactions - 新事务中的异常导致父事务中的回滚

Posted

技术标签:

【中文标题】Spring transactions - 新事务中的异常导致父事务中的回滚【英文标题】:Spring transactions - Exception in new transaction cause rollback in parent one 【发布时间】:2020-03-10 23:02:04 【问题描述】:

我正在为使用 Propagation.REQUIRES_NEW 注释的方法的奇怪行为而苦苦挣扎。

在 placeOrder() 方法的主体中,最后一个操作是发送可能引发运行时异常的短信。

我不介意发送短信会引发异常,所以我不希望在 placeOrder() 中启动事务回滚。

方法 sendSms() 具有传播 REQUIRES_NEW 所以据我了解,它不应该触发暂停事务的回滚,只有

@Component
public class OrderService 

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private ProductDao productDao;

    @Autowired
    private WarehouseService warehouseService;

    @Autowired
    private SmsClient smsClient;

    @Autowired
    private UserDao userDao;

    @Transactional
    @Override
    public void placeOrder(PlaceOrderRequest placeOrderRequest) 
        Product product = productDao.getById(placeOrderRequest.getProductId());
        WarehouseItem warehouseItem = warehouseService.getAvailable(product);
        Order order = orderDao.createNew();
        order.setProduct(product);
        order.setUser(user);
        smsClient.sendSms();
    


@Component
public class SmsClient 

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void sendSms() 
        throwEx();
    

    private void throwEx() throws SmsClientException 
        throw new SmsClientException();
    

以下是来自 TransactionManager 的日志:

Creating new transaction with name [ai.optime.springmicroservicetemplate.domain.order.impl.DefaultOrderService.placeOrder]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Opened new EntityManager [SessionImpl(1447696365<open>)] for JPA transaction
Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@25711903]
Executing DAO method 'getById' from class 'JpaBasedProductDao' with args 1
Found thread-bound EntityManager [SessionImpl(1447696365<open>)] for JPA transaction
Participating in existing transaction
Executing DAO method 'getAvailable' from class 'JpaBasedWarehouseItemDao' with args 1
HHH000397: Using ASTQueryTranslatorFactory
Found thread-bound EntityManager [SessionImpl(1447696365<open>)] for JPA transaction
Participating in existing transaction
Executing DAO method 'createNew' from class 'JpaBasedOrderDao' with args 
Found thread-bound EntityManager [SessionImpl(1447696365<open>)] for JPA transaction
Participating in existing transaction
Executing DAO method 'getById' from class 'JpaBasedUserDao' with args 1
Found thread-bound EntityManager [SessionImpl(1447696365<open>)] for JPA transaction
Participating in existing transaction
Found thread-bound EntityManager [SessionImpl(1447696365<open>)] for JPA transaction
Suspending current transaction, creating new transaction with name [ai.optime.springmicroservicetemplate.api.sms.SmsClient.sendSms]
Opened new EntityManager [SessionImpl(1649890926<open>)] for JPA transaction
Exposing JPA transaction as JDBC org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@6f98ca1d]
Initiating transaction rollback
Rolling back JPA transaction on EntityManager [SessionImpl(1649890926<open>)]
Closing JPA EntityManager [SessionImpl(1649890926<open>)] after transaction
Resuming suspended transaction after completion of inner transaction
Initiating transaction rollback
Rolling back JPA transaction on EntityManager [SessionImpl(1447696365<open>)]
Closing JPA EntityManager [SessionImpl(1447696365<open>)] after transaction

【问题讨论】:

【参考方案1】:

你的理解是错误的。你没有捕捉到smsClient.sendSms() 抛出的异常,所以它是从placeOrder() 抛出的,所以为此方法启动的事务被回滚。

此外,将sendSms() 设为事务性并没有多大意义,因为发送 SMS 很可能不会与任何事务性资源交互。

【讨论】:

我故意不捕获SmsClientException(),因为它应该被异常处理程序捕获。我不想在执行sendSms() 之前回滚placeOrder() 所做的更改,所以我用@Transactional(propagation = Propagation.REQUIRES_NEW) 注释sendSms(),据我了解,应该创建一个新事务,并且此方法中抛出的所有异常不应影响事务始于placeOrder() 也许我补充一点,我受到这篇文章的启发 - ***.com/a/16166576/10848494 不同之处在于,在您引用的帖子中,异常被捕获并处理。在您的代码中,它通过调用堆栈被抛出,它的类型为RuntimeException,而不是检查异常,然后它会在第一个事务中自动触发回滚。 正如艾伦所说。同样,如果事务方法抛出运行时异常(就像在您的示例中那样,因为您没有捕获它),那么事务将被回滚。 好的,现在我明白了。感谢您的帮助,非常感谢。

以上是关于Spring transactions - 新事务中的异常导致父事务中的回滚的主要内容,如果未能解决你的问题,请参考以下文章

Spring Transaction - 记录新事务的方法执行时间

[mybatis-spring] Transaction 事务/事务处理/事务管理器

Spring,为内部方法新起一个事务,此处应有坑。

Spring 事务回滚

聊一聊Spring中的 Transaction基本实现

Spring事务(transactional) - REQUIRES_NEW在JdbcTemplateMybatis中的不同表现