Spring事务

Spring事务大致流程

当我们在某个方法上加了@Transactional注解后,就表示该方法在调用时会开启Spring事 务,而这个方法所在的类所对应的Bean对象会是该类的代理对象。

Spring事务的代理对象执行某个方法时的步骤:

  1. 判断当前执行的方法是否存在@Transactional注解
  2. 如果存在,则利用事务管理器(TransactionMananger)新建一个数据库连接
  3. 修改数据库连接的autocommit为false
  4. 执行target.test(),执行程序员所写的业务逻辑代码,也就是执行sql
  5. 执行完了之后如果没有出现异常,则提交,否则回滚 Spring事务是否会失效的判断标准:某个加了@Transactional注解的方法被调用时,要判 断到底是不是直接被代理对象调用的,如果是则事务会生效,如果不是则失效
  6. 如果没有抛异常,则提交
  7. 如果抛了异常,则回滚

Spring事务传播机制

Spring事务大致流程

当我们在某个方法上加了@Transactional注解后,就表示该方法在调用时会开启Spring事 务,而这个方法所在的类所对应的Bean对象会是该类的代理对象。

Spring事务的代理对象执行某个方法时的步骤:

  1. 判断当前执行的方法是否存在@Transactional注解
  2. 如果存在,则利用事务管理器(TransactionMananger)新建一个数据库连接
  3. 修改数据库连接的autocommit为false
  4. 执行target.test(),执行程序员所写的业务逻辑代码,也就是执行sql
  5. 执行完了之后如果没有出现异常,则提交,否则回滚 Spring事务是否会失效的判断标准:某个加了@Transactional注解的方法被调用时,要判 断到底是不是直接被代理对象调用的,如果是则事务会生效,如果不是则失效
  6. 如果没有抛异常,则提交
  7. 如果抛了异常,则回滚

Spring事务传播机制

默认情况下传播机制为REQUIRED,表示当前如果没有事务则新建一个事务,如果有事务则在当前事 务中执行。

也可以手动设置为REQUIRED_NEW,将ThreadLocal中已有的conn移除,并放入一个挂起对象资源中。

场景一:

typescript 复制代码
@Component 
public class UserService { 
​
      @Autowired 
      private UserService userService; 
​
      @Transactional 
      public void a() { 
         // sql 
         userService.b(); 
      }
​
      @Transactional 
      public void b() { 
          // sql
      } 
} 

执行流程:

  1. 事务管理器创建数据库连接conn
  1. 将autocommit设置为false
  1. 将数据库连接放置到ThreadLocal中
  1. 执行a中的sql
  1. 执行b中的sql
  1. 执行conn的方法commit提交

场景二:

typescript 复制代码
@Component 
public class UserService { 
​
      @Autowired 
      private UserService userService; 
​
      @Transactional
      public void a() { 
         // sql 
         userService.b(); 
      }
​
      @Transactional 
      public void b() { 
          // sql
          int i = 100/0;
      } 
}

执行流程:

1、事务管理器建立数据库连接conn

2、将autocommit设置成false

3、将conn放入ThreadLocal

4、执行方法a中的sql

5、执行方法b中的sql

6、异常报错

7、执行conn中的rollback回滚方法,两个方法中的事务都会回滚掉

场景三:

typescript 复制代码
@Component 
public class UserService { 
​
      @Autowired 
      private UserService userService; 
​
      @Transactional
      public void a() { 
         // sql 
         userService.b(); 
         int i = 100/0;
      }
​
      @Transactional 
      public void b() { 
          // sql
      } 
}

执行流程:

1、事务管理器创建一个数据库连接conn

2、将autocommit设置为false

3、执行方法a中的sql

4、执行b中的方法

5、报异常

6、调用conn的rollback方法,两个方法的事务都会回滚

场景四:

typescript 复制代码
@Component 
public class UserService { 
​
      @Autowired 
      private UserService userService; 
​
      @Transactional
      public void a() { 
         // sql 
         userService.b(); 
​
      }
​
      @Transactional(Propagation=propagation.REQUIRES_NEW)
      public void b() { 
          // sql
      } 
}

执行流程:

1、事务管理器创建一个数据库连接conn

2、将autocommit设置为false

3、执行方法a中的sql

4、将ThreadLocal中a方法的conn移除,挂起到资源对象中

5、事务管理器创建一个数据库连接conn2(方法b的)

6、 执行b方法的sql

7、conn2调用commit方法提交事务

8、然后再把挂起资源对象中的coon(方法a的)恢复到ThreadLocal中

9、a执行完后,conn调用commit方法提交事务

场景五:

java 复制代码
@Component 
public class UserService { 
​
      @Autowired 
      private UserService userService; 
​
      @Transactional
      public void a() { 
         // sql 
         userService.b(); 
         int i = 100/0;
​
      }
​
      @Transactional(Propagation=propagation.REQUIRES_NEW)
      public void b() { 
          // sql
      } 
}

​执行流程:

1、事务管理器创建一个数据库连接conn

2、将autocommit设置为false

3、执行方法a中的sql

4、将conn从ThreadLocal中移除,存入挂起资源中

5、事务管理器新建一个conn2存放到ThreadLocal中

6、报异常

7、两个事务都得回滚(因为事务a中调用了事务b,事务a报异常回滚了,事务b也得回滚)

场景六:

java 复制代码
@Component 
public class UserService { 
​
      @Autowired 
      private UserService userService; 
​
      @Transactional
      public void a() { 
         // sql 
         userService.b(); 
​
      }
​
      @Transactional(Propagation=propagation.REQUIRES_NEW)
      public void b() { 
          // sql
          int i = 100/0;
      } 
}

执行流程:

1、事务管理器创建一个数据库连接conn

2、设置autocommit为false

3、执行方法a的sql

4、将conn从ThreadLocal中移除,并存入挂起资源对象

5、方法b新建一个连接conn2

6、异常报错

7、conn2调用rollback回滚事务

8、继续抛异常,对于test()方法而言,它会接收到一个异常,然后抛出

9、执行conn的rollback()方法进行回滚,最终还是两个方法中的sql都回滚了

Spring事务强制回滚

正常情况下,a()调用b()方法时,如果b()方法抛了异常,但是在a()方法捕获了,那么a()的事务还是会正常提交的,但是你b()方法都执行异常并抛出了,还继续执行干什么,所以我们通常可以有在a()方法中捕获异常并将异常友好的提示出去:

java 复制代码
@Component 
public class UserService { 
​
      @Autowired 
      private UserService userService; 
​
      @Transactional
      public void a() { 
         // sql 
         try{
         userService.b(); 
         }catch{
            // 构造友好的错误信息返回
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
         }
​
      }
​
      @Transactional(Propagation=propagation.REQUIRES_NEW)
      public void b() throws Exception{ 
          // sql
          int i = 100/0;
      } 
}

TransactionSynchronization

Spring事务有可能会 提交,回滚、挂起、恢复,所以Spring事务提供了一种机制,可以让程序员来监 听当前Spring事务所处于的状态。

typescript 复制代码
@Component 
public class UserService { 
      @Autowired 
      private JdbcTemplate jdbcTemplate; 
      @Autowired 
      private UserService userService; 

      @Transactional 
      public void test(){ 
           TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { 
        @Override 
        public void suspend() { 
          System.out.println("test被挂起了"); } 

        @Override 
        public void resume() { 
          System.out.println("test被恢复了"); } 

        @Override 
        public void beforeCommit(boolean readOnly) { 
          System.out.println("test准备要提交了"); } 

        @Override 
        public void beforeCompletion() { 
          System.out.println("test准备要提交或回滚了"); } 

        @Override 
        public void afterCommit() { 
          System.out.println("test提交成功了"); } 

        @Override 
        public void afterCompletion(int status) { 
          System.out.println("test提交或回滚成功了");}}; 
        //方法test()的sql
}

相关推荐
Mahir0817 小时前
Spring 循环依赖深度解密:从问题本质到三级缓存源码级解析
java·后端·spring·缓存·面试·循环依赖·三级缓存
RyFit19 小时前
SpringAI 常见问题及解决方案大全
java·ai
石山代码19 小时前
C++ 内存分区 堆区
java·开发语言·c++
绝知此事19 小时前
【算法突围 01】线性结构与哈希表:后端开发的收纳术
java·数据结构·算法·面试·jdk·散列表
无风听海19 小时前
C# 隐式转换深度解析
java·开发语言·c#
一只大袋鼠20 小时前
Git 进阶(二):分支管理、暂存栈、远程仓库与多人协作
java·开发语言·git
德思特21 小时前
从 Dify 配置页理解 RAG 的重要参数
java·人工智能·llm·dify·rag
YOU OU21 小时前
Spring IoC&DI
java·数据库·spring
один but you21 小时前
从可变参数到 emplace:现代 C++ 性能优化的核心组合
java·开发语言
是码龙不是码农1 天前
ThreadPoolExecutor 7 个核心参数详解
java·线程池·threadpool