前言:最近阅读了网上很多关于讲解Spring事务的七种传播机制的文章,很少有通过结合实际代码进行讲解的,对于小白来说干巴巴的文字解释略微抽象,没有一个具体形象化的概念,下面我就通过理论+实践的方式来对Spring事务的每一种传播机制进行剖析,这样大家也容易记得住,毕竟实践才是检验真理的唯一标准!每一个字和每一行代码都是博主纯手打的!
目录
一、REQUIRED(默认)
1.1、基础概念
官方解释:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
如果A、B两个方法都加了@Transactional注解,默认是REQUIRED传播行为。那么如果A方法调用B方法,它们会共用一个事务,因为默认会使用同一条连接,相当于一个事务里执行。
1.2、代码详解
例如,在插入一条用户信息以后会紧接着插入一条角色信息,最后我手动写了个除数不能为0的异常,以下是关键代码:
UserService关键代码:
@Transactional(rollbackFor = Exception.class)
public void insertUser(){
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setUsername("张三");
user.setPassword("123456");
userMapper.insert(user);
roleService.insertRole();
int a = 1/0;
}
RoleService关键代码:
@Transactional(rollbackFor = Exception.class)
public void insertRole(){
Role role = new Role();
role.setId(UUID.randomUUID().toString());
role.setName("管理员");
roleMapper.insert(role);
}
运行结果如下:
虽然程序中会抛出java.lang.ArithmeticException: / by zero的异常,但事务都进行了回滚操作!
二、SUPPORTS
2.1、基础概念
官方解释:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
如果A方法没有事务(没有加@Transactional注解),B方法配置了SUPPORTS传播行为,那么B方法也会以非事务方式执行。
如果A方法存在事务(加了@Transactional注解),B方法配置了SUPPORTS传播行为,那么B方法会挂起自己的事务,加入到A方法的事务来执行。
2.2、代码详解
整体代码逻辑依旧不变。
2.2.1、不加@Transactional注解
在insertUser方法上面不加@Transactional注解。
UserService关键代码:
public void insertUser(){
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setUsername("张三");
user.setPassword("123456");
userMapper.insert(user);
roleService.insertRole();
int a = 1/0;
}
RoleService关键代码:
@Transactional(rollbackFor = Exception.class,propagation = Propagation.SUPPORTS)
public void insertRole(){
Role role = new Role();
role.setId(UUID.randomUUID().toString());
role.setName("管理员");
roleMapper.insert(role);
}
运行结果如下:
可以很明显的看出,虽然程序中会抛出java.lang.ArithmeticException: / by zero的异常,但2条数据都成功入库了,这是因为insertUser方法没有配置事务,那么insertRole方法也会以非事务的方式进行运行。
2.2.2、加@Transactional注解
仅仅需要在insertUser方法上面加上@Transactional注解。
UserService关键代码:
@Transactional(rollbackFor = Exception.class)
public void insertUser(){
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setUsername("张三");
user.setPassword("123456");
userMapper.insert(user);
roleService.insertRole();
int a = 1/0;
}
RoleService代码不动!
运行结果如下:
虽然程序中会抛出java.lang.ArithmeticException: / by zero的异常,但是2条数据都进行了回滚,这是因为insertRole方法配置了SUPPORTS传播行为,那么insertRole方法会挂起自己的事务,加入到inserUser方法的事务来执行。
三、MANDATORY
3.1、基础概念
官方解释:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
如果A方法没有事务(没有加@Transactional注解),B方法配置了MANDATORY传播行为,那么B方法将会抛出异常。
如果A方法存在事务(加@Transactional注解),B方法配置了MANDATORY传播行为,那么B方法将加入到该存在的事务来执行。
3.2、代码详解
整体代码逻辑依旧不变。
3.2.1、不加@Transactional注解
在insertUser方法上面不加@Transactional注解。
UserService关键代码:
public void insertUser(){
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setUsername("张三");
user.setPassword("123456");
userMapper.insert(user);
roleService.insertRole();
int a = 1/0;
}
RoleService关键代码:
@Transactional(rollbackFor = Exception.class,propagation = Propagation.MANDATORY)
public void insertRole(){
Role role = new Role();
role.setId(UUID.randomUUID().toString());
role.setName("管理员");
roleMapper.insert(role);
}
运行结果如下:
程序会抛出org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'的异常信息,数据库这边User信息入库了,但是Role信息没有,因为insertUser方法不存在事务,才导致出现这个异常并且数据成功入库。
3.2.2、加@Transactional注解
仅仅需要在insertUser方法上面加上@Transactional注解。
UserService关键代码:
@Transactional(rollbackFor = Exception.class)
public void insertUser(){
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setUsername("张三");
user.setPassword("123456");
userMapper.insert(user);
roleService.insertRole();
int a = 1/0;
}
RoleService代码不动!
运行结果如下:
虽然程序抛出:java.lang.ArithmeticException: / by zero的异常信息,但是2条数据都进行了回滚,上面的org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'的异常信息也没有了,所以这种事务的传播机制必须需要存在一个活动的事务。
四、REQUIRES_NEW
4.1、基础概念
官方解释:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
如果A、B方法都有事务,但B配置了REQUIRES_NEW,那么B会起一个新的事务,暂停A的事务。等B事务结束,才恢复A的事务。
4.2、代码详解
整体代码逻辑不变。
UserService关键代码:
@Transactional(rollbackFor = Exception.class)
public void insertUser(){
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setUsername("张三");
user.setPassword("123456");
userMapper.insert(user);
roleService.insertRole();
int a = 1/0;
}
RoleService关键代码:
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
public void insertRole(){
Role role = new Role();
role.setId(UUID.randomUUID().toString());
role.setName("管理员");
roleMapper.insert(role);
}
运行结果如下:
虽然程序抛出:java.lang.ArithmeticException: / by zero的异常信息,但是Role信息插入成功了,这是因为insertUser和insertRole方法公用的不是同一个事务,也可以说是公用的不是同一个数据库链接。
五、NOT_SUPPORTED
5.1、基础概念
官方解释:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
如果A方法没有事务(没有加@Transactional注解),B方法配置了NOT_SUPPORTED传播行为,那么B方法也会以非事务方式执行。
如果A方法存在事务(加了@Transactional注解),B方法配置了NOT_SUPPORTED传播行为,那么B方法会挂起自己的事务,以非事务方式加入到A方法的事务来执行。
5.2、代码详解
整体代码逻辑依旧不变。
5.2.1、不加@Transactional注解
在insertUser方法上面不加@Transactional注解。
UserService关键代码:
public void insertUser(){
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setUsername("张三");
user.setPassword("123456");
userMapper.insert(user);
roleService.insertRole();
int a = 1/0;
}
RoleService关键代码:
@Transactional(rollbackFor = Exception.class,propagation = Propagation.NOT_SUPPORTED)
public void insertRole(){
Role role = new Role();
role.setId(UUID.randomUUID().toString());
role.setName("管理员");
roleMapper.insert(role);
}
运行结果如下:
可以很明显的看出,虽然程序中会抛出java.lang.ArithmeticException: / by zero的异常,但2条数据都成功入库了,虽然insertUser方法没有配置事务,但是insertRole方法配置了NOT_SUPPORTED传播行为,那么inserRole方法也会以非事务方式执行。
5.2.2、加@Transactional注解
仅仅需要在insertUser方法上面加上@Transactional注解。
UserService关键代码:
@Transactional(rollbackFor = Exception.class)
public void insertUser(){
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setUsername("张三");
user.setPassword("123456");
userMapper.insert(user);
roleService.insertRole();
int a = 1/0;
}
RoleService代码不动!
运行结果如下:
可以很明显的看出,虽然程序中会抛出java.lang.ArithmeticException: / by zero的异常,但是Role信息成功入库了,因为insertUser方法配置了事务,但是insertRole方法配置了NOT_SUPPORTED传播行为,那么insertRole方法会挂起自己的事务,以非事务方式加入到insertUser方法的事务来执行。
六、NEVER
6.1、基础概念
官方解释:以非事务方式运行,如果当前存在事务,则抛出异常。
如果A方法没有事务(没有加@Transactional注解),B方法配置了NEVER传播行为,那么B方法会正常以非事务方式执行。
如果A方法存在事务(加了@Transactional注解),B方法配置了NEVER传播行为,那么B方法将会抛出异常。
6.2、代码详解
整体代码逻辑依旧不变。
6.2.1、不加@Transactional注解
在insertUser方法上面不加@Transactional注解。
UserService关键代码:
public void insertUser(){
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setUsername("张三");
user.setPassword("123456");
userMapper.insert(user);
roleService.insertRole();
int a = 1/0;
}
RoleService关键代码:
@Transactional(rollbackFor = Exception.class,propagation = Propagation.NEVER)
public void insertRole(){
Role role = new Role();
role.setId(UUID.randomUUID().toString());
role.setName("管理员");
roleMapper.insert(role);
}
运行结果如下:
可以很明显的看出,虽然程序中会抛出java.lang.ArithmeticException: / by zero的异常,但2条数据都成功入库了,这是因为insertUser方法没有配置事务,insertRole方法配置了NOT_SUPPORTED传播行为,那么insertRole方法会以非事务方式执行。
6.2.2、加@Transactional注解
仅仅需要在insertUser方法上面加上@Transactional注解。
UserService关键代码:
@Transactional(rollbackFor = Exception.class)
public void insertUser(){
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setUsername("张三");
user.setPassword("123456");
userMapper.insert(user);
roleService.insertRole();
int a = 1/0;
}
RoleService代码不动!
运行结果如下:
程序会抛出org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never' 的异常信息,同时2条数据都没有成功入库!因为insertUser方法配置了事务,insertRole方法配置了NEVER传播行为,那么insertRole方法抛出了异常信息。
七、NESTED
7.1、基础概念
官方解释:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于REQUIRED
如果A方法没有事务(没有加@Transactional注解),B方法配置了NESTED传播行为,那么B方法将启动一个新的嵌套事务执行。
如果A方法存在事务(加@Transactional注解),B方法配置了NESTED传播行为,那么B方法将会在嵌套事务内执行。
7.2、代码详解
整体代码逻辑依旧不变。
7.2.1、不加@Transactional注解
在insertUser方法上面不加@Transactional注解。
UserService关键代码:
public void insertUser(){
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setUsername("张三");
user.setPassword("123456");
userMapper.insert(user);
roleService.insertRole();
int a = 1/0;
}
RoleService关键代码:
@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
public void insertRole(){
Role role = new Role();
role.setId(UUID.randomUUID().toString());
role.setName("管理员");
roleMapper.insert(role);
}
运行结果如下:
可以很明显的看出,虽然程序中会抛出java.lang.ArithmeticException: / by zero的异常,但是2条数据都成功入库了。因为insertUser方法没有配置事务,insertRole方法配置了NESTED传播行为,那么insertRole方法将启动一个新的嵌套事务执行。
7.2.2、加@Transactional注解
仅仅需要在insertUser方法上面加上@Transactional注解。
UserService关键代码:
@Transactional(rollbackFor = Exception.class)
public void insertUser(){
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setUsername("张三");
user.setPassword("123456");
userMapper.insert(user);
roleService.insertRole();
int a = 1/0;
}
RoleService代码不动!
运行结果如下:
可以很明显的看出,虽然程序中会抛出java.lang.ArithmeticException: / by zero的异常,但是2条数据都进行了回滚。 这是因为insertUser方法配置了事务,insertRole方法配置了NESTED传播行为,那么insertRole方法将会在嵌套事务内执行。
八、总结
以上就是我总结的Spring事务的7种传播机制,都是博主纯手打的,通过理论+实践的方式通俗易懂的讲解每一种事务的传播机制,让大家更好的去理解,如有问题,欢迎评论区讨论!