1. 什么是事务的传播机制
事务的传播机制简单的说就是一个事务方法在调用另一个事务方法的时候,事务是如何在这些方法中进行传播的。比如说方法A调用了方法B,那么方法A有无事务,方法B有无事务以及事务的传播机制是什么都会对执行的结果产生不同的影响。
2. 事务的传播机制都有哪些以及介绍
Spring事务的传播机制总共有其中,由@Transactional注解的propagation属性来设置。
传播机制 | 描述 |
---|---|
Propagation.REQUIRED | 默认的事务传播级别,表示如果当前存在事务则加入该事务,如果当前不存在事务则创建一个新的事务 |
Propagation.SUPPORTS | 支持事务,表示如果当前存在事务则加入该事务,如果当前不存在事务则以非事务的方式运行 |
Propagation.MANDATORY | 强制型,如果当前存在事务则以事务的方式运行,如果不存在事务则报异常 |
Propagation.REQUIRES_NEW | 无论外部调用是否存在事务,都创建一个新的事务 ,如果外部调用存在事务则将事务挂起,创建新的事务,且两个事务之间互不干扰 |
Propagation.NOT_SUPPORTED | 不支持事务,即以非事务的方式运行,如果外部存在事务则将事务挂起 |
Propagation.NEVER | 不能存在事务,以非事务的方式运行,如果外部存在事务,则抛出异常 |
Propagation.NESTED | 嵌套事务,如果当前调用方法存在一个事务,则会创建一个新的事务当作当前事务的嵌套事务来运行,如果当前不存在事务,则该机制与Propagation.REQUIRED机制一样 |
样例:
Propagation.REQUIRED:
1.
// 调用类方法
@Transactional(rollbackFor = ArithmeticException.class)
@Override
public void testTransaction() {
User user = new User();
user.setUsername("mcj");
user.setPassword("123456");
user.setEmail("[email protected]");
userMapper.insertUser(user);
roleService.testTransaction();
//throw new RuntimeException("测试异常");
}
// 被调用类方法
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = ArithmeticException.class)
public void testTransaction() {
Role role = new Role();
role.setRoleName("管理员1");
insertRole(role);
// 这里设置个异常
int i = 1 / 0;
Role role1 = new Role();
role1.setRoleName("管理员2");
insertRole(role1);
}
执行结果:
结果说明:
由上图可以看到role表和user表中都没有增加数据,这是因为Propagation.REQUIRED
机制,在没有事务的情况下会创建一个新的事务,当已存在事务的情况下会加入当前事务,所以这两个方法从根本上是在同一个事务中,当被调用类的testTransaction方法抛出异常的时候事务进行回滚,调用类同样会进行回滚。
2.
// 调用类方法
@Override
public void testTransaction() {
User user = new User();
user.setUsername("mcj");
user.setPassword("123456");
user.setEmail("[email protected]");
userMapper.insertUser(user);
roleService.testTransaction();
//throw new RuntimeException("测试异常");
}
// 被调用类方法
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = ArithmeticException.class)
public void testTransaction() {
Role role = new Role();
role.setRoleName("管理员1");
insertRole(role);
// 这里设置个异常
int i = 1 / 0;
Role role1 = new Role();
role1.setRoleName("管理员2");
insertRole(role1);
}
执行结果:
结果说明:
可以看到user表中新增了一条数据,而role表中则没有进行新增数据,这是因为Propagation.REQUIRED
机制,在调用者方法上没有事务,被调用方法上有事务的时候会创建一个新的事务,当新的事务中发生异常回滚,并不会对调用者方法产生任何影响,所以调用者方法的数据插入成功了,而被调用者方法则没有插入成功。
Propagation.SUPPORTS:
// 调用者方法
@Override
public void testTransaction() {
User user = new User();
user.setUsername("mcj");
user.setPassword("123456");
user.setEmail("[email protected]");
userMapper.insertUser(user);
roleService.testTransaction();
//throw new RuntimeException("测试异常");
}
// 被调用者方法
@Override
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = ArithmeticException.class)
public void testTransaction() {
Role role = new Role();
role.setRoleName("管理员1");
insertRole(role);
// 这里设置个异常
int i = 1 / 0;
Role role1 = new Role();
role1.setRoleName("管理员2");
insertRole(role1);
}
执行结果:
结果说明:
由上图的执行结果可以看出role表中增加了一条数据,user表中同样增加了一条数据。这是因为Propagation.SUPPORTS
机制在调用者方法没有事务的时候被调用者方法也会以非事务的方式运行,在调用者方法有事务的时候被调用者方法则会加入当前事务,所以这里user表的数据与role表的管理员1数据能够插入成功,管理员2的数据则没有插入。
PS:当调用者方法上存在事务时,其执行情况与Propagation.REQUIRED
机制第一种的时候的情况是一样的,这里就不举例说了。
Propagation.MANDATORY:
1.
// 调用者方法
@Transactional(rollbackFor = ArithmeticException.class)
@Override
public void testTransaction() {
User user = new User();
user.setUsername("mcj-MANDATORY");
user.setPassword("123456");
user.setEmail("[email protected]");
userMapper.insertUser(user);
roleService.testTransaction();
//throw new RuntimeException("测试异常");
}
// 被调用者方法
@Override
@Transactional(propagation = Propagation.MANDATORY, rollbackFor = ArithmeticException.class)
public void testTransaction() {
Role role = new Role();
role.setRoleName("管理员1-MANDATORY");
insertRole(role);
//// 这里设置个异常
//int i = 1 / 0;
Role role1 = new Role();
role1.setRoleName("管理员2-MANDATORY");
insertRole(role1);
}
执行结果:
结果说明:
可以看到role表中的两条数据都插入成功了,user表中的一条数据也插入成功了。这是因为Propagation.MANDATORY
机制,当调用者使用事务的时候,被调用者就直接加入当前事务,也就是一个事务要么全部成功,要么全部失败,当你把抛出除0异常的代码注释去掉的时候可以发现这三条数据都会插入不成功。
2
// 调用者类
@Override
public void testTransaction() {
User user = new User();
user.setUsername("mcj-MANDATORY");
user.setPassword("123456");
user.setEmail("[email protected]");
userMapper.insertUser(user);
roleService.testTransaction();
//throw new RuntimeException("测试异常");
}
// 被调用者类
@Override
@Transactional(propagation = Propagation.MANDATORY, rollbackFor = ArithmeticException.class)
public void testTransaction() {
Role role = new Role();
role.setRoleName("管理员1-MANDATORY");
insertRole(role);
//// 这里设置个异常
//int i = 1 / 0;
Role role1 = new Role();
role1.setRoleName("管理员2-MANDATORY");
insertRole(role1);
}
执行结果:
结果说明:
可以看到role表中一条数据都没有插入成功,user表中的数据插入成功了,同时代码还报了了一个异常。这是因为Propagation.MANDATORY
机制是强制性要求事务的,如果没有事务就会报异常,所以role表中两条数据会一条也插入不成功,而user表中的数据插入成功是因为调用者没有开启事务,被调用者抛出异常回滚不会对其产生影响,所以其能插入成功。
Propagation.REQUIRES_NEW:
1.
// 调用者方法
@Transactional(rollbackFor = ArithmeticException.class)
@Override
public void testTransaction() {
User user = new User();
user.setUsername("mcj-REQUIRES_NEW");
user.setPassword("123456");
user.setEmail("[email protected]");
userMapper.insertUser(user);
roleService.testTransaction();
// 这里设置个异常
int i = 1 / 0;
}
// 被调用者方法
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = ArithmeticException.class)
public void testTransaction() {
Role role = new Role();
role.setRoleName("管理员1-REQUIRES_NEW");
insertRole(role);
//// 这里设置个异常
//int i = 1 / 0;
Role role1 = new Role();
role1.setRoleName("管理员2-REQUIRES_NEW");
insertRole(role1);
}
执行结果:
结果说明:
由上面的结果可以看到role表中的两条数据都插入成功了,user表中的数据没有插入成功。这是因为Propagation.REQUIRES_NEW
机制是如果调用者有事务,那么就会将调用者的事务挂起,然后新建了一个事务,所以当调用者的事务发生异常的时候,不会对被调用者的事务产生影响。
2.
// 调用者方法
@Transactional(rollbackFor = ArithmeticException.class)
@Override
public void testTransaction() {
User user = new User();
user.setUsername("mcj-REQUIRES_NEW2");
user.setPassword("123456");
user.setEmail("[email protected]");
userMapper.insertUser(user);
try {
roleService.testTransaction();
} catch (Exception e) {
System.out.println(e);
}
//// 这里设置个异常
//int i = 1 / 0;
}
// 被调用者方法
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = ArithmeticException.class)
public void testTransaction() {
Role role = new Role();
role.setRoleName("管理员1-REQUIRES_NEW2");
insertRole(role);
// 这里设置个异常
int i = 1 / 0;
Role role1 = new Role();
role1.setRoleName("管理员2-REQUIRES_NEW2");
insertRole(role1);
}
执行结果:
结果说明:
由上面的结果可以知道role表中没有插入新的数据,而user表中插入了一条数据。这同样是因为Propagation.REQUIRES_NEW
机制会创建一个新的事务,所以在新的事务中出现异常,只要调用者将异常捕获,并且调用者不会抛出异常,那么调用者的事务就不会发生回滚。因此这边user表能够插入数据成功,而role表插入数据没有成功。
Propagation.NOT_SUPPORTED:
// 调用者方法
@Transactional(rollbackFor = ArithmeticException.class)
@Override
public void testTransaction() {
User user = new User();
user.setUsername("mcj-NOT_SUPPORTED");
user.setPassword("123456");
user.setEmail("[email protected]");
userMapper.insertUser(user);
roleService.testTransaction();
//// 这里设置个异常
//int i = 1 / 0;
}
// 被调用者方法
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED, rollbackFor = ArithmeticException.class)
public void testTransaction() {
Role role = new Role();
role.setRoleName("管理员1-NOT_SUPPORTED");
insertRole(role);
// 这里设置个异常
int i = 1 / 0;
Role role1 = new Role();
role1.setRoleName("管理员2-NOT_SUPPORTED");
insertRole(role1);
}
执行结果:
结果说明:
从上面的结果中可以看出jrole表中管理员1的数据插入成功了,管理员2的数据由于异常没有插入成功,而user表中的数据则没有插入成功。这是因为Propagation.NOT_SUPPORTED
机制,不适用事务,所以虽然这里调用者方法使用的有事务,但是由于被调用者方法不支持使用事务,所以,这里被调用者方法是没有在事务中的,因此管理员1的数据能插入成功,管理员2的数据插入不成功,而调用者方法由于异常发生回滚,所以user表的数据也是没有插入的。
Propagation.NEVER:
// 调用者方法
@Transactional(rollbackFor = ArithmeticException.class)
@Override
public void testTransaction() {
User user = new User();
user.setUsername("mcj-NEVER");
user.setPassword("123456");
user.setEmail("[email protected]");
userMapper.insertUser(user);
roleService.testTransaction();
//// 这里设置个异常
//int i = 1 / 0;
}
// 被调用者方法
@Override
@Transactional(propagation = Propagation.NEVER, rollbackFor = ArithmeticException.class)
public void testTransaction() {
Role role = new Role();
role.setRoleName("管理员1-NEVER");
insertRole(role);
//// 这里设置个异常
//int i = 1 / 0;
Role role1 = new Role();
role1.setRoleName("管理员2-NEVER");
insertRole(role1);
}
执行结果:
结果说明:
从上面的结果可以出role表和user表数据都没有插入成功,并且接口报了一个异常。这是因为被调用者方法使用的是Propagation.NEVER
机制,这个机制是不允许使用事务的,这里调用者方法使用了事务,所以这里才会报一个异常。
Propagation.NESTED:
1.调用者方法中出现异常
// 调用者方法
@Transactional(rollbackFor = ArithmeticException.class)
@Override
public void testTransaction() {
User user = new User();
user.setUsername("mcj1-NESTED");
user.setPassword("123456");
user.setEmail("[email protected]");
userMapper.insertUser(user);
roleService.testTransaction();
// 这里设置个异常
int i = 1 / 0;
}
// 被调用者方法
@Override
@Transactional(propagation = Propagation.NESTED, rollbackFor = ArithmeticException.class)
public void testTransaction() {
Role role = new Role();
role.setRoleName("管理员1-NESTED");
insertRole(role);
//// 这里设置个异常
//int i = 1 / 0;
Role role1 = new Role();
role1.setRoleName("管理员2-NESTED");
insertRole(role1);
}
执行结果:
结果说明:
这里可以看到两个表中都没有插入成功,这是因为Propagation.NESTED
机制时嵌套事务,当调用者方法的父事务出现异常时,那么其子事务也会被回滚。
2.子事务中出现异常,并且父事务中没有捕获异常
// 调用者方法
@Transactional(rollbackFor = ArithmeticException.class)
@Override
public void testTransaction() {
User user = new User();
user.setUsername("mcj1-NESTED");
user.setPassword("123456");
user.setEmail("[email protected]");
userMapper.insertUser(user);
roleService.testTransaction();
//// 这里设置个异常
//int i = 1 / 0;
}
// 被调用者方法
@Override
@Transactional(propagation = Propagation.NESTED, rollbackFor = ArithmeticException.class)
public void testTransaction() {
Role role = new Role();
role.setRoleName("管理员1-NESTED");
insertRole(role);
// 这里设置个异常
int i = 1 / 0;
Role role1 = new Role();
role1.setRoleName("管理员2-NESTED");
insertRole(role1);
}
执行结果:
结果说明:
由上面的结果可以看出两张表中数据都没有插入成功,因此当嵌套事务中的子事务中出现异常,而父事务并没有对异常进行捕获时,那么父子事务都会进行回滚。
3.子事务中出现异常,父事务中捕获异常
// 调用者方法
@Transactional(rollbackFor = ArithmeticException.class)
@Override
public void testTransaction() {
User user = new User();
user.setUsername("mcj1-NESTED");
user.setPassword("123456");
user.setEmail("[email protected]");
userMapper.insertUser(user);
try {
roleService.testTransaction();
} catch (ArithmeticException e) {
}
//// 这里设置个异常
//int i = 1 / 0;
}
// 被调用者方法
@Override
@Transactional(propagation = Propagation.NESTED, rollbackFor = ArithmeticException.class)
public void testTransaction() {
Role role = new Role();
role.setRoleName("管理员1-NESTED");
insertRole(role);
// 这里设置个异常
int i = 1 / 0;
Role role1 = new Role();
role1.setRoleName("管理员2-NESTED");
insertRole(role1);
}
执行结果:
结果说明:
由上面的结果可以看出,user表中的数据添加成功,而role表中的数据并没有成功。因此当嵌套事务中的子事务中出现异常,而父事务对异常进行捕获,那么父子事务不会进行回滚。
3. 总结
Propagation.REQUIRED
机制是事务传播机制的默认机制,当事务没有设置传播机制的时候默认就是该机制- 这七种事务传播机制中最常用的是
Propagation.REQUIRED
和Propagation.REQUIRES_NEW
这两种 Propagation.NESTED
和Propagation.REQUIRES_NEW
这两种机制在被调用方法抛出异常时他们的结果一样,但是当调用者的方法抛出异常时他们的结果就不一样了。Propagation.NESTED
机制的特性是:当调用者方法事务回滚时,被调用者方法事务也跟着回滚。当被调用者方法事务发送回滚时,调用者方法事务是否回滚取决于是否捕获异常。如果捕获了异常,那么就不回滚,否则回滚