Spring事务
- 在使用Spring框架时、有两种使用事务的方式、一种是编程式、一种是声明式
- @Transaction注解就是声明式的;
- 事务这个概念是数据库层面的,Spring只是基于数据库中的事务进行了扩展,以及提供了一些能更加方便操作事务的方式。
- 通过在某个方法上增加@Transactional注解,就可以开启事务,这个方法中所有的sql都
会在一个事务中执行,统一成功或失败。
实现原理
- 在一个方法上加了@Transaction注解后,在容器启动时、Spring会基于这个类生成一个代理独享,会将这个代理对象作为bean。
- 当在使用这个代理对象的方法时、如果这个方法上存在@Transaction注解,那么代理逻辑会先把事务的自动提交设置为false,然后再去执行原本的业务逻辑方法,如果执行如果执行业务逻辑方法没有出现异常,那么代理逻辑中就会将事务进行提交,如果执行业务逻辑方法出现了异常,那么则会将事务进行回滚。
- 针对哪些异常回滚事务是可以配置的,可以利用@Transactional注解中的rollbackFor属性进行
配置,默认情况下会对RuntimeException和Error进行回滚 - 通过BeanPostProcessor的postProcessAfterInitialization方法实现AOP,使用AOP实现声明式事务;
隔离级别
- spring事务隔离级别就是数据库的隔离级别:外加一个默认级别
- read uncommitted(未提交读)
- read committed(提交读、不可重复读)
- repeatable read(可重复读)
- serializable(可串行化)
如果数据库的隔离级别和Spring配置的隔离不一致,这时候以Spring配置为准,如果Spring配置的事务隔离级别数据库不支持,则以数据库为准;
传播机制
- 多个事务方法相互调用时,事务如何在这些方法间传播;
方法A是一个事务的方法,方法A执行过程中调用了方法B,那么方法B有无事务以及方法B对事务的要求不同都 会对方法A的事务具体执行造成影响,同时方法A的事务对方法B的事务执行也有影响,这种影响具体是什么就 由两个方法所定义的事务传播类型所决定。
-
REQUIRED: 如果A当前没有事务,则自己B创建一个事务,如果A存在事务,则B加入A事务; -
SUPPORTS:如果A当前有事务,则B加入A事务,如果A当前没有事务,则B以非事务方式执行; -
MANDATORY:如果A当前有事务,则B加入A事务,如果A当前不存在事务,则B抛出异常; -
REQUIRES_NEW:不管A是否存在事务,B创建一个新的事务、然后将A的事务挂起 -
NOT_SUPPORTED:B以非事务方式执行、如果A当前存在事务,则挂起当前A事务; -
NEVER:不使用事务、如果A存在事务,B则抛出异常; -
NESTED:如果A当前事务存在,则嵌套事务执行、如果A事务不存在,则B自己创建一个事务; -
NESTED与REQUIRES_NEW的区别
REQUIRES_NEW是新建一个事务,但是这个事务与原有的事务无关,NESTED则是当前存在事务(父事务)时,开启一个嵌套事务(子事务)。在NESTED父事务回滚时、子事务也会回滚,而REQUIRES_NEW原事务回滚,不会影响新开启的事务;
REQUIRED情况下,调用方存在事务时,则被调用方和调用方使用同一事务,那么被调用方出现异常时,由于 共用一个事务,所以无论调用方是否catch其异常,事务都会回滚 而在NESTED情况下,被调用方发生异常 时,调用方可以catch其异常,这样只有子事务回滚,父事务不受影响
事务失效
spring事务的原理是AOP,进行了切面增强,那么失效的根本原因是这个AOP不起作用了。 常见有:
- 发生自调用,类里面使用this调用本类的方法,此时this对象不是代理类而UserService对象本身;
- 方法不是public的
@Transactional 只能用于 public 的方法上 - 数据库不支持事务
可能使用MyISAM引擎 - 分布式事务
一个事务里,操作不同的数据库,出现异常无法回滚 - 异常被吃掉,事务不会回滚
|