欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 艺术 > 【JavaEE】Spring(9):Spring事务

【JavaEE】Spring(9):Spring事务

2025/2/8 1:54:09 来源:https://blog.csdn.net/m0_74270127/article/details/145413115  浏览:    关键词:【JavaEE】Spring(9):Spring事务


一、Spring实现事务

这里说的事务就是在数据库中提到的事务,本篇主要学习如何使用Spring实现事务

1.1 Spring声明式事务 @Transactional

通过@Transactional来操作事务

1. 添加依赖:

<dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId>
</dependency>

2. 在需要的方法上添加 @Transactional ,这样在进入方法时就会开启事务,方法执行完成后自动提交事务,如果在方法执行的过程中出现异常,且该异常没有被捕获就会触发事务回滚

以代码为例

@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@Transactional@RequestMapping("/registry")public String registry(String name, String password) throws IOException {//用户注册userService.registryUser(name, password);log.info("用户数据插入成功");//强制程序抛出异常int a = 10/0;return "注册成功";}
}

我们在上述代码中故意写了异常,此时运行程序,就会触发事务回滚;虽然日志中显示用户数据插入成功,但实际上并未插入成功

@Transactional的作用

@Transactional可以修饰类和方法:

  • 修饰 public 方法时 @Transactional才会生效,如果修饰的不是public方法则不会生效,但也不会报错
  • 修饰类时,该类的所有public方法都会生效

前面说过程序中抛出异常,且异常没有被捕获,则会触发事务回滚

@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@Transactional@RequestMapping("/registry")public String registry(String name, String password) throws IOException {//用户注册userService.registryUser(name, password);log.info("用户数据插入成功");try {int a = 10/0;} catch (Exception e) {e.printStackTrace();}return "注册成功";}
}

此时再运行程序,事务就会提交成功,不会触发事务回滚

那如果异常被捕获后,仍然想触发事务回滚该怎么办,有两种解决方案:

1. 重新抛出异常

try {int a = 10/0;
} catch (Exception e) {throw e;
}

2. 手动回滚事务

try {int a = 10/0;
} catch (Exception e) {TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}

通过 TransactionAspectSupport.currentTransactionStatus() 获取到当前事务,然后通过setRollbackOnly();回滚事务

二、 @Transactional 的三大属性

2.1 rollbackFor

rollbackFor可以指定触发事务回滚的异常类型,@Transactional默认只有运行时异常Error时才会触发事务回滚,当抛出非运行时异常时,不会触发事务回滚

修改代码,使其抛出IOException(非运行时异常):

@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@Transactional@RequestMapping("/registry")public String registry(String name, String password) throws IOException {//用户注册userService.registryUser(name, password);log.info("用户数据插入成功");if (true) {throw new IOException();}return "注册成功";}
}

运行程序,虽然抛出异常,但是事务提交成功

如果面对IOException这种非运行时异常,也想触发事务回滚,就可以通过配置rollbackFor来完成

@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@Transactional(rollbackFor = Exception.class)@RequestMapping("/registry")public String registry(String name, String password) throws IOException {//用户注册userService.registryUser(name, password);log.info("用户数据插入成功");if (true) {throw new IOException();}return "注册成功";}
}

修改成上述代码后,程序中只要抛出Exception及其子类的异常,都会触发事务回滚

2.2 isolation

通过isolations属性可以设置事务的隔离级别

Spring中事务隔离级别有5种:

  • Isolation.DEFAULT:以连接的数据库的事务隔离级别为主
  • Isolation.READ_UNCOMMITTED:读未提交,对应SQL标准中 READ UNCOMMITTED
  • Isolation.READ_COMMITTED:读已提交,对应SQL标准中 READ COMMITTED
  • Isolation.REPEATABLE_READ:可重复读,对应SQL标准中 REPEATABLE READ 
  • Isolation.SERIALIZABLE:串行化,对应SQL标准中 SERIALIZABLE
     
@Transactional(isolation = Isolation.READ_COMMITTED);

2.3 事务传播机制

2.3.1 什么是事务传播机制

事务传播机制:当多个事务方法存在调用关系时,比如方法A的执行过程中需要调用方法B,此时方法B是加入A的事务还是创建自己的事务?事务传播机制就是处理这样的问题的

2.3.2 有哪些事务传播机制

  • Propagation.REQUIRED:默认的事务传播级别,如果当前存在事务,则加入该事务,如果没有则新建一个事务
  • Propagation.SUPPORTS:如果当前存在事务,则加入该事务,如果没有则以非事务的方式运行
  • Propagation.MANDATORY:如果当前存在事务,则加入该事务,如果没有则抛出异常
  • Propagation.REQUIRES_NEW:无论当前有没有事务,都新建一个事务,如果当前有事务则将当前这个事务挂起(不用)
  • Propagation.NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务,则将当前的事务挂起(不用)
  • Propagation.NEVER:以非事务方式运行,如果当前存在事务,则抛出异常
  • Propagation.NESTED:如果当前存在事务,则新建一个事务作为当前事务的嵌套事务来运行,如果当前没有事务,则该取值等价于Propagation.REQUIRED

2.3.3 事务传播机制代码演示

Propagation.REQUIRED

用户注册

@RequestMapping("/propaga")
@RestController
public class PropagationController {@Autowiredprivate UserService userService;@Autowiredprivate LogService logService;@Transactional(propagation = Propagation.REQUIRED)@RequestMapping("/p1")public String r3(String name,String password){//用户注册userService.registryUser(name,password);//记录操作⽇志logService.insertLog(name,"用户注册");return "r3";}
}

用户注册包含插入用户数据和记录日志两个操作:

@Slf4j
@Service
public class UserService {@Autowiredprivate UserInfoMapper userInfoMapper;@Transactional(propagation = Propagation.REQUIRED)public void registryUser(String name,String password){//插⼊⽤⼾信息userInfoMapper.insert(name,password);}
}
@Slf4j
@Service
public class LogService {@Autowiredprivate LogInfoMapper logInfoMapper;@Transactional(propagation = Propagation.REQUIRED)public void insertLog(String name,String op){int a=10/0;//记录⽤⼾操作logInfoMapper.insertLog(name,"⽤⼾注册");}
}

运行数据库,发现没有插入数据
流程描述:
1. p1方法开始事务
2. 用户注册,插入一条数据,执行成功(和p1使用同⼀个事务)
3. 记录操作日志,插⼊⼀条数据,出现异常,执行失败(和p1使用同⼀个事务)
4. 因为步骤3出现异常,事务回滚,步骤2和3使用同⼀个事务,所以步骤2的数据也回滚了
最终所有操作都没有执行成功

REQUIRES_NEW
将UserService和LogService中的方法的事务传播机制改为REQUIRES_NEW:

@Slf4j
@Service
public class UserService {@Autowiredprivate UserInfoMapper userInfoMapper;@Transactional(propagation = Propagation.REQUIRES_NEW)public void registryUser(String name,String password){//插⼊⽤⼾信息userInfoMapper.insert(name,password);}
}
@Slf4j
@Service
public class LogService {@Autowiredprivate LogInfoMapper logInfoMapper;@Transactional(propagation = Propagation.REQUIRES_NEW)public void insertLog(String name,String op){int a=10/0;//记录⽤⼾操作logInfoMapper.insertLog(name,"⽤⼾注册");}
}

用户数据插入成功,日志数据插入失败,因为这两个方法都新建了事务,LogService事务抛出异常不影响UserService事务

NESTED

@Slf4j
@Service
public class UserService {@Autowiredprivate UserInfoMapper userInfoMapper;@Transactional(propagation = Propagation.NESTED)public void registryUser(String name,String password){//插⼊⽤⼾信息userInfoMapper.insert(name,password);}
}
@Slf4j
@Service
public class LogService {@Autowiredprivate LogInfoMapper logInfoMapper;@Transactional(propagation = Propagation.NESTED)public void insertLog(String name,String op){int a=10/0;//记录⽤⼾操作logInfoMapper.insertLog(name,"⽤⼾注册");}
}

运行程序,发现没有数据插入

流程描述:
1. Controller中p1方法开始事务
2. UserService用户注册,插入⼀条数据(嵌套p1事务)
3. LogService 记录操作日志,插⼊⼀条数据,出现异常,执行失败(嵌套p1事务,回滚当前事务,数据添加失败)
4. 由于是嵌套事务,LogService出现异常之后,往上找调⽤它的⽅法和事务,所以用户注册也失败了
5. 最终结果是两个数据都没有添加

p1事务可以认为是父事务,嵌套事务是子事务,父事务出现异常,子事务也会回滚,子事务出现异常,如果不进行处理,也会导致父事务回滚
 

NESTED可以实现部分事务回滚(捕获异常后,手动事务回滚):

@Service
public class LogService {@Autowiredprivate LogInfoMapper logInfoMapper;@Transactional(propagation = Propagation.NESTED)public void insertLog(String name,String op){try {int a=10/0;} catch (Exception e){//回滚当前事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}//记录⽤⼾操作logInfoMapper.insertLog(name,"⽤⼾注册");}
}

此时就不会影响父事务


🙉本篇文章到此结束

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com