JAVA 基础 之 事务的传播与隔离机制
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为。事务传播行为是Spring框架独有的事务增强特性,他不属于的事务实际提供方数据库行为。这是Spring为我们提供的强大的工具箱,使用事务传播可以为我们的开发工作提供许多便利。
事务传播行为用来描述当某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播。
1: 7种传播机制的介绍
public enum Propagation {
REQUIRED(0),
SUPPORTS(1),
MANDATORY(2),
REQUIRES_NEW(3),
NOT_SUPPORTED(4),
NEVER(5),
NESTED(6);
private final int value;
private Propagation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
1.1 REQUIRED (默认方式)
必须在事务中运行,如果当前存在事务则在当前事务中运行子方法,如果不存在事务则新建一个事务运行子方法。
Case1: 外层没有事务,内层事务是Required
@GetMapping("/exception/{name}/{type}")
public void transSupportTest(@PathVariable("name") String name, @PathVariable("type") int type){
this.houseService.insertAHouse();
int i = 1/0;
System.out.println("外层没有事务,内层事务是required,外层抛异常");
}
@Transactional(propagation = Propagation.REQUIRED)
public void insertAHouse(){
MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
this.houseMapper.insertAHouse(house);
System.out.println("insertAHouse"+house.getHouse_id());
}
运行结果:
house表中新增一条记录,user表没有新增记录
当外层没有事务时,内层是一个独立运行的事务,与外层没有关系,即外
层异常不会影响内层的运行
Case2: 外层有事务,内层事务是Required
修改外层insertUser代码设置为有事务
@Transactional(propagation = Propagation.REQUIRED)
public void insertUser(){
this.houseService.insertAHouse();
MUser user = new MUser("ayueyue", 90);
this.userMapper.insertAuser(user);
int i=1/0;
System.out.println("insertAuser" + user.getCxy_id());
}
运行结果:
house表和user表没有新增记录
内层与外层同属于一个事务,即:内层事务roll back,外层也要同时roll
back;如果外层事务roll back,内层也要同时roll back
1.2 SUPPORTS
如果当前方法已经存在事务中了则在事务中运行,如果不存在事务则在非事务环境下运行子方法
Case1: 外层没有事务,内层事务是SUPPORTS
外层代码:
public void insertUser(){
this.houseService.insertAHouse();
MUser user = new MUser("ayueyue", 90);
int i=1/0;
this.userMapper.insertAuser(user);
System.out.println("insertAuser" + user.getCxy_id());
}
内层代码:
@Transactional(propagation = Propagation.SUPPORTS)
public void insertAHouse(){
MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
this.houseMapper.insertAHouse(house);
int i=1/0;
this.houseMapper.insertAHouse(house);
System.out.println("insertAHouse"+house.getHouse_id());
}
运行结果:
house表中新增一条记录,user表没有新增记录
当外层没有事务时,内层在非事务环境下运行
Case2: 外层有事务,内层事务是SUPPORTS
外层代码:
@Transactional
public void transSupportTest(){
this.houseService.insertAHouse();
int i = 1/0;
System.out.println("外层没有事务,内层事务是supports,外层抛异常");
}
内层代码:
@Transactional(propagation = Propagation.SUPPORTS)
public void insertAHouse(){
MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
this.houseMapper.insertAHouse(house);
System.out.println("insertAHouse"+house.getHouse_id());
}
运行结果:
house表和user表没有新增记录
内层与外层同属于一个事务,即:内层事务roll back,外层也要同时roll
back;如果外层事务roll back,内层也要同时roll back
1.3 MANDATORY
必须在事务中运行,如果当前子方法不在事务中抛异常,如果在事务中则沿用当前事务
Case1: 外层没有事务,内层事务是MANDATORY
外层代码:
public void insertUser(){
this.houseService.insertAHouse();
MUser user = new MUser("ayueyue", 90);
this.userMapper.insertAuser(user);
System.out.println("insertAuser" + user.getCxy_id());
}
内层代码:
@Transactional(propagation = Propagation.MANDATORY)
public void insertAHouse(){
MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
this.houseMapper.insertAHouse(house);
System.out.println("insertAHouse"+house.getHouse_id());
}
运行结果:
image.png
Case2: 外层有事务,内层事务是MANDATORY
外层代码:
@Transactional
public void insertUser(){
this.houseService.insertAHouse();
MUser user = new MUser("ayueyue", 90);
this.userMapper.insertAuser(user);
int i=1/0;
System.out.println("insertAuser" + user.getCxy_id());
}
内层代码:
@Transactional(propagation = Propagation.MANDATORY)
public void insertAHouse(){
MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
this.houseMapper.insertAHouse(house);
System.out.println("insertAHouse"+house.getHouse_id());
}
运行结果:
house表和user表没有新增记录
内层与外层同属于一个事务,即:内层事务roll back,外层也要同时roll
back;如果外层事务roll back,内层也要同时roll back
1.4 REQUIRES_NEW
如果当前方法已经存在事务中则挂起当前事务并新建一个事务运行子方法,如果不存在则新建一个事务运行
Case1: 外层方法和内层方法在同一个bean中
在userMapper中新增一个插入house表的接口,编写如下代码:
@Transactional
public void insertAUser(String name, int type){
MUser user = new MUser(name, type);
this.userMapper.insertAuser(user);
System.out.println("insertAuser" + user.getCxy_id());
this.insertAHouse(user.getCxy_id());
int i = 1/0;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertAHouse(int master){
MHouse house = new MHouse("高新区中和街道"+master, "cxy"+master);
this.userMapper.insertAHouse(house);
System.out.println("insertAHouse"+house.getHouse_id());
}
运行结果:
house表和user表没有新增记录(内外层同时发生了roll back),即当二个事务在同一Bean中时,Requires_new没有重新起事务而是沿用了外层事务。
Case 2: 外层方法和内层方法在同一个bean中,外层没有事务
public void insertAUser(String name, int type){
MUser user = new MUser(name, type);
this.userMapper.insertAuser(user);
int i = 1/0;
System.out.println("insertAuser" + user.getCxy_id());
this.insertAHouse(user.getCxy_id());
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertAHouse(int master){
MHouse house = new MHouse("高新区中和街道"+master, "cxy"+master);
this.userMapper.insertAHouse(house);
System.out.println("insertAHouse"+house.getHouse_id());
}
运行结果:
house表新增一条记录,user表新增一条记录(insertAUser函数中的insertAHouse没有执行,因为是非事务环境)
即内层是一个独立运行的事务,和外层运行互不干扰。
Case 3: 外层方法和内层方法在不同的Bean中
在userservice中编写外层代码
@Transactional
public void insertUser(){
this.houseService.insertAHouse();
MUser user = new MUser("ayueyue", 90);
this.userMapper.insertAuser(user);
int i=1/0;
System.out.println("insertAuser" + user.getCxy_id());
}
在houseService中编写内层代码
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertAHouse(){
MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
this.houseMapper.insertAHouse(house);
this.houseMapper.insertAHouse(house);
System.out.println("insertAHouse"+house.getHouse_id());
}
运行结果:
house表新增二条记录,user表没有新增记录
1): 即内层是一个独立运行的事务,和外层事务roll back不影响内层事务。
2): 当我们将外层的异常代码(int i=1/0)移到内层中,会发现内外层同时rollback 了,即内层出错了如果外层没有try catch,则内外层一起roll back
3): 去掉外层的事务,内层是一个独立运行的事务,外层以非事务方式运行
1.5 NOT_SUPPORTED
当外层没有事务时,该方法在非事务环境下运行
Case1: 外层方法和内层方法在同一个bean中
编写外层方法,设置事务
@Transactional
public void insertAUser(String name, int type){
MUser user = new MUser(name, type);
this.userMapper.insertAuser(user);
insertAUserSameBean();
System.out.println("insertAuser" + user.getCxy_id());
}
编写内层方法,设置传播机制NOT_SUPPORTED
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void insertAUserSameBean(){
MUser user = new MUser("sameBean", 999);
this.userMapper.insertAuser(user);
int i =1/0;
//this.insertAUserInternal("sameBean", 999);
System.out.println("insertAuser" + user.getCxy_id());
}
运行结果:
house表和user表都没有新增记录(内层发生异常时把外层也roll back了)
即当内层方法和外层方法在同一个bean中时,NOT_SUPPORTE没有把外层的事务挂起,而是会沿用外层的事务
Case 2: 外层方法和内层方法不在同一个bean中
编写外层方法,设置事务
@Transactional
public void insertAUserInternal(){
MUser user = new MUser("不在同一个bean", 234);
this.userMapper.insertAuser(user);
this.houseService.insertAHouse();
System.out.println("insertAuser" + user.getCxy_id());
// this.insertAUserException();
}
在houseService类编写内层方法,设置传播机制NOT_SUPPORTED
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void insertAHouse(){
MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
this.houseMapper.insertAHouse(house);
int i =1/0;
this.houseMapper.insertAHouse(house);
System.out.println("insertAHouse"+house.getHouse_id());
}
运行结果:
house表新增一条记录,user表没有新增记录(内层是在非事务环境下运行的,而且发生异常时把外层也roll back了)
即当内层方法和外层方法不在同一个bean中时,内层方法在非事务环境下运行,当内层事务发生异常时,会将外层事务roll back.
1.6 NEVER
外层没有事务,非事务方式独立运行方法。当外层有事务分2种情况:
Case1: 外层方法和内层方法在同一个bean中
编写外层方法,设置事务
@Transactional
public void insertUser(){
MUser user = new MUser("ayueyue", 90);
this.userMapper.insertAuser(user);
this.insertAUserException();
System.out.println("insertAuser" + user.getCxy_id());
}
编写内层方法,设置传播机制NEVER
@Transactional(propagation = Propagation.NEVER)
public void insertAUserException(){
MUser user = new MUser("ayueyue", 91);
this.userMapper.insertAuser(user);
System.out.println("insertAuser" + user.getCxy_id());
int x = 1/0;
}
运行结果:
user表没有新增记录(内层运行异常导致外层发生了roll back)
内层方法和外层同一个事务中运行。 内层出错,外层也rollback; 外层出错,内层也rollback
Case 2: 外层方法和内层方法不在同一个bean中
编写外层方法,设置事务
@Transactional
public void insertAUserInternal(){
MUser user = new MUser("不在同一个bean", 234);
this.userMapper.insertAuser(user);
this.houseService.insertAHouse();
System.out.println("insertAuser" + user.getCxy_id());
// this.insertAUserException();
}
在houseService类编写内层方法,设置传播机制NEVER
@Transactional(propagation = Propagation.NEVER)
public void insertAHouse(){
MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
this.houseMapper.insertAHouse(house);
//int i =1/0;
this.houseMapper.insertAHouse(house);
System.out.println("insertAHouse"+house.getHouse_id());
}
运行结果:
house表和user表都没有新增记录并且抛出异常
image.png
1.7 NESTED
如果当前已经在一个事务中了,则嵌套在已有的事务中作为一个子事务。如果当前没在事务中则开启一个事务。
外层没有事务:内层新建一个新的事务独立运行
外层有事务:内外层属于同一个事务,内层运行完后要等待外层一起提交。 外层出错roll back, 内层也roll back; 内层出错了如果外层有try catch,不影响外层执行只roll back内层; 如果外层没有try catch,则内外层一起roll back
case 1: 外层没有事务,内层事务NESTED:
外层代码:
public void insertUser(){
MUser user = new MUser("ayueyue", 90);
this.userMapper.insertAuser(user);
this.houseService.insertAHouse();
System.out.println("insertAuser" + user.getCxy_id());
}
内层代码:
@Transactional(propagation = Propagation.NESTED)
public void insertAHouse(){
MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
this.houseMapper.insertAHouse(house);
int i =1/0;
this.houseMapper.insertAHouse(house);
System.out.println("insertAHouse"+house.getHouse_id());
}
运行结果:
house表没有新增记录,user表新增记录,即当外层没有事务时,NESTED会单独起一个事务运行,与外层互不影响。
case 2:外层有事务,内层事务NESTED
外层代码
@Transactional
public void insertAUserInternal(){
MUser user = new MUser("不在同一个bean", 234);
this.userMapper.insertAuser(user);
this.houseService.insertAHouse();
int i = 1/0;
System.out.println("insertAuser" + user.getCxy_id());
// this.insertAUserException();
}
内层代码
@Transactional(propagation = Propagation.NESTED)
public void insertAHouse(){
MHouse house = new MHouse("高新区中和街道0927", "cxy0927");
this.houseMapper.insertAHouse(house);
this.houseMapper.insertAHouse(house);
System.out.println("insertAHouse"+house.getHouse_id());
}
运行结果:
house表user表都没有新增记录,即当外层有事务时,NESTED嵌入到外层的事务中运行,同时提交,同时roll back.
2 事务的隔离机制
Spring在transaction.annotation包中规定了5种类型的隔离级别
public enum Isolation {
//默认的隔离级别,使用数据库默认的事务隔离级别
DEFAULT(-1),
READ_UNCOMMITTED(1),
READ_COMMITTED(2),
REPEATABLE_READ(4),
SERIALIZABLE(8);
private final int value;
private Isolation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
2.1 READ_UNCOMMITTED
未提交读,是事务隔离级别中最低的一种隔离方式。一个事务可以读取到其他事务没有提交数据,会导致脏读。
2.2 READ_COMMITTED
读写提交,一个事务只能读取其他事务已经提交的数据,可以避免数据脏读的情况。
2.3 REPEATABLE_READ
可重复读,一个事务只能在另外一个事务完全提交了数据后,才能再次读取数据。会导致幻读。
2.3 SERIALIZABLE
串行化,事务的最高隔离级别, 多个事务被处理为顺序执行。