Spring事务的原理:
Spring中对事务支持实际上就是数据库对事务的支持,它依赖于数据库对事务性,只是利用Spring中AOP进行了事务的封装,我们传统的JDBC如果要开启事务一般如下:
- 加载驱动
- 获取连接对象Connection con = DriverManager.getConnection()
- con.setAutoCommit(false);
- 预处理sql语句preparestatment
- 设置占位符
- 执行sql语句并得到查询或者更新、结果
- 提交事务/回滚事务 con.commit() / con.rollback();
- 关闭连接(释放资源)
Spring其实就是通过配置,生成代理对象,动态的为我们执行了第3、7步
Spring中事务的使用:
1.SpringBoot中使用事务非常方便,直接在启动类上添加注解@EnableTransactionManagement,在需要事务管理的方法上添加@Transactional即可
2.传统的SSM中则需要配置:
xml文件中:
<!--开启注解的方式-->
<tx:annotation-driven transaction-manager="transactioManager" />
方法上使用@Transactional注解:
@Transactional(isolation=Isolation.DEFAULT)
public void fun(){
dao.add();
dao.udpate();
}
Spring支持的事务隔离等级
注解@Transactional里的isolation属性为设置的事务隔离级别,Spring中支持五种配置:
- TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
- TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
- TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
第一种配置表示使用和数据库一致的事务隔离级别,其他四种则分别对应了数据库中四种隔离级别:读未提交、读已提交、可重复读、串行化;
Spring中的传播行为:
Spring中规定了一些在嵌套事务的场景的配置,一共有7种:
- TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
- TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED
但一般我们只使用前两个,假设外层事务 Service A 的 Method A() 调用 内层Service B 的 Method B()
- 假设ServiceB.methodB() 的事务级别定义为 PROPAGATION_REQUIRED,那么执行 ServiceA.methodA() 的时候spring已经起了事务,这时调用 ServiceB.methodB(),ServiceB.methodB() 看到自己已经运行在 ServiceA.methodA() 的事务内部,就不再起新的事务。
假如 ServiceB.methodB() 运行的时候发现自己没有在事务中,他就会为自己分配一个事务。
这样,在 ServiceA.methodA() 或者在 ServiceB.methodB() 内的任何地方出现异常,事务都会被回滚。 - 假设ServiceB.methodB() 的事务级别为 PROPAGATION_SUPPORTS,那么当执行到ServiceB.methodB()时,如果发现ServiceA.methodA()已经开启了一个事务,则加入当前的事务,如果发现ServiceA.methodA()没有开启事务,则自己也不开启事务。这种时候,内部方法的事务性完全依赖于最外层的事务。