1、聊一下Spring事务

事务有四大特性,原子性、一致性、隔离性和持久性。

  • 原子性:事务内所有操作要么同时成功,要么同时失败;
  • 一致性:事务的执行必须使数据库从一个一致性状态转换到另一个一致性状态。例如A给B转账50,事务提交后,A账户减少50元,B账户增加50元,A和B的账户总额没有改变。
  • 隔离性:多个事务之间的操作互相隔离,互不影响。
  • 持久性:事务一旦提交,对数据的变更就是永久性的,即使数据

Spring事务使用分为编程式事务和声明式事务,编程式事务像MySQL的事务一样,显式的通过Start和commit开始和提交,不常用,我们常用的是声明式事务,也就是在方法上加一个@Transactional 来使用。
可以通过propagation配置事务的传播机制,默认是REQUIRED,也是我们最常用的。

  • REQUIRED(必须):。如果当前没有事务,就新建一个;如果已经存在一个事务,就加入其中。一起进行回滚
  • SUPPORTS(支持):如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。有事务一起进行回滚
  • MANDATORY(强制):如果当前存在事务,则加⼊该事务;如果当前没有事务,则抛出异常。一起进行回滚
  • REQUIRES_NEW(必须新的):一定会创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。分为两个事务,各自管理回滚。
  • NOT_SUPPORTED(不支持):以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。无回滚概念。
  • NEVER(从不):以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。无回滚概念。
  • NESTED(嵌套):如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏;如果当前没有事务,则该取值等价于 REQUIRED。事务2报错,只回滚事务2,事务1报错,两个事务一起回滚。事务2就是在开始时加了一个保存点,回滚也是回滚到保存点。

可以通过isolation配置事务的隔离级别,默认DEFAULT,使用数据库的默认隔离级别。另外还有READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE四种。

  • READ_UNCOMMITTED(读未提交):会有脏读、重复读、幻读问题。
  • READ_COMMITTED(读已提交):有重复读、幻读问题。
  • REPEATABLE_READ(可重复读):幻读问题。
  • SERIALIZABLE(串行化):单线程,无问题,就是慢。

另外Spring事务默认遇到RuntimeException、Error及其子类的异常时才会回滚,但是遇到普通的Exception时会提交事务,例如自定义的BussesException继承自Exception,当抛出这个BussesException时,Spring事务不会回滚。因此可以通过rollbackFor来配置需要回滚的异常,与之对应的还有noRollbackFor属性,配置不需要回滚的异常,这两个属性配置的是Class对象,另外还有rollbackForClassName和noRollbackForClassName,通过品类全路径名配置。另外需要注意的是,优先级noRollbackFor > rollbackFor > 默认的。

Spring事务的本质就是AOP,当我们使用@Transactional注解一个方法时,Spring的运行时会为这个类创建一个代理对象,我们通过@Resource或者@Autowired或者其他方式注入这个类的对象也是一个代理对象。在方法执行前,代理对象根据注解信息,通过事务管理器PlatformTransactionManager开启一个事务,然后才会去执行实际的业务方法。业务方法如果成功完成,代理对象就会提交事务;业务方法如果抛出了RuntimeException、Error及其子类的异常或者rollbackFor指定的异常,代理就会回滚事务。
PlatformTransactionManager本身就只有三个方法

  • getTransaction:关闭自动提交并开启事务
  • commit:提交事务
  • rollback:回滚事务

2、Spring事务什么场景下会失效

  1. 抛出了RuntimeException、Error及其子类 和 rollbackFor指定的异常之外的异常,可以通过rollbackFor或者noRollbackForClassName来解决。
  2. AOP只能代理public修改的方法,因此当方法修饰符非public,导致事务失效,可以通过修改修饰符为public的方法解决。
  3. 方法内部通过this调用另外一个方法,绕过了代理对象,导致AOP增强失效。
  4. 在非 Spring 管理的类中使用 @Transactional,可以通过在类上加@Component解决
  5. 事务传播属性设置了NEVER或者NOT_SUPPORTED。
  6. 数据库引擎不支持事务,Spring事务本质上还是基于数据库的。
  7. 开启新线程处理数据,原因DataSource和Connection通过Map存储到ThreadLocal中,同一个线程的所有SQL操作都是通过同一个Connection进行处理。新开启的线程拥有自己的ThreadLocal存储,无法继承父线程的Connection,不是同一个Connection,那事务就无从谈起了。

更多推荐