Spring中事务的使用示例、属性及使用中可能出现的问题

前言

本专题大纲如下:

对于专题大纲我又做了调整哈,主要是希望专题的内容能够更丰富,更加详细,本来是想在源码分析的文章中附带讲一讲事务使用中的问题,这两天想了想还是单独写一篇并作为事务专题的收尾篇,也是我Spring源码专题的收尾篇。

本文大纲如下:

在看这篇文章,以及下篇源码分析的文章我希望你对Spring AOP以及有充分的了解,不然一些细节问题你可能看不明白,关于Spring AOP如果你能看完这三篇文章基本上就没什么问题了

Spring官网阅读(十八)AOP的核心概念

Spring中AOP相关的API及源码解析,原来AOP是这样子的

你知道Spring是怎么将AOP应用到Bean的生命周期中的吗?

编程式事务

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. }
  17. }

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

  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. }
  28. }

这些方法具体的实现我们暂且不看,后续进行源码分析时都会详细介绍,之所以将这个代码贴出来是让大家更好的理解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;
  8. }

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. }
  43. }

从这个接口的名字上我们也能知道,它的主要完成了对事务定义的抽象,这些定义有些是数据库层面本身就有的,例如隔离级别、是否只读、超时时间、名称。也有些是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中,当然封装的时候肯定不是直接使用的接口,而是这个接口的一个实现类RuleBasedTransactionAttributeRuleBasedTransactionAttribute,该类的继承关系如下:

  • 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事务的状态,其继承关系如下:

  • 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主要是用来描述创建后的事务的状态

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

PlatformTransactionManager的实现类

  • AbstractPlatformTransactionManager,Spring提供的一个事务管理的基类,提供了事务管理的模板,实现了Spring事务管理的一个标准流程

    1. 判断当前是否已经存在一个事务
    2. 应用合适的事务传播行为
    3. 在必要的时候挂起/恢复事务
    4. 提交时检查事务是否被标记成为rollback-only
    5. 在回滚时做适当的修改(是执行真实的回滚/还是将事务标记成rollback-only
    6. 触发注册的同步回调
  • 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. }
  29. }

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

模拟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 {
  5. }

完成事务管理的核心类:

  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. }
  51. }

实际需要事务管理的类

  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. }
  9. }

切面:

  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. }
  25. }

用于测试的主函数:

  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. }
  8. }

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

总结

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

如果本文对你由帮助的话,记得点个赞吧!也欢迎关注我的公众号,微信搜索:程序员DMZ,或者扫描下方二维码,跟着我一起认认真真学Java,踏踏实实做一个coder。

我叫DMZ,一个在学习路上匍匐前行的小菜鸟!

Spring事务专题(四)Spring中事务的使用、抽象机制及模拟Spring事务实现的更多相关文章

  1. Spring 事务机制详解(事务的隔离性和传播性)

    原文出处: 陶邦仁 Spring事务机制主要包括声明式事务和编程式事务,此处侧重讲解声明式事务,编程式事务在实际开发中得不到广泛使用,仅供学习参考. Spring声明式事务让我们从复杂的事务处理中得到 ...

  2. 【转】Spring总结以及在面试中的一些问题

    [转]Spring总结以及在面试中的一些问题. 1.谈谈你对spring IOC和DI的理解,它们有什么区别? IoC Inverse of Control 反转控制的概念,就是将原本在程序中手动创建 ...

  3. Spring总结以及在面试中的一些问题

    Spring总结以及在面试中的一些问题. 1.谈谈你对spring IOC和DI的理解,它们有什么区别? IoC Inverse of Control 反转控制的概念,就是将原本在程序中手动创建Use ...

  4. nginx基本配置与参数说明以及Nginx中的upstream轮询机制介绍

    转自:http://blog.csdn.net/happydream_c/article/details/54943802 一.nginx简介 Nginx (发音为[engine x])专为性能优化而 ...

  5. 【Java EE 学习 52】【Spring学习第四天】【Spring与JDBC】【JdbcTemplate创建的三种方式】【Spring事务管理】【事务中使用dbutils则回滚失败!!!??】

    一.JDBC编程特点 静态代码+动态变量=JDBC编程. 静态代码:比如所有的数据库连接池 都实现了DataSource接口,都实现了Connection接口. 动态变量:用户名.密码.连接的数据库. ...

  6. Spring事务专题(三)事务的基本概念,Mysql事务处理原理

    前言 本专题大纲: 我重新整理了大纲,思考了很久,决定单独将MySQL的事务实现原理跟Spring中的事务示例分为两篇文章,因为二者毕竟没有什么实际关系,实际上如果你对MySQL的事务原理不感兴趣也可 ...

  7. Spring事务专题(五)聊聊Spring事务到底是如何实现的

    前言 本专题大纲: 本文为本专题倒数第二篇文章. 在上篇文章中我们一起学习了Spring中的事务抽象机制以及动手模拟了一下Spring中的事务管理机制,那么本文我们就通过源码来分析一下Spring中的 ...

  8. 深入学习Spring框架(四)- 事务管理

    1.什么是事务? 事务(Transaction)是一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的工作单位,是数据库环境中的逻辑工作单位.事务是为了保证数据库的完整性.例如:A给B转账,需 ...

  9. Spring 事务传播机制和数据库的事务隔离级别

    Propagation(事务传播属性) 类别 传播类型 说明 支持当前事务 REQUIRED 如果当前没有事务,就新建一个事务.@Transaction的默认选择 支持当前事务 SUPPORTS 就以 ...

随机推荐

  1. Linux基础入门(一)初识Shell

    Linux基础入门(一)初识Shell shell是什么 Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言. Shell ...

  2. Redis之字典

    概念 字典,又称为符号表.关联数组或映射(map),是一种用于保存键值对(key-value pair)的抽象数据结构.字典中每个键都是独一无二的,程序可以根据键来更新值,或者删除整个键值对. 用途 ...

  3. nvm配置及常用指令、配置全局node_global

    1.nvm-windows下载 nvm下载链接点击最新版本nvm-setup.zip下载安装即可. 2.配置nvm环境变量(安装nvm会默认配置,可忽略) 环境变量打开方式:右键此电脑 > 属性 ...

  4. 中介者模式(c++实现)

    中介者模式 目录 中介者模式 模式定义 模式动机 UML类图 源码实现 优点 缺点 模式定义 中介者模式(Mediator),用一个中介对象来封装一系列的对象交互.中介者使各对象不需要显示地相互引用, ...

  5. C++语法小记---标准库

    C++标准库 C++标准库包含如下内容: C++标准编译工具链 C++扩展编译工具链(各种C++编译器独有) C++标准库 C++库 C库 C兼容库(为了兼容能够用C编译器编译的项目,直接使用C++也 ...

  6. 洛谷p1120小木棍(剪枝优化)

    #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> ...

  7. ES模糊查询来对应mysql的like查询

    使用ES查询来对应mysql的like查询 建立一个测试索引 PUT /test_like1 { "mappings" : { "properties" : { ...

  8. 大数据框架Hive优化方法

    常规调优手段 Fetch抓取 某些情况查询不必用MapReduce计算,比如select*,可以直接读取文件 本地模式 有时数据量比较小,hive可以通过本地模式在单台机器上处理所有任务,对于小数据集 ...

  9. Pycharm远程解释器SFTP开发和调试

    转载:https://blog.csdn.net/ll641058431/article/details/53049453 使用PyCharm进行远程开发和调试 你是否经常要在Windows 7或MA ...

  10. android studio 部分问题及解决方案

    1 启动多个虚拟机后开启指定端口的虚拟机     https://blog.csdn.net/chuyouyinghe/article/details/72958004       adb devic ...