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
}

相关推荐
南山十一少4 分钟前
Spring Security+JWT+Redis实现项目级前后端分离认证授权
java·spring·bootstrap
427724002 小时前
IDEA使用git不提示账号密码登录,而是输入token问题解决
java·git·intellij-idea
chengooooooo2 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
李长渊哦2 小时前
常用的 JVM 参数:配置与优化指南
java·jvm
计算机小白一个2 小时前
蓝桥杯 Java B 组之设计 LRU 缓存
java·算法·蓝桥杯
南宫生5 小时前
力扣每日一题【算法学习day.132】
java·学习·算法·leetcode
计算机毕设定制辅导-无忧学长5 小时前
Maven 基础环境搭建与配置(一)
java·maven
风与沙的较量丶6 小时前
Java中的局部变量和成员变量在内存中的位置
java·开发语言
m0_748251726 小时前
SpringBoot3 升级介绍
java
极客先躯7 小时前
说说高级java每日一道面试题-2025年2月13日-数据库篇-请说说 MySQL 数据库的锁 ?
java·数据库·mysql·数据库的锁·模式分·粒度分·属性分