
一什么是事务怎么理解事务事务是保证数据库数据一致性的核心原生JDBC事务代码冗余侵入性强。Spring基于AOP动态代理进行封装提供声明式编程式两种事务实现方式。二事务特性Spring实现事务的底层原理原子性事务内所有操作要么全部成功提交要么全部失败回滚不可以部分执行一致性事务执行前后数据库数据完整性约束规则不被破坏隔离性多个并发事务相互隔离互不干扰通过隔离级别控制隔离程度持久性事务提交成功后服务器宕机重启不丢失三Spring事务的两种实现方式3.1 声明式事务主流需要添加依赖如下特点基于AOP动态代理无代码侵入使用简单。通过Transactional注解实现。生效范围仅作用于public方法方法执行前开启事务方法执行结束没有异常就提交事务否则回滚事务最后结束事务。3.2 编程式事务特殊场景特点需要手动控制事务的开启提交回滚代码具有侵入性。下面是如何实现编程式事务四Transactional详解如前边所说加了Transactional注解之后程序出现异常会自动回滚但是如果手动捕获异常就依然会提交事务如下代码但是如果希望程序报异常就手动再次手动抛异常但是如果希望程序不报异常而且希望事务回滚就需要进行手动回滚3.1 rollbackFor属性Transactional 默认只在遇到运行时异常和Error时才会回滚非运行时异常不发生回滚当代码中通过把rollbackFor属性设置为所有异常类型时候无论发生什么种类异常代码都会发生回滚3.2 Spring事务传播机制propagation属性事务的传播机制用于控制方法嵌套调用时候事务的传递创建挂起规则。3.2.1 Propagation.REQUIRED默认的事务传播级别如果当前存在事务则加入该事物如果没有事务则创建一个新的事务3.2.2 Propagation.SUPPORTS如果当前存在事务则加入该事务如果当前没有事务则以非事务的方式继续运行3.2.3 Propagation.MANDATORY强制性如果当前存在事务则加入该事务如果当前没有事务则抛出异常3.2.4 Propagation.REQUIRES_NEW创建一个新的事物如果当前存在事务则把当前事务挂起不管外部方法是否开启事务Propagation.REQUIRES_NEW修饰的内部方法都会新开启自己的事务并且开启的事务相互独立互不干扰3.2.5 Propagation.NOT——SUPPORTED以非事务的方式运行如果当前存在事务则把当前事务挂起。3.2.6 Propagation.NEVER以非事务的方式运行如果当前存在事务则抛出异常3.3 Spring事务隔离级别 isolation属性事务隔离级别有什么用本质就是控制不同并发事务之间的读写可见性规定当前事务能读到其他事务什么状态的数据从而控制脏读不可重复读幻读是否出现。并发事务三大脏问题1脏读读取到其他事物未提交的脏数据对方回滚后数据失效2不可重复读同一事务内多次读取同一数据可能会被其他事务修改更新3幻读同一事务内范围查询数据被其他事务新增 / 删除数据导致查询条数不一致3.3.1 Isolation.DEFAULT以连接的数据库的事务隔离级别为主3.3.2 Isolation.READ_UNCOMMITTED读未提交3.3.3 Isolation.READ_COMMITTED读已提交3.3.4 Isolation.REPEATABLE_READ可重复读3.3.5 Isolation.SERIALIZABLE串行化四Spring事务失效场景场景一注解加在非public方法上失效原因SpringAOP动态代理进拦截public方法private / procted / 默认权限方法无法被代理事务不生效解决方法统一将事务方法定义为public场景二同类内部方法自调用失效原因本类中this.xxx()调用带Transactional的方法未经过Spring代理对象AOP无法切入事务失效。解决方法注入自身代理类调用场景三异常类型不匹配默认不回滚失效原因Spring事务默认只回滚运行时异常和Error解决方法手动通过rollbackFor来指定异常类型一般是rollbackFor Exception.class场景四内部方法try-catch吞掉异常失效原因手动捕获异常不向外抛出AOP切面无法感知异常不会触发回滚解决方法手动再抛出异常或者手动调用事务回滚方法场景五事务方法被final / static修饰失效原因final / static方法无法被AOP动态代理重写事务注解失效解决方案事务方法禁止加finalstatic修饰场景六只读事务执行写操作失效原因配置 readOnly true 只读事务执行增删改查操作会直接报错事务失效解决方法查询方法只读事务写业务关闭readOnly场景七多线程异步调用事务方法失效原因Spring事务基于线程本地变量ThreadLocal实现多线程环境下线程上下文不同事务无法传递父子线程事务独立解决方案异步方法单独开启事务不依赖主线程事务五底层原理Spring事务执行核心流程Spring事务核心AOP动态代理 ThreadLocal事务上下文 事务管理器1调用事务方法Spring动态生成代理对象2切面前置拦截根据传播机制判断是否新建事务3通过ThreadLocal绑定当前事务上下文(也就是调用方法往ThreadLocalMap往里面存数据)4执行目标业务方法5方法无异常事务提交有异常事务回滚6.释放事务资源清楚线程上下文。补充ThreadLocal是个啥Thread每个线程实例内部有一个成员变量ThreadLocalMap threadLocals 这是线程私有容器别的线程访问不到。ThreadLocalMap: 是Thread的静态内部类本质上是一个自定义哈希表专门存线程私有数据ThreadLocal对外提供set/get/remove方法的工具类本身不存数据只负责操作当前线程中的ThreadLocalMapThreadLocal会根据当前正在运行的线程首先拿到这个县城专属的ThreadLocalMap