Spring中事务的使用、抽象机制及模拟Spring事务实现

数据库 其他数据库
本文主要介绍了Spring中的事务相关内容,对Spring中的事务抽象机制做了介绍,然后带着这些问题看源码你才能知道Spring为什么要做这些事情!

本文大纲如下:

Spring事务应用大纲

编程式事务

Spring提供了两种编程式事务管理的方法

  •  使用 TransactionTemplate 或者 TransactionalOperator.
  •  直接实现TransactionManager接口

如果是使用的是命令式编程,Spring推荐使用TransactionTemplate 来完成编程式事务管理,如果是响应式编程,那么使用TransactionalOperator更加合适。

TransactionTemplate

使用示例(我这里直接用的官网提供的例子了) 

  1. public class SimpleService implements Service {  
  2.     private final TransactionTemplate transactionTemplate;  
  3.      // 使用构造对transactionTemplate进行初始化  
  4.     // 需要提供一个transactionManager  
  5.     public SimpleService(PlatformTransactionManager transactionManager) {  
  6.         this.transactionTemplate = new TransactionTemplate(transactionManager);  
  7.     }  
  8.     public Object someServiceMethod() {  
  9.         return transactionTemplate.execute(new TransactionCallback() {  
  10.             public Object doInTransaction(TransactionStatus status) { 
  11.                  // 这里实现自己的相关业务逻辑  
  12.                 updateOperation1();  
  13.                 return resultOfUpdateOperation2();  
  14.             }  
  15.         });  
  16.     }  

在上面的例子中,我们显示的使用了TransactionTemplate来完成事务管理,通过实现TransactionCallback接口并在其doInTransaction方法中完成了我们对业务的处理。我们可以大概看下TransactionTemplate的execute方法的实现: 

  1. public <T> T execute(TransactionCallback<T> action) throws TransactionException {  
  2.  Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");  
  3.  if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {  
  4.   return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);  
  5.  }  
  6.  else {  
  7.            // 1.通过事务管理器开启事务  
  8.   TransactionStatus status = this.transactionManager.getTransaction(this);  
  9.   T result;  
  10.   try {  
  11.                // 2.执行传入的业务逻辑  
  12.    result = action.doInTransaction(status);  
  13.   }  
  14.   catch (RuntimeException | Error ex) {  
  15.    // 3.出现异常,进行回滚  
  16.    rollbackOnException(status, ex);  
  17.    throw ex;  
  18.   }  
  19.   catch (Throwable ex) {  
  20.    // 3.出现异常,进行回滚  
  21.    rollbackOnException(status, ex);  
  22.    throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");  
  23.   }  
  24.            // 4.正常执行完成的话,提交事务  
  25.   this.transactionManager.commit(status);  
  26.   return result;  
  27.  }  

这些方法具体的实现我们暂且不看,后续进行源码分析时都会详细介绍,之所以将这个代码贴出来是让大家更好的理解TransactionTemplate的工作机制:实际上就是通过一个TransactionCallback封装了业务逻辑,然后TransactionTemplate会在事务的上下文中调用。

在上面的例子中doInTransaction是有返回值的,而实际上有时候并不需要返回值,这种情况下,我们可以使用TransactionCallbackWithoutResult提代TransactionCallback。 

  1. transactionTemplate.execute(new TransactionCallbackWithoutResult() {  
  2.     protected void doInTransactionWithoutResult(TransactionStatus status) {  
  3.         updateOperation1();  
  4.         updateOperation2();  
  5.     }  
  6. }); 

❝实际上我们还可以通过TransactionTemplate指定事务的属性,例如隔离级别、超时时间、传播行为等等

TransactionTemplate是线程安全的,我们可以全局配置一个TransactionTemplate,然后所有的类都共享这个TransactionTemplate。但是,如果某个类需要特殊的事务配置,例如需要定制隔离级别,那么我们就有必要去创建不同的TransactionTemplate。❞

TransactionOperator

❝TransactionOperator适用于响应式编程的情况,这里就不做详细介绍了❞

TransactionManager

实际上TransactionTemplate内部也是使用TransactionManager来完成事务管理的,我们之前也看过它的execute方法的实现了,其实内部就是调用了TransactionManager的方法,实际上就是分为这么几步

  1.  开启事务
  2.  执行业务逻辑
  3.  出现异常进行回滚
  4.  正常执行则提交事务

这里我还是直接用官网给出的例子 

  1. // 定义事务  
  2. DefaultTransactionDefinition def = new DefaultTransactionDefinition();  
  3. def.setName("SomeTxName");  
  4. def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);  
  5. // txManager,事务管理器  
  6. // 通过事务管理器开启一个事务  
  7. TransactionStatus status = txManager.getTransaction(def);  
  8. try {  
  9.     // 完成自己的业务逻辑  
  10.  
  11. catch (MyException ex) {  
  12.     // 出现异常,进行回滚  
  13.     txManager.rollback(status);  
  14.     throw ex;  
  15.  
  16. // 正常执行完成,提交事务  
  17. txManager.commit(status); 

我们在后边的源码分析中其实重点分析的也就是TransactionManager的源码。

申明式事务

在对编程式事务有一定了解之后我们会发现,编程式事务存在下面几个问题:

  1.  「我们的业务代码跟事务管理的代码混杂在一起」。
  2.  「每个需要事务管理的地方都需要写重复的代码」

如何解决呢?这就要用到申明式事务了,实现申明式事务一般有两种方式

  •  基于XML配置
  •  基于注解

申明式事务事务的实现原理如下(图片来源于官网):

实现原理

「实际上就是结合了APO自动代理跟事务相关API」。通过开启AOP自动代理并向容器中注册了事务需要的通知(Transaction Advisor),在Transaction Advisor调用了事务相关API,其实内部也是调用了TransactionManager的方法。

基于XML配置这种方式就不讲了,笔者近两年时间没用过XML配置,我们主要就看看通过注解方式来实现申明式事务。主要涉及到两个核心注解

  1.  @EnableTransactionManagement
  2.  @Transactional

@EnableTransactionManagement这个注解主要有两个作用,其一是,开启AOP自动代理,其二是,添加事务需要用到的通知(Transaction Advisor),如果你对AOP有一定了解的话那你应该知道一个Advisor实际上就是一个绑定了切点(Pointcut)的通知(Advice),通过@EnableTransactionManagement这个注解导入的Advisor所绑定的切点就是通过@Transactional来定义的。

申明式事务的例子我这里就省去了,我相信没几个人不会用吧.....

Spring对事务的抽象

Spring事务抽象的关键就是事务策略的概念,事务策略是通过TransactionManager接口定义的。TransactionManager本身只是一个标记接口,它有两个直接子接口

  1.  ReactiveTransactionManager,这个接口主要用于在响应式编程模型下,不是我们要讨论的重点
  2.  PlatformTransactionManager,命令式编程模型下我们使用这个接口。

❝关于响应式跟命令式编程都可以单独写一篇文章了,本文重点不是讨论这两种编程模型,可以认为平常我们使用的都是命令式编程❞

PlatformTransactionManager

PlatformTransactionManager接口定义 

  1. public interface PlatformTransactionManager extends TransactionManager {  
  2.  // 开启事务  
  3.     TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;  
  4.     // 提交事务  
  5.     void commit(TransactionStatus status) throws TransactionException;  
  6.     // 回滚事务  
  7.     void rollback(TransactionStatus status) throws TransactionException;  

PlatformTransactionManager是命令式编程模型下Spring事务机制的中心接口,定义了完成一个事务必须的三个步骤,也就是说定义了事务实现的规范

  •  开启事务
  •  提交事务
  •  回滚事务

通常来说,我们不会直接实现这个接口,而是通过继承AbstractPlatformTransactionManager,这个类是一个抽象类,主要用作事务管理的模板,这个抽象类已经实现了事务的传播行为以及跟事务相关的同步管理。

回头看接口中定义的三个方法,首先是开启事务的方法,从方法签名上来看,其作用就是通过一个TransactionDefinition来获取一个TransactionStatus类型的对象。为了更好的理解Spring中事务的抽象我们有必要了解下这两个接口

TransactionDefinition

接口定义如下: 

  1. public interface TransactionDefinition {  
  2.      // 定义了7中事务的传播机制  
  3.  int PROPAGATION_REQUIRED = 0 
  4.  int PROPAGATION_SUPPORTS = 1 
  5.  int PROPAGATION_MANDATORY = 2 
  6.  int PROPAGATION_REQUIRES_NEW = 3 
  7.  int PROPAGATION_NOT_SUPPORTED = 4 
  8.  int PROPAGATION_NEVER = 5 
  9.  int PROPAGATION_NESTED = 6 
  10.     // 4种隔离级别,-1代表的是使用数据库默认的隔离级别  
  11.     // 比如在MySQL下,使用的就是ISOLATION_REPEATABLE_READ(可重复读)  
  12.  int ISOLATION_DEFAULT = -1;  
  13.  int ISOLATION_READ_UNCOMMITTED = 1;    
  14.  int ISOLATION_READ_COMMITTED = 2;   
  15.  int ISOLATION_REPEATABLE_READ = 4;   
  16.  int ISOLATION_SERIALIZABLE = 8;     
  17.     // 事务的超时时间,默认不限制时间  
  18.  int TIMEOUT_DEFAULT = -1; 
  19.      // 提供了对上面三个属性的get方法  
  20.  default int getPropagationBehavior() {  
  21.   return PROPAGATION_REQUIRED;  
  22.  }  
  23.  default int getIsolationLevel() {  
  24.   return ISOLATION_DEFAULT;  
  25.  }  
  26.  default int getTimeout() {  
  27.   return TIMEOUT_DEFAULT;  
  28.  }  
  29.     // 事务是否是只读的,默认不是  
  30.  default boolean isReadOnly() {  
  31.   return false;  
  32.  }    
  33.     // 事务的名称  
  34.  @Nullable  
  35.  default String getName() {  
  36.   return null;  
  37.  }    
  38.     // 返回一个只读的TransactionDefinition  
  39.     // 只对属性提供了getter方法,所有属性都是接口中定义的默认值  
  40.  static TransactionDefinition withDefaults() {  
  41.   return StaticTransactionDefinition.INSTANCE;  
  42.  }  

从这个接口的名字上我们也能知道,它的主要完成了对事务定义的抽象,这些定义有些是数据库层面本身就有的,例如隔离级别、是否只读、超时时间、名称。也有些是Spring赋予的,例如事务的传播机制。Spring中一共定义了7种事务的传播机制

  •  TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  •  TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  •  TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  •  TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  •  TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
  •  TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  •  TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

关于事务的传播在源码分析的文章中我会重点介绍,现在大家留个印象即可。

我们在使用申明式事务的时候,会通过@Transactional这个注解去申明某个方法需要进行事务管理,在@Transactional中可以定义事务的属性,这些属性实际上就会被封装到一个TransactionDefinition中,当然封装的时候肯定不是直接使用的接口,而是这个接口的一个实现类RuleBasedTransactionAttribute。RuleBasedTransactionAttribute,该类的继承关系如下:

RuleBasedTransactionAttribute

  •  DefaultTransactionDefinition,实现了TransactionDefinition,并为其中的定义的属性提供了默认值   
  1. // 默认的传播机制为required,没有事务新建一个事务  
  2.     // 有事务的话加入当前事务  
  3.     private int propagationBehavior = PROPAGATION_REQUIRED 
  4.     // 隔离级别跟数据库默认的隔离级别一直  
  5.     private int isolationLevel = ISOLATION_DEFAULT 
  6.     // 默认为-1,不设置超时时间  
  7.     private int timeout = TIMEOUT_DEFAULT 
  8.     // 默认不是只读的  
  9.     private boolean readOnly = false
  •  TransactionAttribute,扩展了``DefaultTransactionDefinition`,新增了两个事务的属性   
  1. // 用于指定事务使用的事务管理器的名称  
  2.     String getQualifier();  
  3.     // 指定在出现哪种异常时才进行回滚  
  4.     boolean rollbackOn(Throwable ex); 
  •  DefaultTransactionAttribute,继承了DefaultTransactionDefinition,同时实现了TransactionAttribute接口,定义了默认的回滚异常 
  1. // 抛出RuntimeException/Error才进行回滚  
  2.   public boolean rollbackOn(Throwable ex) {  
  3.       return (ex instanceof RuntimeException || ex instanceof Error);  
  4.   } 
  •  RuleBasedTransactionAttribute,@Transactional注解的rollbackFor等属性就会被封装到这个类中,允许程序员自己定义回滚的异常,如果没有指定回滚的异常,默认「抛出RuntimeException/Error才进行回滚」

TransactionStatus

这个接口主要用于描述Spring事务的状态,其继承关系如下:

TransactionStatus

  •  TransactionExecution,这个接口也是用于描述事务的状态,TransactionStatus是在其上做的扩展,内部定义了以下几个方法 
  1. // 判断当前事务是否是一个新的事务  
  2.  // 不是一个新事务的话,那么需要加入到已经存在的事务中  
  3.  boolean isNewTransaction();  
  4.  // 事务是否被标记成RollbackOnly  
  5.  // 如果被标记成了RollbackOnly,意味着事务只能被回滚  
  6.  void setRollbackOnly();   
  7.  boolean isRollbackOnly();  
  8.  // 是否事务完成,回滚或提交都意味着事务完成了  
  9.  boolean isCompleted(); 
  •  SavepointManager,定义了管理保存点(Savepoint)的方法,隔离级别为NESTED时就是通过设置回滚点来实现的,内部定义了这么几个方法   
  1. // 创建保存点  
  2.    Object createSavepoint() throws TransactionException;  
  3.    // 回滚到指定保存点  
  4.    void rollbackToSavepoint(Object savepoint) throws TransactionException;  
  5.    // 移除回滚点  
  6.    void releaseSavepoint(Object savepoint) throws TransactionException; 
  •  TransactionStatus,继承了上面这些接口,额外提供了两个方法   
  1. //用于判断当前事务是否设置了保存点  
  2.    boolean hasSavepoint(); 
  3.    // 这个方法复写了父接口Flushable中的方法  
  4.    // 主要用于刷新会话  
  5.    // 对于Hibernate/jpa而言就是调用了其session/entityManager的flush方法  
  6.    void flush(); 

❝小总结:通过上面的分析我们会发现,TransactionDefinition的主要作用是给出一份事务属性的定义,然后事务管理器根据给出的定义来创建事务,TransactionStatus主要是用来描述创建后的事务的状态❞

在对TransactionDefinition跟TransactionStatus有一定了解后,我们再回到PlatformTransactionManager接口本身,PlatformTransactionManager作为事务管理器的基础接口只是定义管理一个事务必须的三个方法:开启事务,提交事务,回滚事务,接口仅仅是定义了规范而已,真正做事的还是要依赖它的实现类,所以我们来看看它的继承关系

PlatformTransactionManager的实现类

PlatformTransactionManager

  •  AbstractPlatformTransactionManager,Spring提供的一个事务管理的基类,提供了事务管理的模板,实现了Spring事务管理的一个标准流程
  1.  判断当前是否已经存在一个事务
  2.  应用合适的事务传播行为
  3.  在必要的时候挂起/恢复事务
  4.  提交时检查事务是否被标记成为rollback-only
  5.  在回滚时做适当的修改(是执行真实的回滚/还是将事务标记成rollback-only)

触发注册的同步回调

在AbstractPlatformTransactionManager提供了四个常见的子类,其说明如下

关于事务管理器的详细代码分析放到下篇文章,本文对其有个大概了解即可。

Spring中事务的同步机制

Spring中事务相关的同步机制可以分为两类

  •  资源的同步
  •  行为的同步

什么是资源的同步呢?在一个事务中我们往往会一次执行多个SQL(如果是单条的SQL实际上没有必要开启事务),为了保证事务所有的SQL都能够使用一个数据库连接,这个时候我们需要将数据库连接跟事务进行同步,这个时候数据库连接就是跟这个事务同步的一个资源。

那什么又是行为的同步呢?还是以数据库连接为例子,在事务开启之前我们需要先获取一个数据库连接,同样的在事务提交时我们需要将连接关闭(不一定是真正的关闭,如果是连接池只是归还到连接池中),这个时候关闭连接这个行为也需要跟事务进行同步

那么Spring是如何来管理同步的呢?同样的,Spring也提供了一个同步管理器TransactionSynchronizationManager,这是一个抽象类,其中所有的方法都是静态的,并且所有的方法都是围绕它所申明的几个静态常量字段,如下: 

  1. // 这就是同步的资源,Spring就是使用这个完成了连接的同步  
  2. private static final ThreadLocal<Map<Object, Object>> resources =  
  3.     new NamedThreadLocal<>("Transactional resources");  
  4. // TransactionSynchronization完成了行为的同步  
  5. // 关于TransactionSynchronization在后文进行分析  
  6. private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =  
  7.     new NamedThreadLocal<>("Transaction synchronizations"); 
  8. // 事务的名称  
  9. private static final ThreadLocal<String> currentTransactionName =  
  10.     new NamedThreadLocal<>("Current transaction name");  
  11. // 事务是否被标记成只读  
  12. private static final ThreadLocal<Boolean> currentTransactionReadOnly =  
  13.     new NamedThreadLocal<>("Current transaction read-only status"); 
  14. // 事物的隔离级别  
  15. private static final ThreadLocal<Integer> currentTransactionIsolationLevel =  
  16.     new NamedThreadLocal<>("Current transaction isolation level");  
  17. // 是否真实开启了事务   
  18. private static final ThreadLocal<Boolean> actualTransactionActive =  
  19.     new NamedThreadLocal<>("Actual transaction active"); 

可以看到所有的同步都是通过ThreadLocal实现的,对于ThreadLocal本文不做详细分析,如果对ThreadLocal还不了解的同学也没有关系,对于本文而言你只需要知道ThreadLocal能将资源跟当前线程绑定即可,例如ThreadLocal<Map<Object, Object>> resources这个属性就代表要将一个map绑定到当前线程,它提供了set跟get方法,分别用于将属性绑定到线程上以及获取线程上绑定的属性。

上面的几个变量中除了synchronizations之外其余的应该都很好理解,synchronizations中绑定的是一个TransactionSynchronization的集合,那么这个TransactionSynchronization有什么用呢?我们来看看它的接口定义 

  1. public interface TransactionSynchronization extends Flushable {  
  2.  // 事务完成的状态  
  3.     // 0 提交  
  4.     // 1 回滚  
  5.     // 2 异常状态,例如在事务执行时出现异常,然后回滚,回滚时又出现异常  
  6.     // 就会被标记成状态2  
  7.  int STATUS_COMMITTED = 0 
  8.  int STATUS_ROLLED_BACK = 1 
  9.  int STATUS_UNKNOWN = 2 
  10.     // 我们绑定的这些TransactionSynchronization需要跟事务同步  
  11.     // 1.如果事务挂起,我们需要将其挂起  
  12.     // 2.如果事务恢复,我们需要将其恢复  
  13.  default void suspend() {  
  14.  }  
  15.  default void resume() {  
  16.  }  
  17.  @Override  
  18.  default void flush() {  
  19.  }  
  20.     // 在事务执行过程中,提供的一些回调方法  
  21.  default void beforeCommit(boolean readOnly) {  
  22.  }  
  23.  default void beforeCompletion() {  
  24.  }  
  25.  default void afterCommit() {  
  26.  }  
  27.  default void afterCompletion(int status) {  
  28.  }  

可以看到这个接口就是定义了一些方法,这些个方法可以在事务达到不同阶段后执行,可以认为定义了事务执行过程的一些回调行为,这就是我之前说的行为的同步。

模拟Spring事务的实现

本文的最后一部分希望大家模拟一下Spring事务的实现,我们利用现有的AOP来实现事务的管理。数据库访问我们直接使用jdbc,在模拟之前我们先明确两点

  1.  切点应该如何定义?
  2.  通知要实现什么功能?

我们先说第一个问题,因为是我们自己模拟,所以关于切点的定义我们就设置的尽量简单一些,不妨就直接指定某个包下的所有类。对于第二个问题,我们也不做的过于复杂,在方法执行前开启事务,在方法执行后提交事务并关闭连接,所以我们需要定义一个环绕通知。同时,我们也需要将连接跟事务同步,保证事务中的所有SQL共用一个事务是实现事务管理的必要条件。基于此,我们开始编写代码

我们只需要引入Spring相关的依赖跟JDBC相关依赖即可,该项目仅仅是一个Spring环境下的Java项目,没有Web依赖,也不是SpringBoot项目,项目结构如下:

POM文件: 

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"  
  3.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
  5.     <modelVersion>4.0.0</modelVersion>  
  6.     <groupId>com.dmz.framework</groupId>  
  7.     <artifactId>mybatis</artifactId>  
  8.     <version>1.0-SNAPSHOT</version> 
  9.     <dependencies>      
  10.         <dependency>  
  11.             <groupId>mysql</groupId>  
  12.             <artifactId>mysql-connector-java</artifactId>  
  13.             <version>8.0.15</version>  
  14.         </dependency>       
  15.          <dependency>  
  16.             <groupId>org.springframework</groupId>  
  17.             <artifactId>spring-context</artifactId>  
  18.             <version>5.2.6.RELEASE</version>  
  19.         </dependency>       
  20.          <dependency>  
  21.             <groupId>org.springframework</groupId>  
  22.             <artifactId>spring-aop</artifactId>  
  23.             <version>5.2.6.RELEASE</version>  
  24.         </dependency> 
  25.         <dependency>  
  26.             <groupId>org.aspectj</groupId>  
  27.             <artifactId>aspectjweaver</artifactId>  
  28.             <version>1.9.5</version>  
  29.         </dependency>  
  30.     </dependencies>  
  31. </project> 

配置类: 

  1. // 开启AOP跟扫描组件即可  
  2. @EnableAspectJAutoProxy 
  3. @ComponentScan("com.dmz.mybatis.tx_demo")  
  4. public class Config {  

完成事务管理的核心类: 

  1. public class TransactionUtil {  
  2.     public static final ThreadLocal<Connection> synchronousConnection =  
  3.             new ThreadLocal<Connection>();  
  4.     private TransactionUtil() {  
  5.     } 
  6.     public static Connection startTransaction() {  
  7.         Connection connection = synchronousConnection.get();  
  8.         if (connection == null) {  
  9.             try {  
  10.                 // 这里替换成你自己的连接地址即可  
  11.                 connection = DriverManager  
  12.                         .getConnection("jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8", "root", "123");  
  13.                 synchronousConnection.set(connection);  
  14.                 connection.setAutoCommit(false);  
  15.             } catch (SQLException e) {  
  16.                 e.printStackTrace();  
  17.             }  
  18.         }  
  19.         return connection;  
  20.     }  
  21.     public static int execute(String sql, Object... args) {  
  22.         Connection connection = startTransaction();  
  23.         try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {  
  24.             if (args != null) {  
  25.                 for (int i = 1; i < args.length + 1; i++) {  
  26.                     preparedStatement.setObject(i, args[i - 1]);  
  27.                 }  
  28.             }  
  29.             return preparedStatement.executeUpdate();  
  30.         } catch (SQLException e) {  
  31.             e.printStackTrace();  
  32.         }  
  33.         return 0; 
  34.      } 
  35.     public static void commit() {  
  36.         try (Connection connection = synchronousConnection.get()) {  
  37.             connection.commit();  
  38.             synchronousConnection.remove();  
  39.         } catch (SQLException e) {  
  40.             e.printStackTrace();  
  41.         }  
  42.     }  
  43.     public static void rollback() {  
  44.         try (Connection connection = synchronousConnection.get()) {  
  45.             connection.rollback();  
  46.             synchronousConnection.remove();  
  47.         } catch (SQLException e) {  
  48.             e.printStackTrace();  
  49.         }  
  50.     }  

实际需要事务管理的类 

  1. @Component  
  2. public class UserService {  
  3.     public void saveUser() {  
  4.         TransactionUtil.execute  
  5.                 ("INSERT INTO `test`.`user`(`id`, `name`) VALUES (?, ?)", 100, "dmz");  
  6.         // 测试回滚  
  7.         // throw new RuntimeException();  
  8.     }  

切面: 

  1. @Component  
  2. @Aspect  
  3. public class TxAspect {  
  4.     @Pointcut("execution(public * com.dmz.mybatis.tx_demo.service..*.*(..))")  
  5.     private void pointcut() {  
  6.     }  
  7.     @Around("pointcut()") 
  8.      public Object around(JoinPoint joinPoint) throws Throwable {  
  9.         // 在方法执行前开启事务  
  10.         TransactionUtil.startTransaction();  
  11.         // 执行业务逻辑  
  12.         Object proceed = null 
  13.         try {  
  14.             ProceedingJoinPoint method = (ProceedingJoinPoint) joinPoint;  
  15.             proceed = method.proceed();  
  16.         } catch (Throwable throwable) {  
  17.             // 出现异常进行回滚  
  18.             TransactionUtil.rollback();  
  19.             return proceed;  
  20.         }  
  21.         // 方法执行完成后提交事务  
  22.         TransactionUtil.commit();  
  23.         return proceed;  
  24.     }  

用于测试的主函数: 

  1. public class Main {  
  2.     public static void main(String[] args) {  
  3.         AnnotationConfigApplicationContext ac =  
  4.                 new AnnotationConfigApplicationContext(Config.class);  
  5.         UserService userService = ac.getBean(UserService.class);  
  6.         userService.saveUser();  
  7.     }  

具体的测试过程跟测试结果我就不放了,大家把代码拷贝过去自行测试就好了

总结

本文主要介绍了Spring中的事务相关内容,对Spring中的事务抽象机制做了介绍,主要是为了让大家在接下来一篇源码文章中能减轻负担,希望大家可以根据自己理解动手模拟下Spring中事务的实现哦,当你自己去实现的时候肯定会碰到一系列的问题,然后带着这些问题看源码你才能知道Spring为什么要做这些事情! 

 

责任编辑:庞桂玉 来源: Hollis
相关推荐

2023-10-30 07:36:19

Spring事务传播机制

2009-09-25 12:59:53

Hibernate事务

2009-07-20 18:11:52

iBATIS事务Spring

2023-05-05 07:39:04

Spring事务面试

2020-07-17 08:07:54

Spring数据库事务

2022-08-27 14:14:06

Spring事务开发

2009-06-22 09:01:57

Spring声明式事务

2018-11-16 15:35:10

Spring事务Java

2011-03-25 10:00:23

Spring3.0事务的配置

2023-12-29 18:53:58

微服务Saga模式

2022-02-14 16:53:57

Spring项目数据库

2021-10-29 07:49:22

Spring事务管理

2009-06-29 17:54:47

Spring事务隔离

2024-01-29 08:28:01

Spring事务失效

2022-09-27 21:14:54

Spring事务传播机制

2022-08-22 08:04:25

Spring事务Atomicity

2022-04-26 21:49:55

Spring事务数据库

2011-11-25 10:25:27

SpringJava

2009-06-19 18:26:38

Spring事务配置

2011-02-28 13:51:30

Spring事物配置
点赞
收藏

51CTO技术栈公众号