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
}

相关推荐
Yaml41 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~1 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616881 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
aloha_7892 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
记录成长java2 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
睡觉谁叫~~~3 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust
程序媛小果3 小时前
基于java+SpringBoot+Vue的旅游管理系统设计与实现
java·vue.js·spring boot
小屁孩大帅-杨一凡3 小时前
java后端请求想接收多个对象入参的数据
java·开发语言
java1234_小锋4 小时前
使用 RabbitMQ 有什么好处?
java·开发语言
TangKenny4 小时前
计算网络信号
java·算法·华为