• 【Spring 事务和事务传播机制】


    目录

    1 事务概述

    1.1 为什么需要事务

    1.2 事务的特性

    1.3 Spring 中事务的实现

    2 Spring 声明式事务

    2.1 @Transactional

    2.2 @Transactional 的作用范围

    2.3 @Transactional 的各种参数

    2.3.1 ioslation 

    2.4 事务发生了异常,也不回滚的情况

    异常被捕获时

    3 事务的传播机制 

    3.1 定义

    3.2 为什么需要事务传播机制

    3.3 事务传播机制种类

    Propagation.REQUIRED 

     Propagation.SUPPORTS 

     Propagation.MANDATORY

    Propagation.REQUIRES_NEW

    Propagation.NOT_SUPPORTED

    Propagation.NEVER

    Propagation.NESTED


    1 事务概述

    1.1 为什么需要事务

    事务定义

    将⼀组操作封装成⼀个执⾏单元(封装到⼀起),要么全部成功,要么全部失败。

    对于转账来说:

    第一步操作:A 账户 -1000 元

    第二步操作:B 账户 +1000 元

    使用了事务之后,这一组操作要么一起执行成功,要么一起失败。如果没有事务的情况下,有可能第一步成功了,第二步失败了,那么对于 A 来说,它的账户的 1000 块钱就人间蒸发了。

    1.2 事务的特性

    事务有4 ⼤特性(ACID),原⼦性、持久性、⼀致性和隔离性,具体概念如下:

    原⼦性(Atomicity):⼀个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中 间某个环节。事务在执⾏过程中发⽣错误,会被回滚(Rollback)到事务开始前的状态,就像这个 事务从来没有执⾏过⼀样。

    ⼀致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写⼊的资料必须完 全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以⾃发性地完成预定的⼯ 作。

    隔离性(Isolation):数据库允许多个并发事务同时对其数据进⾏读写和修改的能⼒,隔离性可以防⽌多个事务 并发执⾏时由于交叉执⾏⽽导致数据的不⼀致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串⾏化 (Serializable)。

    持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

    1.3 Spring 中事务的实现

    Spring 中事务操作分为两类:

    1. 编程式事务(手动写代码操作事务)

    2. 声明式事务(利用注解自动开启和提交事务)

    2 Spring 声明式事务

    MySQL 中,事务有三个重要的操作:开启事务、提交事务、回滚事务,对应的操作命令如下:

    -- 开启事务

    start transaction;

     

    -- 业务执行

      

    -- 提交事务

    commit;

      

    -- 回滚事务

    rollback;

    而 Spring 声明式事务的实现与 MySQL 事务的实现基本一致,只是前者自动的,只需要在需要的⽅法上添加 @Transactional 注解就可以实现了,⽆需⼿动开启事务和提交事务,进⼊⽅法时⾃动开启事务,⽅法执⾏完会⾃动提交事务,如果中途发⽣了没有处理的异常会⾃动回滚事务。

    2.1 @Transactional

    1. package com.example.demo.model;
    2. import lombok.Data;
    3. import java.time.LocalDateTime;
    4. @Data
    5. public class UserInfo {
    6. private int id;
    7. private String username;
    8. private String password;
    9. private String photo;
    10. private LocalDateTime createtime;
    11. private LocalDateTime updatetime;
    12. private int state;
    13. }
    1. package com.example.demo.mapper;
    2. import com.example.demo.model.UserInfo;
    3. import org.apache.ibatis.annotations.Insert;
    4. import org.apache.ibatis.annotations.Mapper;
    5. @Mapper
    6. public interface UserMapper {
    7. @Insert("insert into userinfo(username,password) values(#{username},#{password})")
    8. int add(UserInfo userInfo);
    9. }
    1. package com.example.demo.service;
    2. import com.example.demo.mapper.UserMapper;
    3. import com.example.demo.model.UserInfo;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.stereotype.Service;
    6. @Service
    7. public class UserService {
    8. @Autowired
    9. private UserMapper userMapper;
    10. public int add(UserInfo userInfo){
    11. return userMapper.add(userInfo);
    12. }
    13. }
    1. package com.example.demo.controller;
    2. import com.example.demo.model.UserInfo;
    3. import com.example.demo.service.UserService;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.transaction.annotation.Transactional;
    6. import org.springframework.web.bind.annotation.RequestMapping;
    7. import org.springframework.web.bind.annotation.RestController;
    8. @RequestMapping("/user")
    9. @RestController
    10. public class UserController {
    11. @Autowired
    12. private UserService userService;
    13. @RequestMapping("/add")
    14. @Transactional
    15. public int add(){
    16. // 1. 非空判断
    17. UserInfo userInfo = new UserInfo();
    18. userInfo.setUsername("莉丝");
    19. userInfo.setPassword("6363");
    20. // 2. 调用 service 执行添加
    21. int result = userService.add(userInfo);
    22. System.out.println("result:"+result);
    23. int num = 10/0;
    24. // 3. 将结果给前端
    25. return 0;
    26. }
    27. }

    启动项目发现,使用了 @Transactional 注解的确回滚了事务:

     可以看到,信息并没有添加到数据库的 userinfo 这表里。

    2.2 @Transactional 的作用范围

    @Transactional 可以用来修饰方法或类,修饰方法时,只能应用到 public 方法上,其他访问权限均不生效;修饰类时,该注解对该类中所有的 public 方法都生效。

    2.3 @Transactional 的各种参数

    参数作用
    value配置多个事务管理器时,使用该属性指定一个事务管理器
    transactionManager
    propagation事务的传播行为,默认值为 Propagation.REQUIRED
    isolation事务的隔离级别,默认值为 Isolation.DEFAULT
    timeout事务的超出时间,默认值为 -1。如果超出该时间限制但事务还没有完成,则自动回滚事务
    readOnly是否为只读事务,默认值为 false;
    rollbackFor指定触发事务回滚的异常类型,可以多个
    rollbackForClassName指定触发事务回滚的异常类名称,可以多个
    noRollbackFor指定不回滚事务的异常类型,可以多个
    noRollbackForClassName指定不回滚事务的异常类名称,可以多个

    2.3.1 ioslation 

    根据文章开头的介绍,我们知道事务有四大特性,分别是原子性、一致性、隔离性以及持久性。而这四大特性中,只有隔离性,也就是隔离级别是可以设置的。

    事务的隔离级别是用来保障多个并发事务执行更加可控,更符合程序员的预期。

    在 Spring 中,可以通过 @Transactional 中的 isolation 属性进行设置。

    MySQL 事务隔离级别:

    1. READ UNCOMMITTED:读未提交,该隔离级别的事务可以看到其他事务中未提交的数据,⽽未提交的数据可能会发⽣回滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读。

    2. READ COMMITTED:读已提交,该隔离级别的事务能读取到已经提交事务的数据, 因此它不会有脏读问题。但由于在事务的执⾏中可以读取到其他事务提交的结果,所以在不同时间的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读。

    3. REPEATABLE READ:可重复读,是 MySQL 的默认事务隔离级别,它能确保同⼀事务多次查询的结果⼀致,这就意味着,该级别的事务 A 正在执行,而另一个事务 B 成功插入了一条数据,但由于可重复读要保证每次查询的结果一致,所以 A 就会查询不到这条数据,但是 A 也想插入这条数据,却发现插入不了,这就是幻读。

    4. SERIALIZABLE:序列化,事务最⾼隔离级别,它会强制事务排序,使之不会发⽣冲突,从⽽解决了脏读、不可重复读和幻读问题,但因为执⾏效率低,所以真正使⽤的场景并不多。

     

    事务隔离级别脏读不可重复读幻读
    读未提交
    读已提交×
    可重复读××
    串行化×××

     

    ● 脏读:⼀个事务读取到了另⼀个事务修改的数据之后,后⼀个事务⼜进⾏了回滚操作,从⽽导致第⼀个事务读取的数据是错误的。

    ● 不可重复读:⼀个事务两次查询得到的结果不同,因为在两次查询中间,有另⼀个事务把数据修改了。

    ● 幻读:⼀个事务两次查询中得到的结果集不同,因为在两次查询中另⼀个事务有新增了⼀部分数据。

    不同的数据库的隔离级别是不一样的,MySQL 有四种,Oracle 有三种。Spring 的隔离级别为五种,是为了兼容不同的数据库。

    Spring 中事务隔离级别包含以下 5 种:

    1. Isolation.DEFAULT:以连接的数据库的事务隔离级别为主。

    2. Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读。

    3. Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重复读。

    4. Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读(MySQL默认级别)。

    ​​​​​​​

    5. Isolation.SERIALIZABLE:串⾏化,可以解决所有并发问题,但性能太低。

    如果 Spring 的隔离级别与 MySQL 设置的隔离级别不同的时候,以 Spring 设置的隔离级别为主。

    Spring 相当于一个客户端,设置了,以 Spring 设置的为主,没有设置时,跟连接的数据库的隔离级别一样,也就是 DEFAULT。

    隔离级别枚举的值以 2 的次方,性能更高。

    2.4 事务发生了异常,也不回滚的情况

    异常被捕获时

    1. package com.example.demo.controller;
    2. import com.example.demo.model.UserInfo;
    3. import com.example.demo.service.UserService;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.transaction.annotation.Transactional;
    6. import org.springframework.web.bind.annotation.RequestMapping;
    7. import org.springframework.web.bind.annotation.RestController;
    8. @RequestMapping("/user")
    9. @RestController
    10. public class UserController {
    11. @Autowired
    12. private UserService userService;
    13. @RequestMapping("/add")
    14. @Transactional
    15. public int add(){
    16. // 1. 非空判断
    17. UserInfo userInfo = new UserInfo();
    18. userInfo.setUsername("莉丝");
    19. userInfo.setPassword("6363");
    20. // 2. 调用 service 执行添加
    21. int result = userService.add(userInfo);
    22. System.out.println("result:"+result);
    23. try{
    24. int num = 10/0;
    25. }catch (Exception e){
    26. }
    27. // 3. 将结果给前端
    28. return 0;
    29. }
    30. }

     

    @Transactional 是AOP,出现异常的时候,代理对象可以感知到异常,并进行事务的回滚。但如果异常被捕获了,外部的代理对象就感知不到了,就不会进行事务的回滚了。

    对此有两种解决方案。

    1. 将异常继续抛出去,让代理对象感知到异常,也就能自动的回滚事务了。

    1. package com.example.demo.controller;
    2. import com.example.demo.model.UserInfo;
    3. import com.example.demo.service.UserService;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.transaction.annotation.Transactional;
    6. import org.springframework.web.bind.annotation.RequestMapping;
    7. import org.springframework.web.bind.annotation.RestController;
    8. @RequestMapping("/user")
    9. @RestController
    10. public class UserController {
    11. @Autowired
    12. private UserService userService;
    13. @RequestMapping("/add")
    14. @Transactional
    15. public int add(){
    16. // 1. 非空判断
    17. UserInfo userInfo = new UserInfo();
    18. userInfo.setUsername("埃罗尔");
    19. userInfo.setPassword("9146");
    20. // 2. 调用 service 执行添加
    21. int result = userService.add(userInfo);
    22. System.out.println("result:"+result);
    23. try{
    24. int num = 10/0;
    25. }catch (Exception e){
    26. throw e;
    27. }
    28. // 3. 将结果给前端
    29. return 0;
    30. }
    31. }

    2.  使用代码,手动回滚事务

    1. package com.example.demo.controller;
    2. import com.example.demo.model.UserInfo;
    3. import com.example.demo.service.UserService;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.transaction.annotation.Transactional;
    6. import org.springframework.transaction.interceptor.TransactionAspectSupport;
    7. import org.springframework.web.bind.annotation.RequestMapping;
    8. import org.springframework.web.bind.annotation.RestController;
    9. @RequestMapping("/user")
    10. @RestController
    11. public class UserController {
    12. @Autowired
    13. private UserService userService;
    14. @RequestMapping("/add")
    15. @Transactional
    16. public int add(){
    17. // 1. 非空判断
    18. UserInfo userInfo = new UserInfo();
    19. userInfo.setUsername("埃罗尔");
    20. userInfo.setPassword("9146");
    21. // 2. 调用 service 执行添加
    22. int result = userService.add(userInfo);
    23. System.out.println("result:"+result);
    24. try{
    25. int num = 10/0;
    26. }catch (Exception e){
    27. TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    28. }
    29. // 3. 将结果给前端
    30. return 0;
    31. }
    32. }

     在网页没有报错的情况下,进行了事务的回滚。

    3 事务的传播机制 

    3.1 定义

    Spring 事务传播机制定义了多个包含了事务的⽅法,相互调⽤时,事务是如何在这些⽅法间进⾏传递的。

    3.2 为什么需要事务传播机制

    事务隔离级别是保证多个并发事务执⾏的可控性的(稳定性的),⽽事务传播机制是保证⼀个事务在多个调⽤⽅法间的可控性的(稳定性的)。

    8

     

    3.3 事务传播机制种类

    Propagation.REQUIRED 

    默认的事务传播级别,如果当前存在事务,就加入这个事务(加入意味着成为这个外部事务的一部分),如果不存在事务,则会自己创建一个新的事务。与其他被调用的事务,要么一起提交事务,要么一起回滚事务。

    1. package com.example.demo.controller;
    2. import com.example.demo.model.UserInfo;
    3. import com.example.demo.service.UserService;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.transaction.annotation.Transactional;
    6. import org.springframework.transaction.interceptor.TransactionAspectSupport;
    7. import org.springframework.web.bind.annotation.RequestMapping;
    8. import org.springframework.web.bind.annotation.RestController;
    9. @RequestMapping("/user")
    10. @RestController
    11. public class UserController {
    12. @Autowired
    13. private UserService userService;
    14. @RequestMapping("/add")
    15. @Transactional
    16. public int add(){
    17. // 1. 非空判断
    18. UserInfo userInfo = new UserInfo();
    19. userInfo.setUsername("埃罗尔");
    20. userInfo.setPassword("9146");
    21. // 2. 调用 service 执行添加
    22. int result = userService.add(userInfo);
    23. System.out.println("result:"+result);
    24. try{
    25. int num = 10/0;
    26. }catch (Exception e){
    27. TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    28. }
    29. // 3. 将结果给前端
    30. return 0;
    31. }
    32. }
    1. package com.example.demo.service;
    2. import com.example.demo.mapper.UserMapper;
    3. import com.example.demo.model.UserInfo;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.stereotype.Service;
    6. import org.springframework.transaction.annotation.Transactional;
    7. @Service
    8. public class UserService {
    9. @Autowired
    10. private UserMapper userMapper;
    11. @Transactional
    12. public int add(UserInfo userInfo){
    13. int result = userMapper.add(userInfo);
    14. System.out.println("add result -> " + result);
    15. insert(userInfo);
    16. return result;
    17. }
    18. @Transactional
    19. public int insert(UserInfo userInfo){
    20. int result = userMapper.add(userInfo);
    21. System.out.println("insert result -> " + result);
    22. int num = 10 / 0;
    23. return result;
    24. }
    25. }

    add 调用两个加了@Transactional 的 service 的方法。

     Propagation.SUPPORTS 

    如果当前存在事务(存在于别的方法开启的事务中,而不是自己启动事务),则加入该事务;如果没有,则以非事务的方式继续运行。

    修改 insert 方法如下,添加 propagation 参数:

    1. package com.example.demo.controller;
    2. import com.example.demo.model.UserInfo;
    3. import com.example.demo.service.UserService;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.transaction.annotation.Propagation;
    6. import org.springframework.transaction.annotation.Transactional;
    7. import org.springframework.transaction.interceptor.TransactionAspectSupport;
    8. import org.springframework.web.bind.annotation.RequestMapping;
    9. import org.springframework.web.bind.annotation.RestController;
    10. @RequestMapping("/user")
    11. @RestController
    12. public class UserController {
    13. @Autowired
    14. private UserService userService;
    15. @RequestMapping("/add")
    16. @Transactional(propagation = Propagation.SUPPORTS)
    17. public int add(){
    18. // 1. 非空判断
    19. UserInfo userInfo = new UserInfo();
    20. userInfo.setUsername("埃罗尔");
    21. userInfo.setPassword("9146");
    22. // 2. 调用 service 执行添加
    23. int result = userService.add(userInfo);
    24. System.out.println("result:"+result);
    25. try{
    26. int num = 10/0;
    27. }catch (Exception e){
    28. // throw e;
    29. TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    30. }
    31. // 3. 将结果给前端
    32. return 0;
    33. }
    34. }

     对于上述 add 来说,它并没有存在于任何一个事务当中,所以它按照非事务的方式运行。

    1. package com.example.demo.service;
    2. import com.example.demo.mapper.UserMapper;
    3. import com.example.demo.model.UserInfo;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.stereotype.Service;
    6. import org.springframework.transaction.annotation.Propagation;
    7. import org.springframework.transaction.annotation.Transactional;
    8. @Service
    9. public class UserService {
    10. @Autowired
    11. private UserMapper userMapper;
    12. @Transactional(propagation = Propagation.SUPPORTS)
    13. public int add(UserInfo userInfo){
    14. int result = userMapper.add(userInfo);
    15. System.out.println("add result -> " + result);
    16. insert(userInfo);
    17. return result;
    18. }
    19. @Transactional(propagation = Propagation.SUPPORTS)
    20. public int insert(UserInfo userInfo){
    21. int result = userMapper.add(userInfo);
    22. System.out.println("insert result -> " + result);
    23. int num = 10 / 0;
    24. return result;
    25. }
    26. }

    service 中, add 被 controller 调用,由于 controller 不存在事务,因此 add 也不存在于任何事务中,所以 add 按照非事务的方式运行,同样对于 insert 来说也是如此,按照非事务的方式运行。

    运行结果是:添加了两条信息:

     

    如果 controller 的 add 的事务传播机制改成 Propagation.REQUIRED ,其他两个方法不变,结果会怎么样呢?

    1. package com.example.demo.controller;
    2. import com.example.demo.model.UserInfo;
    3. import com.example.demo.service.UserService;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.transaction.annotation.Propagation;
    6. import org.springframework.transaction.annotation.Transactional;
    7. import org.springframework.transaction.interceptor.TransactionAspectSupport;
    8. import org.springframework.web.bind.annotation.RequestMapping;
    9. import org.springframework.web.bind.annotation.RestController;
    10. @RequestMapping("/user")
    11. @RestController
    12. public class UserController {
    13. @Autowired
    14. private UserService userService;
    15. @RequestMapping("/add")
    16. @Transactional(propagation = Propagation.REQUIRED)
    17. public int add(){
    18. // 1. 非空判断
    19. UserInfo userInfo = new UserInfo();
    20. userInfo.setUsername("埃罗尔");
    21. userInfo.setPassword("9146");
    22. // 2. 调用 service 执行添加
    23. int result = userService.add(userInfo);
    24. System.out.println("result:"+result);
    25. try{
    26. int num = 10/0;
    27. }catch (Exception e){
    28. // throw e;
    29. TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    30. }
    31. // 3. 将结果给前端
    32. return 0;
    33. }
    34. }

     Propagation.MANDATORY

    mandatory 强制性的意思。如果当前存在事务,则加入该事务;如果没有事务,则抛出异常。

    Propagation.REQUIRES_NEW

    表示创建⼀个新的事务,如果当前存在事务,则把当前事务挂 起。也就是说不管外部⽅法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部⽅法会新开 启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。

    Propagation.NOT_SUPPORTED

    以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。

    Propagation.NEVER

    以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。

    1. package com.example.demo.controller;
    2. import com.example.demo.model.UserInfo;
    3. import com.example.demo.service.UserService;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.transaction.annotation.Propagation;
    6. import org.springframework.transaction.annotation.Transactional;
    7. import org.springframework.transaction.interceptor.TransactionAspectSupport;
    8. import org.springframework.web.bind.annotation.RequestMapping;
    9. import org.springframework.web.bind.annotation.RestController;
    10. @RequestMapping("/user")
    11. @RestController
    12. public class UserController {
    13. @Autowired
    14. private UserService userService;
    15. @RequestMapping("/add")
    16. @Transactional(propagation = Propagation.REQUIRED)
    17. public int add(){
    18. // 1. 非空判断
    19. UserInfo userInfo = new UserInfo();
    20. userInfo.setUsername("埃罗尔");
    21. userInfo.setPassword("9146");
    22. // 2. 调用 service 执行添加
    23. int result = userService.add(userInfo);
    24. System.out.println("result:"+result);
    25. try{
    26. int num = 10/0;
    27. }catch (Exception e){
    28. // throw e;
    29. TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    30. }
    31. // 3. 将结果给前端
    32. return 0;
    33. }
    34. }
    1. package com.example.demo.service;
    2. import com.example.demo.mapper.UserMapper;
    3. import com.example.demo.model.UserInfo;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.stereotype.Service;
    6. import org.springframework.transaction.annotation.Propagation;
    7. import org.springframework.transaction.annotation.Transactional;
    8. @Service
    9. public class UserService {
    10. @Autowired
    11. private UserMapper userMapper;
    12. @Transactional(propagation = Propagation.NEVER)
    13. public int add(UserInfo userInfo){
    14. int result = userMapper.add(userInfo);
    15. System.out.println("add result -> " + result);
    16. insert(userInfo);
    17. return result;
    18. }
    19. @Transactional(propagation = Propagation.NEVER)
    20. public int insert(UserInfo userInfo){
    21. int result = userMapper.add(userInfo);
    22. System.out.println("insert result -> " + result);
    23. int num = 10 / 0;
    24. return result;
    25. }
    26. }

     

    运行结果: 

     不添加任何一条信息。

    Propagation.NESTED

    如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏;如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED。

    嵌套的事务不影响外部的事务。NESTED 嵌套 NESTED ,一个完蛋,所有的 NESTED 都完蛋。

    1. package com.example.demo.controller;
    2. import com.example.demo.model.UserInfo;
    3. import com.example.demo.service.UserService;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.transaction.annotation.Propagation;
    6. import org.springframework.transaction.annotation.Transactional;
    7. import org.springframework.transaction.interceptor.TransactionAspectSupport;
    8. import org.springframework.web.bind.annotation.RequestMapping;
    9. import org.springframework.web.bind.annotation.RestController;
    10. @RequestMapping("/user")
    11. @RestController
    12. public class UserController {
    13. @Autowired
    14. private UserService userService;
    15. @RequestMapping("/add")
    16. @Transactional(propagation = Propagation.REQUIRED)
    17. public int add(){
    18. // 1. 非空判断
    19. UserInfo userInfo = new UserInfo();
    20. userInfo.setUsername("玛丽莲·梦露");
    21. userInfo.setPassword("18");
    22. // 2. 调用 service 执行添加
    23. int result = userService.add(userInfo);
    24. System.out.println("result:"+result);
    25. userService.insert(userInfo);
    26. // 3. 将结果给前端
    27. return 0;
    28. }
    29. }
    1. package com.example.demo.service;
    2. import com.example.demo.mapper.UserMapper;
    3. import com.example.demo.model.UserInfo;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.stereotype.Service;
    6. import org.springframework.transaction.annotation.Propagation;
    7. import org.springframework.transaction.annotation.Transactional;
    8. import org.springframework.transaction.interceptor.TransactionAspectSupport;
    9. @Service
    10. public class UserService {
    11. @Autowired
    12. private UserMapper userMapper;
    13. @Transactional(propagation = Propagation.NESTED)
    14. public int add(UserInfo userInfo){
    15. int result = userMapper.add(userInfo);
    16. System.out.println("add result -> " + result);
    17. return result;
    18. }
    19. @Transactional(propagation = Propagation.NESTED)
    20. public int insert(UserInfo userInfo){
    21. int result = userMapper.add(userInfo);
    22. System.out.println("insert result -> " + result);
    23. try {
    24. int num = 10 / 0;
    25. }catch (Exception e){
    26. TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    27. }
    28. return result;
    29. }
    30. }

    如果 insert 方法写成以下这种形式,不但 insert 感知到了异常,调用方感知到了异常,就会造成总体回滚,这就意味着数据无法添加。

    1. @Transactional(propagation = Propagation.NESTED)
    2. public int insert(UserInfo userInfo){
    3. int result = userMapper.add(userInfo);
    4. System.out.println("insert result -> " + result);
    5. int num = 10 / 0;
    6. return result;
    7. }

    而如果对 insert 进行手动回滚操作,这样只有 insert 感知到了异常,但是调用方并没有感知。

    为什么把 insert 放在 add 里,也会回滚。


  • 相关阅读:
    MySQL笔记-05 数据表操作
    高数中值定理总结
    股票量化交易接口的功能逻辑
    paddlepaddle
    使用爬虫代码获得深度学习目标检测或者语义分割中的图片。
    小米miui全机型代码对照与各机型发布时间表 了解小米机型发展历程
    【JS】牛客专项练习02
    详细讲解23种设计模式
    PostgreSQL 查询语句大全
    Maven项目指定main方法配置
  • 原文地址:https://blog.csdn.net/qq_41233305/article/details/132696348