参考文章:
    SpringBoot之数据库(四)——事务处理:隔离级别与传播行为
    https://blog.csdn.net/huangjun0210/article/details/84202333

    SpringBoot之数据库(四)——事务处理:隔离级别与传播行为

    1. 隔离级别(isolation) 1.1 未提交读 1.2 读写提交 1.3 可重复读 1.4 串行化 1.5 使用合理的隔离级别

    2. 传播行为(pragation) 2.1 REQUIRED(0) 2.2 SUPPORTS(1) 2.3 MANDATORY(2) 2.4 REQUIRES_NEW(3) 2.5 NOT_SUPPORTED(4) 2.6 NEVER(5) 2.7 NESTED(6)

    3. @Transactional的自调用失效问题

    在Spring中,数据库事务是通过AOP技术来提供服务的。对于声明式事务,是使用@Transactional进行标注的。
    在@Transactional允许配置许多事物的属性,如事务的隔离级别与传播行为。

    1. 隔离级别(isolation)
      数据库标准提出了4种isolation事务隔离级别,分别为:未提交读、读写提交、可重复读和串行化。源码如下: ``` package org.springframework.transaction.annotation;

    /*

    • 隔离级别数字数字配置含义:
    • -1:数据库默认隔离级别
    • 1:未提交读
    • 2:读写提交
    • 4:可重复读
    • 8:串行化 */ public enum Isolation { DEFAULT(-1), READ_UNCOMMITTED(1), READ_COMMITTED(2), REPEATABLE_READ(4), SERIALIZABLE(8);

      private final int value;

      private Isolation(int value) {

      1. this.value = value;

      }

      public int value() {

      1. return this.value;

      } } ```

    SpringBoot设置默认隔离级别配置:

    1. spring:
    2. datasource:
    3. tomcat:
    4. default-transaction-isolation: 2
    5. # dbcp2:
    6. # default-transaction-isolation: 2

    1.1 未提交读
    未提交读(READ_UNCOMMITTED)是最低的隔离级别,其含义是允许一个事物读取另一个事物没提交的数据。优点在于并发能力高,适合那些对数据一致性没有要求而追求高并发的场景,最大缺点是出现脏读。

    1. @Transactional(isolation = Isolation.READ_UNCOMMITTED)

    例子讲解: SpringBoot之数据库(四)——事务处理:隔离级别与传播行为 - 图1 T3时刻,因为采用未提交读,所以事务2可以读取事务1未提交的库存数据为1,这里当它扣减库存后则数据为0,然后它提交了事务,库存就变为了0 。,而事务1在T5时刻回滚事务,因为第一类丢失更新已经被克服,所以它不会将库存回滚到2,那么最后的结果就变为了0,这样就出现了错误。

    1.2 读写提交
    读写提交(READ_COMMITTED),一个事务只能读取另外一个事务已提交的数据,不能读取未提交的数据。该级别克服了脏读,但不可重复读。

    1. @Transactional(isolation = Isolation.READ_COMMITTED)

    案例讲解: SpringBoot之数据库(四)——事务处理:隔离级别与传播行为 - 图2 SpringBoot之数据库(四)——事务处理:隔离级别与传播行为 - 图3

    1.3 可重复读
    可重复读(REPEATABLE_READ),目标是克服读写提交中出现的不可重复读的现象,但会出现幻读。

    1. @Transactional(isolation = Isolation.REPEATABLE_READ)

    案例讲解: SpringBoot之数据库(四)——事务处理:隔离级别与传播行为 - 图4 SpringBoot之数据库(四)——事务处理:隔离级别与传播行为 - 图5

    1.4 串行化
    串行化(SERIALIZABLE),是数据库最高的隔离级别,它能够完全保证数据的一致性,但性能降低了。

    1.5 使用合理的隔离级别

    隔离级别和可能发生的现象如下: SpringBoot之数据库(四)——事务处理:隔离级别与传播行为 - 图6

    对于不同的数据库,支持的隔离级别也不一样:Oracle只能支持读写提交和串行化,而MySQL能够支持4种,对于Oracle默认的隔离级别为读写提交,MySQL则是可重复读。

    1. 传播行为(pragation)
      Spring事务机制中对数据库存在7种传播行为,源码如下: ``` package org.springframework.transaction.annotation;

    public enum Propagation { REQUIRED(0), SUPPORTS(1), MANDATORY(2), REQUIRES_NEW(3), NOT_SUPPORTED(4), NEVER(5), NESTED(6);

    1. private final int value;
    2. private Propagation(int value) {
    3. this.value = value;
    4. }
    5. public int value() {
    6. return this.value;
    7. }

    }

    1. 2.1 REQUIRED(0)<br />需要事务,它是默认传播行为,如果当前存在事务,就沿用当前事务,否则新建一个事务运行子方法。
    2. 2.2 SUPPORTS(1)<br />支持事务,如果当前存在事务,就沿用当前事务,如果不存在,则继续采用无事务的方式运行子方法。
    3. 2.3 MANDATORY(2)<br />必须使用事务,如果当前没有事务,则会抛出异常,如果存在当前事务,就沿用当前事务。
    4. 2.4 REQUIRES_NEW(3)<br />无论当前事务是否存在,都会创建新事务运行方法,这样新事务就可以拥有新的锁和隔离级别等特性,与当前事务相互独立。
    5. 2.5 NOT_SUPPORTED(4)<br />不支持事务,当前存在事务时,将挂起事务,运行方法。
    6. 2.6 NEVER(5)<br />不支持事务,如果当前方法存在事务,则抛出异常,否则继续使用无事务机制运行。
    7. 2.7 NESTED(6)<br />在当前方法调用子方法时,如果子方法发生异常,只回滚子方法执行过的SQL,而不回滚当前方法的事务。
    8. 常用的传播行为主要有三种:REQUIRED REQUIRES_NEW NESTED
    9. 3. @Transactional的自调用失效问题<br />注解@transactional的底层实现是Spring AOP技术,而Spring AOP技术使用的是动态代理。这就意味着对于静态(static)方法和非public方法,注解@Transactional是失效的。<br />自调用是指一个类的一个方法去调用自身另外一个方法的过程。在自调用的过程中,是类自身的调用,而不是代理对象去调用, 那么就不会产生 AOP,这样 Spring就不能把你的代码织入到约定的流程中。<br />为了克服这个问题,一方面可以写两个Service,用一个Service去调用另一个Service,这样就是代理对象的调用。Spring才会将你的代码织入事务流程。另一方面,也可以从Spring IoC容器中获取代理对象来启用AOP。从Spring IoC容器中获取代理对象的代码实例如下:

    import com.springboot.web.dao.UserDao; import com.springboot.web.model.User; import com.springboot.web.service.UserService; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional;

    import java.util.List;

    @Service public class UserServiceImpl implements UserService, ApplicationContextAware {

    1. @Autowired
    2. UserDao userDao;
    3. private ApplicationContext applicationContext;
    4. //实现生命周期方法,设置Ioc容器
    5. @Override
    6. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    7. this.applicationContext = applicationContext;
    8. }
    9. @Override
    10. @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
    11. public void createUsers(List<User> userList){
    12. UserService userService = applicationContext.getBean(UserService.class);
    13. for(User user : userList){
    14. userService.createUser(user);
    15. }
    16. }
    17. @Override
    18. @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRES_NEW)
    19. public void createUser(User user){
    20. userDao.createUser(user);
    21. }

    } ``` ————————————————
    版权声明:本文为CSDN博主「逍遥俊子」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/huangjun0210/article/details/84202333