spring的4种事务特性,5种隔离级别,7种传播行为
什么是事务?
事务(Transaction) 是访问并可能更新数据库中各种数据项的一个程序执行单元。
事务是恢复和并发控制的基本单元,有以下4个属性(ACID):
- 原子性:一个事务是一个不可分割的工作单位,事务中包括的诸多操作要么都做,要么都不做;
- 一致性:事务必须是使数据库从一个一致性状态变到另一个一致性状态。事务的一致性和原子性是密切相关的。
- 隔离性:一个事务的执行不能被其他事务干扰。
- 持久性:持久性也称为永久性,指一个事务一旦提交,它的改变就是永久性的。
Spring事物的本质其实就是数据库对事物的支持,没有数据库的事物支持,spring是无法提供事务功能的,对于纯JDBC操作数据库,想要用到事务,可以按照以下步骤:
- 获取连接 Connection con = DriverManager.getConnection();
- 开启事务 con.setAutoCommit(true/false);
- 执行crud操作;
- 提交事务/回滚事务 con.commit() / con.rollback();
- 关闭连接 con.close();
使用spring的事物管理功能之后,我们可以不用再写步骤2和4的代码,而是有spring自动完成。下面以注解方式为例:
- 配置文件开启注解驱动,在相关的类和方法上通过注解@Transactional标识;
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
@TargetDataSource(dataSource = TargetDataSource.DataSourceEnum.write)
@Transactional(rollbackFor = Exception.class)
public void updateRoleFunctionByRoleId(MaintenanceUser maintenanceUser, Long roleId, String allowFuncs) {
// 业务逻辑代码
}
- spring在启动的时候会去解析生成相关的bean,这时候会查看拥有相关注解的类和方法,并且为这些类和方法生成代理,并根据@Transactional的相关参数进行相关的配置注入,这样就在代理中为我们把相关的事务处理掉了(开启正常提交事务,异常回滚事务);
- 真正的数据库层的事务提交和回滚是通过bin log或者redo log实现的。
Spring事务的传播属性:
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。这些属性在TransactionDefinition中定义:Spring事务传播属性和隔离级别。
spring事务有7种传播行为,分别是:
1、PROPAGATION.REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
2、PROPAGATION.SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
3、PROPAGATION.MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
4、PROPAGATION.REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
5、PROPAGATION.NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6、PROPAGATION.NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
7、PROPAGATION.NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
注解@Transactional默认的传播行为是:PROPAGATION.REQUIRED
注意事项:
1、在需要事务管理的地方加@Transactional 注解。@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。
2、@Transactional 注解只能应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。
3、注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据。必须在配置文件中使用配置元素,才真正开启了事务行为。
4、Spring团队建议在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。在接口上使用 @Transactional 注解,只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承 的,这就意味着如果正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装。
数据库隔离级别 : 事务的四种隔离级别
临时表:针对每张表一一对应的表,单例的,只要一张表;判断这条数据有没有被锁定,有没有正在被操作,只要看看临时表里面有没有这条记录。数据被修改了,没有被提交,只是存在于临时表中,处于临时状态。有可能会被撤回,被回滚。当用户去查询表时,临时表里面有这条数据,数据会被优先访问。如果前一个事务发生回滚,则会导致用户读取的数据和我们数据库持久化的数据是不一致的,这种情况就叫脏读。临时表在事务commit()或者rollback()以后就会消失。
- 脏读:一个事务对数据进行了增删改,但是还未提交(数据在我们的临时表里面);另一个事务可以读取到未提交的数据(读取临时表中的数据),如果第一个事务这时候回滚了,那么第二个事务就读到了脏数据。
- 不可重复读:一个事务中发生了2次读操作,第一次读操作(临时表)和第二次读操作之间,另一个事务对数据进行了修改(临时表),这时候2次读取的数据是不一致的。
- 幻读:第一个事务对一定范围的数据进行批量修改,第二个事务在这个范围增加一条数据,这个时候第一个事务就会丢失对新增数据的修改。
总结:
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大;
大多数的数据库默认隔离级别为Read Commited,比如SqlServer,Oracle;
少数数据库默认隔离级别为:Repeatable Read ,比如:MySQL
Spring事务接口管理
总结:
-
什么是事物?
一个整体的执行逻辑单元,只有两个结果,要么全失败,要么全成功。 -
事务的特性
原子性,隔离性,持久性,一致性。 -
事务的基本原理
从数据库角度来说:就是提供了一种后悔机制。这里用到了临时表,在执行增删改之前,先将满足条件的数据查询出来放入到临时表中,然后先在临时表中操作这些数据,完成过程中如果没有出现任何问题,就将临时表中的数据同步到实际的表中,并返回影响的行数。
如果在临时表中操作数据时出现了错误,那就将临时表中满足条件的数据清除,并返回错误码。
如果想要对一个数据表的数据进行清空,千万别用delete from ,这种情况会锁表;加了where条件就是行锁。 -
spring的事务配置
AOP配置:指定需要加事务的方法;
声明试事务配置,事务的传播属性,隔离级别,回滚条件等;
传播属性:DEFAULT REQUIRED EQUIRES_NEW SUPPORTS NESTED
隔离级别:DEFAULT READ_UNCOMMITTED READ_COMMITTED REPEATABLE_READ SERIALIZABLE -
源码分析
通过解析配置文件,得到TransactionDefintion 实际上就是AOP中的MethodInterceptor(方法代理),然后可以在满足条件的方法调用前后加一些东西;PlatformTransactionManager 中的方法:
getTransaction 调用了TransactionSynchronizationManager类的getResource();从ThreadLocal里面取值, Map<DataSource,CommectionHolder> 获取一个连接对象Connection;conn.setAutoCommit(false);
Commit con.commit();
Rollback con.rollback();