传播行为:当内部事务方法被当前事务方法(外部事务)调用时,必须指定内部事务应该如何传播。例如:方法可能继续在外部事务中运行,也可能开启一个新事务,并在自己的事务中运行。
关于事务回滚机制,我个人认为主要是看异常发生点的位置,异常发生时,还未提交的事务都会被回滚,还未运行的事务也不会被执行;而已经提交了的则不会被回滚。
事务传播行为定义内部事务如何在外部事务中运行。
注意,在同一个Service中的方法互相调用时不存在事务传播,事务特性以当前事务为准(Spring同一个Bean中方法的互相调用中被调用方法的注解会失效)。只有在两个不同的Service之间调用才会有事务的传播特性。
Spring基于注解@Transactional的属性propagation属性有以下七种:
REQUIRED默认的传播行为,如果外部事务存在,则内部事务将会在该事务中运行;否则,会启动一个新的事务。
内部方法必须在事务中运行。
REQUIRES_NEW不论外部事务是否存在,内部事务总会开启新的事务,如果有外部事务,则将其挂起,当内部事务完成后再继续外部事务。
内部方法必须在“新事务”中运行。
SUPPORTS如果存在一个外部事务,则内部方法在外部事务中运行。如果没有外部事务,则以非事务的执行。
内部方法不必保证一定要在事务中运行。
NOT_SUPPORTED内部方法总是非事务地执行,并挂起任何存在的外部事务。不支持事务
MANDATORY如果已经存在一个外部事务,则内部方法在外事务中运行。如果外部事务,则抛出异常。
内部方法必须在外部事务中运行,否则抛出异常。强制事务
NEVER总是非事务地执行,如果存在一个活动的外部事务,则抛出异常。
必须在非事务中运行,否则抛出异常。强制非事务
NESTED如果当前已经存在一个外部事务,那么该方法将会以嵌套子事务的方式在外部事务中运行。
子事务回滚savepoint非事务回滚,在主事务完成后才会提交或者回滚。
示例:测试代码,内部事务插入角色,外部事务插入用户。
//测试//正常操作会在数据库两个表中各插入一条记录@SpringBootTest@ComponentScanclass DemoApplicationTests { @Autowired UserService userService = null; @Test void contextLoads() { User user = new User(); user.setName("张三"); userService.addUser(user); }} @Servicepublic class RoleServiceImpl implements RoleService { @Autowired RoleDao roleDao = null; //内部事务 @Transactional(propagation = Propagation.REQUIRED) @Override public void addRole(Role role) { roleDao.addRole(role); throw new RuntimeException("内部事务异常"); }} @Service public class UserServiceImpl implements UserService { @Autowired UserDao userDao = null; @Autowired RoleService roleService = null; //外部事务 @Transactional @Override public void addUser(User user) { userDao.addUser(user); Role role = new Role(); role.setRname("人事部"); roleService.addRole(role);//调用RoleService的方法 throw new RuntimeException("外部事务异常"); } }
一、REQUIRED
内部事务传播特性为REQUIRED时:在外部事务中运行或者开启新事务。
外部事务存在时:当外部方法或者内部方法发生异常时,因为内部事务在外部事务中运行,所以两条插入操作都会回滚,数据不会被持久化操作,因为他们是同一条事务。
外部事务不存在时:外部方法异常时,对两条操作都无影响,外部方法是在无事务中运行的;而内部方法是在内部事务中运行的,内部方法异常时,内部方法是在事务中运行,因此会回滚,而外部方法是无事务的,所以不会影响插入。二:REQUIRES_NEW
内部事务传播特性为REQUIRES_NEW时:不论是否存在外部事务,都会开启新事务。
外部事务存在时:当外部方法产生异常时,外部事务回滚,内部事务正常提交;当内部方法产生异常时,内部事务回滚,同时异常抛至外部,因此也会回滚外部事务。他们是两条事务,内部事务运行时,外部事务是被挂起的,当内部事务完成后继续外部事务。
外部事务不存在时:外部方法异常时,对两条操作都无影响,外部方法是在无事务中运行的;而内部方法是在内部事务中运行的,内部方法异常时,内部方法是在事务中运行,因此会回滚,而外部方法是无事务的,所以不会影响插入。三:SUPPORTS
内部事务传播特性为SUPPORTS时:如果存在一个外部事务,则内部方法在外部事务中运行。如果没有外部事务,则以非事务的执行。
外部事务存在时:当外部方法或者内部方法发生异常时,因为内部事务在外部事务中运行,所以两条插入操作都会回滚,数据不会被持久化操作,因为他们是同一条事务。
外部事务不存在时:内外方法均在无事务中运行。四、NOT_SUPPORTED
内部事务传播特性为NOT_SUPPORTED时:内部方法总是非事务地执行,并挂起任何存在的外部事务。
外部事务存在时:当外部方法异常时,回滚外部事务,而内部是非事务的,因此对内部事务不影响;当内部方法异常时,内部是非事务的,因此不会回滚内部事务,异常会抛至外部,回滚外部事务。
外部事务不存在时:内外方法均在无事务中运行。五、MANDATORY
内部事务传播特性为MANDATORY时:如果已经存在一个外部事务,则在外部事务中运行。如果没有外部事务,则抛出异常(强制事务)。
外部事务存在时:当外部方法或者内部方法发生异常时,因为内部事务在外部事务中运行,所以两条插入操作都会回滚,数据不会被持久化操作,因为他们是同一条事务。
外部事务不存在时:异常:IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory' and 外部事务会正常提交。
六、NEVER
内部事务传播特性为NEVER时:总是非事务地执行,如果存在一个活动的外部事务,则抛出异常(强制非事务)。
外部事务存在时:异常:IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never' 内外事务均不会提交。
外部事务不存在时:内外方法均在无事务中运行。七、NESTED
内部事务传播特性为NESTED时:内部开启一个可嵌套的事务,它是已经存的外部事务的一个真正的子事务。
潜套事务开始执行时, 它将取得一个 savepoint,如果这个嵌套事务失败,我们将回滚到此 savepoint(非事务回滚),潜套事务是外部事务的一部分,只有外部事务结束后它才会被提交或回滚。
外部事务存在时:内部方法异常会回滚至savepoint,同时异常抛至外部,然后才是主事务的rollback;外部方法异常时,会回滚主事务(包括子事务的事务回滚)。外部事务不存在时:相当于REQUIRES_NEW,内部为一个新事务。关于REQUIRES_NEW和NESTED的区别
NESTED子事务的回滚只是回滚至保存点,而非事务回滚,在主事务完成后才会被提交或者回滚。