必看!导致事务失效的7大典型场景!

必看!导致事务失效的7大典型场景!
最新回答
コ夏日纠结成风

2020-10-25 05:44:21

导致@Transactional声明式事务失效的7大典型场景及原因、解决方案如下

  • 方法访问修饰符非public

    原因:Spring AOP代理生成事务代理时,仅对public方法生效。源码中computeTransactionAttribute方法会检查方法修饰符,非public方法直接返回null,导致事务不生效。

    示例

    @Servicepublic class OrderService { @Transactional private void createOrder() { // 事务失效 // ... }}

    解决方案:将方法修饰符改为public。

  • 自调用问题

    原因:类内部非事务方法直接调用事务方法(通过this),绕过Spring代理对象,事务无法生效。

    示例

    @Servicepublic class OrderService { public void placeOrder() { createOrder(); // 直接调用,事务失效 } @Transactional public void createOrder() { // ... }}

    解决方案

    将事务方法移至其他Service类。

    通过ApplicationContext获取代理对象调用:

    @Autowiredprivate ApplicationContext context;public void placeOrder() { OrderService proxy = context.getBean(OrderService.class); proxy.createOrder(); // 通过代理调用}
  • 异常被捕获且未重新抛出

    原因:Spring默认仅对未捕获的RuntimeException或Error回滚事务。若异常被捕获且未抛出,代理认为方法执行成功,提交事务。

    示例

    @Transactionalpublic void transferMoney() { try { addMoney(); } catch (Exception e) { log.error("异常", e); // 捕获未抛出,事务不回滚 }}

    解决方案

    手动设置回滚:

    catch (Exception e) { TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}

    重新抛出异常:

    catch (Exception e) { throw e; // 或仅setRollbackOnly}
  • 事务传播机制配置错误

    原因:错误使用传播行为(如Propagation.NOT_SUPPORTED挂起事务、Propagation.NEVER拒绝事务)会导致事务失效。Propagation.REQUIRES_NEW会开启新事务,需谨慎避免嵌套事务问题。

    示例

    @Transactional(propagation = Propagation.NOT_SUPPORTED) // 事务挂起public void process() { // ...}

    解决方案:根据业务需求选择正确的传播行为(如默认Propagation.REQUIRED)。

  • 数据库引擎不支持事务

    原因:底层数据库引擎未开启事务支持(如MySQL使用MyISAM引擎)。即使代码配置@Transactional,也无法实现事务。

    示例:MySQL的MyISAM引擎不支持事务,需切换为InnoDB。

    解决方案:确认数据库引擎支持事务(如InnoDB、PostgreSQL等)。

  • 方法被final或static修饰

    原因:CGLIB无法代理final方法(不可重写),JDK动态代理无法处理static方法,导致事务失效。

    示例

    @Servicepublic class OrderService { @Transactional public final void createOrder() { // final方法,事务失效 // ... }}

    解决方案:移除final或static修饰符。

  • 多线程中调用事务方法

    原因:事务基于线程绑定(通过ThreadLocal存储上下文),子线程无法继承父线程的事务上下文。

    示例

    @Transactionalpublic void process() { new Thread(() -> { dao.update(); // 子线程无事务 }).start();}

    解决方案

    使用事务同步机制(如Spring的TransactionSynchronizationManager)。

    手动管理事务(如编程式事务TransactionTemplate)。

总结:@Transactional失效的核心原因多与Spring AOP代理机制相关(如方法修饰符、自调用、代理限制),或与事务基础支持(如数据库引擎、线程绑定)有关。掌握这些场景可避免常见陷阱,提升代码健壮性。