spring-事务

PS : 以下相关代码分析都是 基于 spring-tx : 5.3.2 版本

spring 事务抽象

关于spring 事务抽象主要体现为 事务管理 以及 事务相关定义;

事务管理 : org.springframework.transaction.PlatformTransactionManager  ,这里的platform 指代的是 原生JDBC(DataSourceTransactionManager) / JTA (JtaTransactionManager)/ hibernate (HibernateTransactionManager(需要引入 hibernate相关))

事务规范定义 : org.springframework.transaction.TransactionDefinition  其主要包含了 以下定义规范

  • propagation 事务传播
    • required : 如果当前没有事务则需要开启一个新的事务,反之如果存在事务则加入当前已存在的事务
    • supports : 如果存在事务则加入当前事务,反之则已无事务方式运行
    • mandatory : 强制性的,要求当前必须存在事务,否则会抛出异常;在事务存在的情况下,加入当前事务
    • requires_new : 不管事务是否存在都会开启一个新的事务,如果存在事务,则会挂起原有的事务; 对于这种情况主要是针对 spring service 之间方法调用
    • not_supported : 即使在调用方存在事务的情况下,也会以非事务方式运行
    • never : 强制要求必须不能存在事务,如果存在事务则会抛出异常
    • nested : 在事务存在的情况下会开启新的事务,但其存在一个层级关系,新开的事务称为内层事务,对于内层事务的具体提交或回滚是受完成事务控制的;
  • isolation  事务隔离级别,默认为-1,表示其使用数据库的事务隔离级别;其包含以下几种事务隔离级别(按照从低到高进行排序)
    • read_uncommitted : 读未提交, 会存在 脏读/幻读/不可重复读的问题
    • read_committed : 读已提交,会存在 幻读/不可重复读的问题
    • repeatable_read : 可重复读, 会存在幻读的问题
    • serializable : 序列化 , 不会存在任何事务问题
  • timeOut 事务过期时间,默认为-1,表示其使用数据库的事务默认过期时间  

关于 requires_new 和 nested 的区别?

https://stackoverflow.com/questions/12390888/differences-between-requires-new-and-nested-propagation-in-spring-transactions

PROPAGATION_REQUIRES_NEW starts a new, independent "inner" transaction for the given scope. This transaction will be committed or rolled back completely independent from the outer transaction, having its own isolation scope, its own set of locks, etc. The outer transaction will get suspended at the beginning of the inner one, and resumed once the inner one has completed. ...

PROPAGATION_NESTED on the other hand starts a "nested" transaction, which is a true subtransaction of the existing one. What will happen is that a savepoint will be taken at the start of the nested transaction. Íf the nested transaction fails, we will roll back to that savepoint. The nested transaction is part of of the outer transaction, so it will only be committed at the end of of the outer transaction.(PS JDBC模式) ...

 

可以查看 org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction 

在外部事务不存在的情况下,对于 requires_new 和 nested 两种事务隔离级别都会创建一个新的事务; (org.springframework.transaction.support.AbstractPlatformTransactionManager#startTransaction)

 在外部事务存在的情况下,不同的事务传播机制(org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction)

  • requires_new : 会首先挂起外部已存在的事务,并开启新的事务,在新的事务中会存储当前被挂起的外部事务; 对于外部事务和新创建的事务,实际其是相互独立存在的,内部事务的提交和回滚操作都不会影响到外部事务,除非出现异常上抛,导致整个方法异常中止,且触发了外部事务的回滚条件;否则新事务的任何操作都不会影响到外部事务的执行,且在内部事务完成(提交或回滚)之后会恢复当前被挂起的外部事务
  • nested : 判断当前是否是 JTA平台
    • 如果是JTA平台则会调用 startTransaction来创建一个新的transactionStatus,并标记当前状态为一个新的事务,并不会创建事务回滚点;因此其提交操作和 requires_new是相同的都是提交的新的事务
    • 如果是JDBC 则会直接利用AbstractPlatformTransactionManager#prepareTransactionStatus创建新的transactionStatus,但事务依旧是外部已存在的事务,并不会标记当前事务状态为一个新的事务,并会创建事务回滚点,由于对于JDBC实际并未创建新的事务,因此对于当前nested本身就等于外部事务,因此最终提交操作仍然是由外部事务管理,其和requires事务传播级别操作相同

关于 提交操作可以查看 org.springframework.transaction.support.AbstractPlatformTransactionManager#processCommit

关于 回滚操作可以查看 org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback

  • requires_new 级别下 由于两个事务的独立性,因此对于回滚操作都是单独的互不影响的
  • nested 级别下
    • 当外部事务回滚时,内部事务也会被回滚
    • 当内部事务回滚时,外部事务不会被回滚

关于requires_new 和 nested 对比测试代码

https://github.com/mengyan183/spring-family/blob/main/spring-transaction/src/test/java/com/xingguo/spring/transaction/annotationtx/service/TransactionPropagationServiceTest.java

 

 关于spring 事务具体操作

spring事务存在以下两种方式可以开启事务

  1. 编程式事务
  2. 声明式事务

关于编程式事务

对于编程式事务主要是使用以下两种方式

  1. org.springframework.transaction.support.TransactionTemplate#execute : 支持具有返回值的操作
  2. org.springframework.transaction.support.TransactionOperations#executeWithoutResult : 支持无返回值的操作

设置事务相关的定义 : 其不支持设置 rollback等相关参数, 对于事务的回滚操作需要利用 org.springframework.transaction.TransactionExecution#setRollbackOnly 来实现事务回滚

  1. org.springframework.transaction.support.DefaultTransactionDefinition#setIsolationLevel : 设置事务隔离级别
  2. org.springframework.transaction.support.DefaultTransactionDefinition#setPropagationBehavior : 设置事务传播机制
  3. org.springframework.transaction.support.DefaultTransactionDefinition#setTimeout : 设置事务过期时间

关于声明式事务

对于声明式事务 其实际是利用了 spring IOC 和 AOP 来实现的,其利用TransactionalInterceptor来拦截相关配置的代理类中的支持事务的方法,通过切面 环绕(around) 整个代理方法实现事务管理;

 对于使用声明式事务需要经过以下步骤

  1. 支持事务注解(两种方式)
    • xml 配置 使用 <tx:annotation-driven/>
    • 注解 org.springframework.transaction.annotation.EnableTransactionManagement (关于当前注解存在以下相关属性配置)
      • mode : AdviceMode.PROXY(默认设置,表示其基于动态代理进行增强); AdviceMode.ASPECTJ (表示其基于ASPECTJ进行增强)
      • proxyTargetClass : 对于当前属性只有在 mode 为 PROXY模式下才会生效; 默认值为 false(表示其使用 JDK动态代理(PROXY)); 当属性值为true 时,表示其使用CGLIB进行动态代理
      • order : 表示当前aop 执行顺序
  2. 使用事务注解 org.springframework.transaction.annotation.Transactional, 其包含以下属性配置
    1. transactionManager : 设置当前事务管理器,默认为容器中声明的bean
    2. propagation : 设置事务传播机制,默认为 REQUIRED
    3. isolation : 设置事务隔离级别 ,默认为 -1
    4. readOnly : 设置是否为自读 , 默认为false
    5. timeOut : 设置事务最长存活时间,默认为 -1
    6. rollBackFor : 批量设置触发事务回滚的所有异常类型,数据类型为 Throwable的子类
    7. noRollBackFor : 批量设置不会触发事务回滚的所有异常类型,数据类型为 Throwable的子类    

 

对于声明式spring事务不生效的理解

对于同一个service中的内部调用,如果入口方法会被spring代理事务管理,而对于内部调用是不执行spring代理的,因此对于内部调用的方法和入口方法是一致的,如果入口方法被spring事务托管,则内部的方法也被入口已开启的事务进行托管(被外部事务管理),如果入口不被spring事务托管,则内部方法也不会被事务管理;

对于spring 实际是利用了AOP进行的切面管理控制,其实际作用点在于 TransactionInterceptor

 

 
posted @ 2020-12-21 22:09  郭星  阅读(194)  评论(0)    收藏  举报