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事务的传播和隔离的主要内容,如果未能解决你的问题,请参考以下文章