学习笔记
事务的四大特性ACID
- 原子性Atomic,操作要么全部成功提交,要么全部失败回滚
- 一致性Consistency,事务必须使数据库从一个一致性状态到达另一个一致性状态
- 隔离性Isolation,多个用户并发访问数据库时,各个事务互不影响,每个事务感受不到其它事物的存在
- 持久性Durability,事务对数据库的影响是永久性的
Spring后端层级
- Controller:负责数据校验等功能
- Service:负责业务相关的逻辑,其中包括事务
- DAO:负责数据库持久层操作,如Dao、Mapper等进行数据库操作
事务分类
- 声明式事务:通过
@Transactional
注解来声明事务 - 编程式事务:手动编写代码控制事务的提交和回滚
编程式事务 With AOP
- Service层中的方法代码只需要正常处理业务逻辑
- 事务相关的开启事务,提交事务,回滚事务写在AOPConfig中
- 将含有所有操作的方法注册到
@Pointcut
中,Spring会管理这个方法 - 使用
@Around
注解,Spring会在其前后执行任务,代理执行被代理对象方法前开启事务,在代理对象方法结束后提交事务 - 使用
@AfterThrowing
注解,其中注册含有所有操作的方法,一旦这个方法出现异常会调用这个注解对应的方法,在这个方法中进行事务回滚
- 将含有所有操作的方法注册到
- 当使用AOP时,不同于单纯使用JDBC和Mybatis
- 单纯使用JDBC和Mybatis时手动控制事务,使用了try/catch/finally代码块其中分别存放了开启事务、提交事务、回滚事务、释放资源
- 而AOP使用了
@AfterThrowing
注解来声明了专门处理异常的方法,如果自行try/catch/finally的话,异常被用户自行解决了,没有办法被Spring AOP捕获
声明式事务
在含有全部操作的方法上写一个注解@Transactional
注解,Spring会将整个方法识别为一个事务,进行管理
@Transactional
注解的位置
- 在方法上,当前方法为声明式事务
- 在类上,当前类的所有方法均为声明式事务
- 在父类上,只有执行父类方法时才为声明式事务
操作数据的问题
- 脏读:A事务读到B事务未提交的数据,此时B事务发生错误并回滚,导致读取数据前后不一致
- 不可重复读:A事务避免大,其内部几次相同数据库操作前后读取到B事务提交更新或删除的某几行数据,导致几次读取数据前后不一样,数据库系统由于时间差同样的数据库操作无法重新读到相同的内容
- 幻读:A事务读取前后B事务插入了新数据,导致读取前后的总记录数不一致,常见于COUNT()
隔离机制
- ISOLATION_READ_UNCOMMITTED:允许读取未提交的数据,可能导致脏读、不可重复读、幻读
- ISOLATION_READ_COMMITTED:允许在一个事务中读取另一个已经提交的事务中的数据。可以避免脏读,但无法避免不可重复读和幻读
- ISOLATION_REPEATABLE_READ:一个事务不可能更新由另一个事务修改但尚未提交的数据,可以避免脏读和不可重复读,但无法避免幻读
- ISOLATION_SERIALIZABLE:所有的事务都在一个执行队列中,依次顺序执行,并不是并行。可以避免脏读、不可重复读、幻读,但是这种隔离级别效率很低
传播行为
传播行为一般用于事务嵌套的场景,如当一个事务方法内部调用了另外一个事务方法
- PROPAGATION_REQUIRED(默认):当前方法必须运行在事务当中,如果当前存在一个事务,就在这个事务中运行,如果没有则创建一个新事务
- PROPAGATION_NOT_SUPPORTED:当前方法不能运行在事务当中,如果当前存在一个事务,则挂起这个事务,待当前方法执行完成后,后到之前挂起的事务中
- PROPAGATION_REQUIRES_NEW:当前方法必须运行在自己的事务中,如果当前已经有一个事务,则挂起已有事务,创建自己的事务并运行
- PROPAGATION_SUPPORTS:当前方法不需要运行在事务中,但是如果当前有一个事务,则运行在该事务中
- PROPAGATION_MANDATORY:当前方法必须运行在事务当中,如果没有,则抛出异常
- PROPAGATION_NESTED:当前事务存在,则该方法应运行在一个嵌套事务中
- PROPAGATION_NEVER:表示当前方法不能运行在一个事务中,否则抛出异常