目录
1.Spring数据库事务管理器的设计
2.声明式事务
3.数据库的相关知识
4.选择隔离级别和传播行为
1.Spring数据库事务管理器的设计
在Spring中数据库事务是通过PlatformTransactionManager进行管理的
1.1 TransactionTemplate源码
- 事务的创建、提交和回滚是听过PlatformTransactionManager接口来实现的
- 当事务产生异常时会回滚事务
- 当无异常时,会提交事务
1.2 事务管理器的实现
常用的是DataSourceTransactionManager,继承了AbstractPlatformTransactionManager,又实现了PlatformTransactionManager(该接口方法有:获取事务状态、提交事务、回滚事务)
1.3 配置事务管理器
<!--配置数据源-->
<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${db.driverClassName}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</bean>
<!--配置数据源事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2.声明式事务
声明式事务的流程
代码例子
@Service(value = "roleService")
public class RoleServiceImpl implements RoleService {
@Autowired
private RoleMapper roleMapper;
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 3)
public int insertRole(Role role) {
return roleMapper.insertRole(role);
}
}
当roleMapper的insert方法抛出异常时,Spring就会回滚事务,如果成功,就提交事务
3.数据库的相关知识
3.1 数据库事务ACID特性
数据库事务正确执行的4个基础要素是:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)
- 原子性:整个事务中的所有操作,要么全部成功,要么全部不完成
- 一致性:指一个事务可以改变封装状态。事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。
- 隔离性:指两个事务之间的隔离程度
- 持久性:在事务完成以后,该事务对数据库所做的更改便持久保存在数据库之中,并不会被回滚
3.2 丢失更新
- 第一类丢失更新
两个事务并发,一个回滚、一个提交成功导致不一致
事务回滚覆盖另外一个事务的更新 - 第二类丢失更新
不同的事务中,两个事务无法探知对方的事务操作,导致不一致
事务提交覆盖另外一个事务的更新
3.3 隔离级别
隔离级别可以在不同程度上减少丢失更新
隔离级别定义为4层,分别是:脏读(dirty read)、读/写提交(read commit)、可重复读(repeatable read)和序列化(Serializable)
- 脏读: 事务一可以读取事务二未提交的事务
- 读/写提交: 一个事务只能读取另一个事务已经提交的数据(消除了脏读带来的问题,带来了不可重复读问题)
- 可重复读: 在同一个事务内的查询都是事务开始时刻一致的,针对数据库同一条记录而言。可重复读会使得同一条数据库记录的读/写按照一个序列化进行操作,不会产生交叉情况,但是如果数据库同时对多条记录进行读/写,这个时候会带来幻读问题
- 序列化: 一种让SQL按照顺序读/写的方式,消除数据库事务之间并发产生数据不一致的问题。
各类隔离级别和产生的现象
隔离级别 | 脏读 | 不可重读 | 幻读 |
---|---|---|---|
脏读 | √ | √ | √ |
读/写提交 | × | √ | √ |
可重复读 | × | × | √ |
序列化 | × | × | × |
4.选择隔离级别和传播行为
4.1 选择隔离级别
在互联网应用中,不但要考虑数据库数据的一致性,而且要考虑系统的性能。
从脏读到序列化,系统性能直线下降。
大部分场景下,企业会选择读/写提交的方式设置事务。(有助于提高并发,又压制了脏读,但对于数据一致性问题没有解决)
使用读/写提交隔离级别:
@Autowired
private RoleMapper roleMapper;
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, timeout = 3)
public int insertRole(Role role) {
return roleMapper.insertRole(role);
}
@Transactional隔离级别默认值是:Isolation.DEFAULT
- MySQL支持4种隔离级别,默认的是可重复读
- Oracle只能支持读/写提交和序列化隔离级别,默认的是读/写提交
4.2 传播行为
传播行为是指方法之间的调用事务策略的问题。
Spring的7种传播行为
传播行为 | 含义 | 备注 |
---|---|---|
REQUIRED | 当方法调用时,如果不存在当前事务,那么就创建事务;如果之前的方法已经存在事务了,就沿用之前的事务 | Spring默认传播行为 |
SUPPORTS | 当方法调用时,如果不存在当前事务,那么就不启用事务;如果存在当前事务,那么就沿用当前事务 | – |
MANDATORY | 方法必须在事务内运行 | 如果不存在当前事务,那么就抛出异常 |
REQUIRES_NEW | 无论是否存在当前事务,方法都会在新的事务中运行 | 事务管理器会打开新的事务运行该方法 |
NOT_SUPPORTED | 不支持事务,如果不存在当前事务也不会创建事务;如果存在当前事务,则挂起它,直至该方法结束后才恢复当前事务 | 适用于那些不需要事务的SQL |
NEVER | 不支持事务,只有在没有事务的环境中才能运行它 | 如果方法存在当前事务,则抛出异常 |
NESTED | 嵌套事务,调用方法如果抛出异常只回滚自己内部执行的SQL,而不回滚主方法的SQL | 如果当前数据库支持保存点,那么就在当前事务上使用保存点技术;如果发生异常则将方法内执行的SQL回滚到保存点,而不是全部回滚 |