[Spring] Spring 探秘 之 事务配置(二)

[Spring] Spring 探秘 之 事务配置(二)

手机用户请横屏获取最佳阅读体验,REFERENCES中是本文参考的链接,如需要链接和更多资源,可以加入『知识星球』获取长期知识分享服务。

前文

>>> [Spring] Spring 探秘 之 事务配置(一)

Spring 事务传播规则

当我们调用一个基于Spring的Service接口方法(如UserService#addUser())时,它将运行于Spring管理的事务环境中,Service接口方法可能会在内部调用其它的Service接口方法以共同完成一个完整的业务操作,因此就会产生服务接口方法嵌套调用的情况, Spring通过事务传播行为控制当前的事务如何传播到被嵌套调用的目标服务接口方法中。

Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播:

在这里插入图片描述

事务传播行为类型说明
PROPAGATION_REQUIRED指定当前方法必需在事务环境中运行,如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS指定当前方法加入当前事务环境,支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY指定当前方法必须加入当前事务环境,使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW指定当前方法总是会为自己发起一个新的事务,如果发现当前方法已运行在一个事务中,则原有事务被挂起,我自己创建一个属于自己的事务,直我自己这个方法commit结束,原先的事务才会恢复执行。
PROPAGATION_NOT_SUPPORTED指定当前方法以非事务方式执行操作,如果当前存在事务,就把当前事务挂起,等我以非事务的状态运行完,再继续原来的事务。
PROPAGATION_NEVER指定当前方法绝对不能在事务范围内执行,如果方法在某个事务范围内执行,容器就抛异常,只有没关联到事务,才正常执行。(以非事务方式执行,如果当前存在事务,则抛出异常)
PROPAGATION_NESTED指定当前方法执行时,如果已经有一个事务存在,则运行在这个嵌套的事务中.如果当前环境没有运行的事务,就新建一个事务,并与父事务相互独立,这个事务拥有多个可以回滚的保证点。就是指我自己内部事务回滚不会对外部事务造成影响,只对DataSourceTransactionManager事务管理器起效。

说明:

当使用PROPAGATION_NESTED时,底层的数据源必须基于JDBC 3.0,并且实现者需要支持保存点事务机制.

事务隔离的必要性

  • 不考虑隔离性的话会出现以下问题:

    • 脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的.
    • 不可重复读:一个事务重新读取之前读取过的数据,发现该数据已经被另一个事务(在初始读之后提交)修改.
    • 幻读:一个事务重新执行一个返回符合一个搜索条件的行集合的查询, 发现满足条件的行集合因为另一个最近提交的事务而发生了改变.

而隔离级别就是解决以上问题的。

  • 隔离级别
隔离级别说明
ISOLATION_DEFAULTSpring默认选项,使用数据库的默认隔离级别(不同的数据库会有所区别)
ISOLATION_READ_UNCOMMITTED读未提交:允许读取还未提交的改变了的数据,可能导致脏读、幻读、不可重复读
ISOLATION_READ_COMMITTED读已提交:允许在并发事务已经提交后读取,可防止脏读(幻读和不可重复读可能会发生)
ISOLATION_REPEATABLE_READ可重复读:对相同字段的读取是一致的(除了数据库事务改变),防止脏读、不可重复读。
ISOLATION_SERIALIZABLE可序列化:串行化,完全遵从ACID的隔离级别,效率也是最低的。

Pg、Oracle默认的隔离级别为读已提交。

事务嵌套

REQUIRED(REQUIRES_NEW) 内层会新建一个事务,代码执行完成后,直接提交事务

/*UserInfoServiceImpl.propagationRequired*/

@Override
@Transactional(propagation = Propagation.REQUIRED)
public void propagationRequired() {
    log.debug("---------------propagationRequired");
    transationSevice.propagationRequiresNew();
    log.debug("---------------propagationRequired");
}

/*TransationSeviceImpl.propagationRequiresNew*/
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void propagationRequiresNew() {
    log.debug("---------------propagationRequiresNew");
}

日志

在这里插入图片描述

在这里插入图片描述

REQUIRED(REQUIRES_NEW[Exception]) 内层事务异常不会影响外层事务


/*UserInfoServiceImpl.propagationRequiredRollback*/

@Override
@Transactional(propagation = Propagation.REQUIRED)
public void propagationRequiredRollback() {
    try {
         log.debug("---------------propagationRequiredRollback");
         transationSevice.propagationRequiresNewRollBack();
         log.debug("---------------propagationRequiredRollback");
     } catch (Exception e) {
         log.error("---------------excaption");
     }
 }

/*TransationSeviceImpl.propagationRequiresNewRollBack*/
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void propagationRequiresNewRollBack() {
    log.debug("-------------------propagationRequiresNewRollBack");
    throw new RuntimeException();
}

在这里插入图片描述

REQUIRED(REQUIRES_NEW)[Exception] 外层事务异常不会影响内层事务

/*UserInfoServiceImpl.propagationRequiredCurrentRollback*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void propagationRequiredCurrentRollback() {
    log.debug("---------------propagationRequiredCurrentRollback");
    transationSevice.propagationRequiresNew();
    log.debug("---------------propagationRequiredCurrentRollback");
    throw new RuntimeException();
}

/*TransationSeviceImpl.propagationRequiresNew*/
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void propagationRequiresNew() {
    log.debug("---------------propagationRequiresNew");
}

在这里插入图片描述

REQUIRED(NESTED)内部嵌套事务结束后和外部事务一起提交


/*UserInfoServiceImpl.propagationRequiredAndNested*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void propagationRequiredAndNested() {
   log.debug("---------------propagationRequiredAndNested");
   UserInfo userInfo = new UserInfo("xx1",1,"xx1","xx","xx",1,new Date());
   insertSelective(userInfo);
   transationSevice.propagationNested();
   log.debug("---------------propagationRequiredAndNested");
   userInfo = new UserInfo("xx2",1,"xx2","xx","xx",1,new Date());
   insertSelective(userInfo);
}

/*TransationSeviceImpl.propagationNested*/
@Override
@Transactional(propagation = Propagation.NESTED)
public void propagationNested() {
   log.debug("-------------------propagationNested");
   UserInfo userInfo = new UserInfo("xx3",1,"xx1","xx","xx",1,new Date());
   userInfoService.insertSelective(userInfo);
}

在这里插入图片描述

在这里插入图片描述

REQUIRED(NESTED[Exception]) 虽然内部事务嵌套在外部事务中,但内部回滚时只回滚内部的操作不影响外部

/*UserInfoServiceImpl.propagationRequiredAndNested*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void propagationRequiredAndNested() {
    try {
        log.debug("---------------propagationRequiredAndNested");
        UserInfo userInfo = new UserInfo("xx1", 1, "xx1", "xx", "xx", 1, new Date());
        insertSelective(userInfo);
        transationSevice.propagationNested();
        log.debug("---------------propagationRequiredAndNested");
        userInfo = new UserInfo("xx2", 1, "xx2", "xx", "xx", 1, new Date());
        insertSelective(userInfo);
    } catch (Exception e) {
        log.error("---------------excaption");
    }
}

/*TransationSeviceImpl.propagationNested*/
@Override
@Transactional(propagation = Propagation.NESTED)
public void propagationNested() {
   log.debug("-------------------propagationNested");
   UserInfo userInfo = new UserInfo("xx3",1,"xx1","xx","xx",1,new Date());
   userInfoService.insertSelective(userInfo);
   throw new RuntimeException();
}

在这里插入图片描述

在这里插入图片描述

REQUIRED(NESTED)[Exception] 外部事务回滚导致内部也不会提交

/*UserInfoServiceImpl.propagationRequiredAndNested*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void propagationRequiredAndNested() {
      log.debug("---------------propagationRequiredAndNested");
      UserInfo userInfo = new UserInfo("xx1", 1, "xx1", "xx", "xx", 1, new Date());
      insertSelective(userInfo);
      transationSevice.propagationNested();
      log.debug("---------------propagationRequiredAndNested");
      userInfo = new UserInfo("xx2", 1, "xx2", "xx", "xx", 1, new Date());
      insertSelective(userInfo);
      throw new RuntimeException();
}

/*TransationSeviceImpl.propagationNested*/
@Override
@Transactional(propagation = Propagation.NESTED)
public void propagationNested() {
   log.debug("-------------------propagationNested");
   UserInfo userInfo = new UserInfo("xx3",1,"xx1","xx","xx",1,new Date());
   userInfoService.insertSelective(userInfo);
}

REQUIRED(NESTED)[Exception]事务日志

REQUIRED(NESTED)[Exception]数据表

REQUIRED(REQUIRED[Exception]) 内部回滚,外部进行异常捕获,试图使当前事务不回滚的情况下报错

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

错误信息提示为已将当前事务标记为回滚状态

/*UserInfoServiceImpl.propagationRequired*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void propagationRequired() {
   try {
       log.debug("---------------propagationRequired");      
       transationSevice.propagationRequired();
       log.debug("---------------propagationRequired");
   } catch (Exception e) {
       log.error("---------------excaption");
   }
}

/*TransationSeviceImpl.propagationRequired*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void propagationRequired() {
   log.debug("----------------propagationRequired");
   throw new RuntimeException();
}

REQUIRED(REQUIRED[Exception])事务日志

REQUIRED(REQUIRED[Exception])数据表为空

尝试在外层进行事务提交

@Override
@Transactional(propagation = Propagation.REQUIRED)
public void propagationRequired() {
    UserInfo userInfo = new UserInfo("xx1", 1, "xx1", "xx", "xx", 1, new Date());
    insertSelective(userInfo);
    transationSevice.propagationNested();
    try {
        log.debug("---------------propagationRequired");
        transationSevice.propagationRequired();
        log.debug("---------------propagationRequired");
    } catch (Exception e) {
        log.error("---------------excaption");
    }
    UserInfo userInfo2 = new UserInfo("xx2", 1, "xx1", "xx", "xx", 1, new Date());
    insertSelective(userInfo2);
    transationSevice.propagationNested();
}

REQUIRED(REQUIRED[Exception])事务日志

REQUIRED(REQUIRED[Exception])数据表为空

相同传播规则的事务,内部异常会导致整体事务无法提交,直接回滚。

事务注解 和 AOP切面的环绕通知

  • 内部调用无法触发事务
/*TransationSeviceImpl.propagationRequired*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void propagationRequired() {
      log.debug("---------------propagationRequired");
      propagationRequiresNew();//或this.propagationRequiresNew();
      log.debug("---------------propagationRequired");
}

/*TransationSeviceImpl.propagationRequiresNew*/
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void propagationRequiresNew() {
    log.debug("---------------propagationRequiresNew");
}

内部调用无法触发事务

  • 非接口定义的内部方法无法触发事务
/*TransationSeviceImpl.propagationRequired*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void propagationRequired() {
      log.debug("---------------propagationRequired");
      propagationRequiresNew();
      log.debug("---------------propagationRequired");
}

/*TransationSeviceImpl.propagationRequiresNew2*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void propagationRequiresNew2() {
    log.debug("---------------propagationRequiresNew2");
}

非接口定义的内部方法无法触发事务

  • 接口实例注入后触发AOP环绕通知

@Resource
private UserInfoService userInfoService;

/*TransationSeviceImpl.propagationRequired*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void propagationRequired() {
      log.debug("---------------propagationRequired");
      userInfoService.propagationRequiresNew();
      log.debug("---------------propagationRequired");
}

/*TransationSeviceImpl.propagationRequiresNew2*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void propagationRequiresNew2() {
    log.debug("---------------propagationRequiresNew2");
}

接口实例注入后触发AOP环绕通知

备注

Spring Boot 只开启事务日志

logging:
  level:
    root: WARN
    com:
      example: DEBUG
    org:
      springframework:
        jdbc: DEBUG

REFRENCES


更多

扫码关注架构探险之道,回复『源码』,获取本文相关源码

.

扫码加入知识星球,获取珍贵笔记、视频、电子书的等资源。

.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值