SpringJdbc学习笔记-04事务的传播和隔离

Posted Moon&&Dragon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringJdbc学习笔记-04事务的传播和隔离相关的知识,希望对你有一定的参考价值。

4 Spring的事务传播行为

名称说明
PROPAGATION_REQUIRED(默认),如果当前存在事务,就加入;如果当前不存在事务,就新建一个事务。
PROPAGATION_SUPPORTS如果当前存在事务,就使用当前事务,如果当前没有事务,那么就以非事务方法执行。
PROPAGATION_MANDATORY如果当前存在事务,就使用当前事务,如果当前没有事务,那么就会抛出异常IllegalTransactionStateException
PROPAGATION_REQUIRES_NEW无论当前存不存在事务,都会创建一个新事务;如果当前存在事务,就将当前事务挂起(暂停)。
PROPAGATION_NOT_SUPPORTED无论当前存不存在事务,都以非事务的方式执行,如果当前存在事务,就将当前事务挂起(暂停)。
PROPAGATION_NEVER以非事务方式执行,不支持在事务的环境中执行,如果当前存在事务,就抛异常。
PROPAGATION_NESTED如果当前存在事务,则以子事务在嵌套事务内部执行,如果当前不存在事务就会和REQUIRED一样,新建一个事务。

准备好的测试类

Service1:

@Service
public class StudentService1 {
    @Autowired
    private StudentService2 studentService2;
    
  	// 测试时,service的事务第一次没有加
    @Transactional(propagation = Propagation.REQUIRED)
    public void test() {
        studentService2.batchInsert();
    }
}

Service2:

@Service
public class StudentService2 {
    @Autowired
    private StudentDao studentDao;

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void batchInsert(){
        try{
            for (int i = 0; i < 10; i++) {
                if (i == 5) {
                    throw new RuntimeException("无缘无故的异常");
                }
                Student student = new Student(null, "小月月", "湖北武汉" + i, 20);
                studentDao.insert(student);
            }
        }catch (Exception e){
            System.out.println(e.getMessage());
            throw new RuntimeException(e.getMessage());
        }
    }
}

4.1 REQUIRED

默认事务传播行为,如果事务存在就加入,如果不存在就创建

结果分析:

  • 在service1中不加事务,在执行时,会去默认的创建Service2的事务

在这里插入图片描述

  • 在service1中加上事务,在执行时,我们会发现,先去创建了service1的事务,但是到了service2的时候,并没有去重新创建事务,而是加入了已有的事务

在这里插入图片描述


4.2 SUPPORTS

如果有事务,就加入没有事务就不用事务

结果分析:

  • service1中添加事务,而在service2中将事务传播改为supports,会发现和默认的测试2中时一样的,在service2中加入到了已有的事务

在这里插入图片描述

  • 而在service1中取消事务后,在运行时会发现,service2并没有自己新建事务,直接就是没有事务

在这里插入图片描述


4.3 MANDATORY

如果有事务,就加入,如果没有,就抛异常

结果分析:

  • service1中添加事务,而在service2中将事务传播改为mandatory,会发现和默认的测试2中时一样的,在service2中加入到了已有的事务

在这里插入图片描述

  • 而在service1中取消事务后,执行到service2的时候并没有新建事务或没用事务,而是直接去抛出了异常

在这里插入图片描述


4.4 REQUIRES_NEW

不管有没有事务,都会去创建一个事务,如果有事务,会把之前的事务挂起,然后创建新事务

结果分析:

  • 在service1存在默认事务,service2是requires_new的传播方式,发现会去挂起1中的事务,而创建一个新的事务

在这里插入图片描述


4.5 NOT_SUPPORTED

不管有没有事务,都会去以非事务的方式执行,相比supports,更加的肯定,就是不用事务,你有事务,我不加入,直接把你挂起来。

结果分析:

  • service1中添加默认事务,service2中的事务的传播是not_supported,所以service2会把service1中的事务挂起,而去使用非事务的方式执行,所以后面会和我们说,应该要回滚,但是找不到可用的事务,因为唯一可用的事务被挂起了

    在这里插入图片描述


4.6 NEVER

不管有没有事务,都会以非事务的方式执行,但是如果有事务的话,不会像not_supported把事务挂起,而是直接抛异常,直接不干了

结果分析:

service1存在默认事务,service2的事务的传播改为never,在执行后直接抛出了异常,说找到了事务的存在,而nerver要求事务不能存在,否则不执行

在这里插入图片描述


4.7 NESTED

1、内嵌事务是一个新事务

2、内嵌事务可以单独回滚,不影响外部事物和其他子事务

3、如果外部事务回滚了,那么内嵌事务一定会回滚

4、只有外部事务提交了,内部事务才会提交

在这里插入图片描述

在之前的传播行为中,基本都是会去寻找有没有事务,如果存在就加入,不存在就创建等等,或者直接不能在事务中执行,而和requires_new比的话,nested是创建了一个子事务,这个事务属于外部的事务,是包含的关系。而requires_new是将之前的事务挂起,重新去创建一个平级的事务,是平级关系。

将service2的代码中事务的传播改为nested,并添加一个没有异常的方法,将service1改为如下

@Service
public class StudentService1 {
    @Autowired
    private StudentService2 studentService2;

    @Transactional(propagation = Propagation.REQUIRED)
    public void test() {

        try{
            /*
            * 有异常而且传播行为是NESTED
            * 这里使用try把异常处理掉,会发现在service2里面的事务回去回滚
            * 但是不会影响到其他的方法
            */
            studentService2.batchInsert();
        }catch (Exception e){}

        // 第二个方法,没有异常,可以正常执行,不会受到上面的回滚而回滚
        studentService2.batchInsertNoException();
    }
}

结果分析:

  • 执行后我们会发现在方法1中出现异常后,在内部的子事务进行了回滚,但是外部事务中还有方法2是没异常的,提交成功

在这里插入图片描述

  • 方法1的传播方式为nested,出现异常后进行了回滚,方法2正常执行

在这里插入图片描述


5 事务的隔离级别

5.1 在使用事务常见的问题

  • 脏读

    事务一读取数据时,事务二也在进行,事务一进行操作后,还没有提交被事务二读取到,事务二对事务一还没有提交的数据进行修改,这时候就会出现事务一在事务二读取到数据以后,进行修改数据,单事务二却在改变之前的数据。

在这里插入图片描述

  • 不可重复读

    事务一读取到数据以后,正在对数据进行业务处理的时候,事务二对数据进行了修改,那么事务一再读取的时候就会发现数据不一致

  • 幻读

    和不可重复读比较类似,也是事务事务一在处理数据时,事务二对数据进行了添加,那么事务一读取到的数据也不一致。和不可重复读的区别主要是前者一般是修改或删除,而后者主要是新增

5.2 isolation事务隔离级别

名称说明
ISOLATION_DEFAULT默认,默认级别,默认使用数据库的隔离级别
ISOLATION_READ_UNCOMMITTED未提交读,隔离级别最低,没有提交就可以读到,会出现脏读,不可重复读,幻读等
ISOLATION_READ_COMMITTED提交读,只有提交后才会被读取到,可以避免脏读,但是无法避免不可重复读,幻读等
ISOLATION_REPEATABLE_READ可重复读,保证一个事务前后读查询取到的数据的一致,可以避免脏读,不可重复读,但是无法避免幻读。
ISOLATION_SERIALIZABLE序列化级,最高级别,可以严格的进行事务隔离,但是资源消耗大

以上是关于SpringJdbc学习笔记-04事务的传播和隔离的主要内容,如果未能解决你的问题,请参考以下文章

SpringJdbc学习笔记-02编程式事务

SpringJdbc学习笔记-02编程式事务

SpringJdbc学习笔记-03声明式事务

SpringJdbc学习笔记-03声明式事务

spring学习笔记声明式事务

Spring事务配置的五种方式和spring里面事务的传播属性和事务隔离级别