学习笔记

事务的四大特性ACID

  • 原子性Atomic,操作要么全部成功提交,要么全部失败回滚
  • 一致性Consistency,事务必须使数据库从一个一致性状态到达另一个一致性状态
  • 隔离性Isolation,多个用户并发访问数据库时,各个事务互不影响,每个事务感受不到其它事物的存在
  • 持久性Durability,事务对数据库的影响是永久性的

Spring后端层级

  • Controller:负责数据校验等功能
  • Service:负责业务相关的逻辑,其中包括事务
  • DAO:负责数据库持久层操作,如Dao、Mapper等进行数据库操作

事务分类

  • 声明式事务:通过@Transactional注解来声明事务
  • 编程式事务:手动编写代码控制事务的提交和回滚

编程式事务 With AOP

  1. Service层中的方法代码只需要正常处理业务逻辑
  2. 事务相关的开启事务,提交事务,回滚事务写在AOPConfig中
    • 将含有所有操作的方法注册到@Pointcut中,Spring会管理这个方法
    • 使用@Around注解,Spring会在其前后执行任务,代理执行被代理对象方法前开启事务,在代理对象方法结束后提交事务
    • 使用@AfterThrowing注解,其中注册含有所有操作的方法,一旦这个方法出现异常会调用这个注解对应的方法,在这个方法中进行事务回滚
  3. 当使用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:表示当前方法不能运行在一个事务中,否则抛出异常