事务传播机制
概述
- 事务传播行为:由一个事务传播行为修饰的方法被嵌套进另一个方法时事务如何传播
- spring在TransactionDefintion规定了7中事务传播行为
- spring框架特有的事务增强行为
- Spring事务 的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的
- 真正的数据库层的事务提交和回滚是通过bin log或者redo log实现的
获取连接 Connection con = DriverManager.getConnection()
开启事务con.setAutoCommit(true/false);
执行CRUD
提交事务/回滚事务 con.commit() / con.rollback();
关闭连接 conn.close();
七种事务传播行为
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。共享同一个connect,jdbc的connect.commit、connect.rollback,一起提交,一起回滚 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。内部方法的事务性完全依赖于最外层的事务 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | (子事务)如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
外围方法未开启事务
- (非同一类)内部方法开启事务使用
Propagation.REQUIRED
则内部方法会开启新的单独事务,且互不影响 - 在外围方法未开启事务的情况下
Propagation.NESTED
和Propagation.REQUIRED
作用相同,修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰
外围方法开启事务
- 内部方法加入外围事务,内部方法抛出异常回滚,即使方法被catch不被外围方法感知,整个事务依然回滚。
- 在外围方法开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法依然会单独开启独立事务,且与外部方法事务也独立,内部方法之间、内部方法和外部方法事务均相互独立,互不干扰
- 外围方法开启事务,内部事务为外围事务的子事务,外围方法回滚,内部方法也要回滚
- 外围方法开启事务,内部事务为外围事务的子事务,内部方法抛出异常回滚,且外围方法感知异常致使整体事务回滚。
- 外围方法开启事务,内部事务为外围事务的子事务,插入“李四”内部方法抛出异常,可以单独对子事务回滚。
- 证明在外围方法开启事务的情况下Propagation.NESTED修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务
REQUIRED,REQUIRES_NEW,NESTED异同
- NESTED和REQUIRED修饰的内部方法都属于外围方法事务,如果外围方法抛出异常,这两种方法的事务都会被回滚。但是REQUIRED是加入外围方法事务,所以和外围事务同属于一个事务,一旦REQUIRED事务抛出异常被回滚,外围方法事务也将被回滚。而NESTED是外围方法的子事务,有单独的保存点,所以NESTED方法抛出异常被回滚,不会影响到外围方法的事务。
- NESTED和REQUIRES_NEW都可以做到内部方法事务回滚而不影响外围方法事务。但是因为NESTED是嵌套事务,所以外围方法回滚之后,作为外围方法事务的子事务也会被回滚。而REQUIRES_NEW是通过开启新的事务实现的,内部事务和外围事务是两个事务,外围事务回滚不会影响内部事务。
事务失效的情况
待补充
总结
场景使用
- 对于非严格要求记录的信息,例如普通日志记录,可以使用
Propagation.NOT_SUPPORTED
修饰,此方法抛出异常也不影响其他事务 - unchecked (that is, it extends the
java.lang.RuntimeException
class) 官方解释未捕获异常定义 - spring默认只对RuntimeException和Error做捕捉,并回滚,其他的异常,直接提交
- 典型错误:如果serviceA的methodA调用serviceB的methodB方法,serviceB中直接trycatch了DAO操作的异常同时没有抛出,那么serviceB将被认为事务中没有异常发生,间接导致methodA事务正常提交,导致脏数据产生
注意:考虑到spring的事务传播机制,我们再对代码进行try catch时,应该多一些思考,事务传播往往和异常有着千丝万缕的关系。check exception不用多说了,主要是对于runtimeException,一定要慎重。 比如,在最内层catch异常,会对整个事务的原子性造成污染,即try的DAO可能执行失败了,但是外层方法的事务还是提交了。 当存在需要单独开辟事务的场景时,需要考虑其他的事务传播属性