Spring 事务传播机制,失效场景等小结

Spring事务传播机制
Spring 七种传播机制
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED)支持当前事务,如果有事务加入当前事务,没有则会创建一个新的事务
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS)支持当前事务,如果有事务加入当前事务,如果没有事务的话以非事务方式执行
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY)支持当前事务,当前如果没有事务抛出异常
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW)不管是否有事务创建一个新的事务并挂起当前事务
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED)以非事务方式执行,如果当前存在事务则将当前事务挂起
NEVER(TransactionDefinition.PROPAGATION_NEVER)以非事务方式进行,如果存在事务则抛出异常
NESTED(TransactionDefinition.PROPAGATION_NESTED)如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

注意:事务的传播在类内部方法中调用时是失效的

以PROPAGATION_REQUIRED为例子讲一下传播机制中的当前事务和新建事务的概念。

假设有以下几个类

ContollerA
ServiceA,save方法被@Transactional(propagation = Propagation.REQUIRED)修饰
ServiceB,delete方法被@Transactional(propagation = Propagation.REQUIRED)修饰
方法调用链为ControllerA -> ServiceA#save() -> ServiceB#delete()

那么时序图如下图1-1所示。
在这里插入图片描述

图1-1
解析如下

对于ServiceA的save方法来说,由于Controller调用的时候没有开启任何事务,因此,Spring内部识别到REQUIRED传播机制则会主动开启一个事务。

对于ServiceB的delete方法来说,由于save方法已经开启了一个事务,那么对于delete方法来说当前已经存在了一个事务,那么Spring就不会再为delete方法开启一个事务,而是直接把delete方法加入save方法前开启的事务中。

最后save方法和delete方法执行完之后,Spring则会内部把事务提交上去。

Spring事务失效场景
  1. 数据库引擎不支持事务,比如MyISAM

  2. 入口方法不是public,这一点由Spring的AOP特性决定的,理论上而言,不public也能切入,但spring可能是觉得private自己用的方法,应该自己控制,不应该用事务切进去吧)。另外private 方法, final 方法 和 static 方法不能添加事务,加了也不生效

  3. Spring事务管理默认只支持运行期异常进行回滚(至于为什么spring要这么设计:因为spring认为Checked的异常属于业务的,coder需要给出解决方案而不应该直接扔该框架)

  4. 没有启用事务和切面

  5. 类是否被正确代理

  6. 业务和事务要在同一个线程,否则事务也不生效,比如

    @Transactional
    @Override
    public void save(User user1, User user2) {
        new Thread(() -> {
              saveError(user1, user2);
              System.out.println(1 / 0);
        }).start();
    }
    
  7. 默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。

Spring 事务四大特性
  • 原子性 : 操作要么全部成功,要么全部失败回滚
  • 一致性 : 事务执行前和执行后处于一致性状态。例如,转账前A、B共5000元,A、B之间转账后,两者之和仍应该是5000元。
  • 隔离性 : 事务之间互不干扰
  • 持久性 : 事务一旦提交,数据的改变是永久性的,即使这时候数据库发生故障,数据也不会丢失。
事务隔离级别
  • 未提交读(READ UNCOMMITED)

    在未提交读级别下,事务中的修改,即使没有提交,对其他事务也是可见的,事务可以读取未提交的数据,这也被称为脏读

  • 提交读(READ COMMIT)

    一个事务开始时,只能看见已经提交的事务所做的修改,换句话说,一个事务直到提交之前,所做的任何修改对其他事务都是不可见的,这个级别有时候也被称为不可重复读,因为两次执行同样的查询可能会得到不同的结果

  • 可重复读 (REPEATABLE READ)

    可重复读解决了脏读的问题,该级别保证了统一个事务中多次读取同样记录的结果是一致的。但是理论上,可重复读隔离级别无法解决幻读问题,所谓幻读,指的是当某个事务在读取某个范围内的记录时,会产生幻行,很多DB通过MVCC机制解决这个问题

  • 序列化 (SERIALIZABLE)

    序列化级别时最高的隔离级别,它通过强制事务串行执行,避免了前面所有的所有问题

产生的问题
  • 脏读,在未提交读隔离级别出现
  • 不可重复读,在提交读隔离级别出现
  • 幻读,可重复读隔离级别通过MVCC解决
Spring 手动开启事务

使用场景,外部类需要调用父类的方法,然后父类会调用子类实现的抽象方法,父类不需要事务,但是子类实现的方法需要事务,因为外部类是调用父类的方法,在子类添加@Transactional注解无法生效,例如

 @Scheduled(fixedRate = 1000 * 60 * 3)
  public void importPaidOrders() {
    // 外部调用  
    paidOrdersImporter.importOrders(tmb,now, TypeTkStatus.RELATION_PAID);
  }
 
  // 父类方法
  public void importOrders(String startTime,String endTime, TypeTkStatus tkStatus){
    doDealWithOrder(...);
  }
  
  // 抽象方法
 abstract void doDealWithOrder(
      Orders orders,
      Orders previousOrder,
      Long parentUserId,
      Long parentPUserId,
      User user,
      User oneLevelUser
  );
  
 // 子类实现
 public void doDealWithOrder(
      Orders orders,
      Orders previousOrder,
      Long parentUserId,
      Long parentPUserId,
      User user,
      User oneLevelUser
  ){
  	...
  }
  

这个时候可以手动开启事务,如何手动开启?

  // 注入DataSourceTransactionManager
  @Autowired
  DataSourceTransactionManager dataSourceTransactionManager;
  // 注入TransactionDefinition
  @Autowired
  TransactionDefinition transactionDefinition;

  @Override
  @Transactional
  void doDealWithOrder(Orders orders, Orders previousOrder, Long parentUserId, Long parentPUserId,
      User user, User oneLevelUser) {
    // 开启事务  
    TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    try {
      // business logic
      ...
      // 提交事务    
      dataSourceTransactionManager.commit(transactionStatus);
    }catch (Exception ex){
      logger.error("paid orders exception is : {}",ex);
      // 回滚事务  
      dataSourceTransactionManager.rollback(transactionStatus);
    }
  }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值