赞
踩
回顾地址: 深入理解数据库事务(超详细)_数据库事务操作_Maiko Star的博客-CSDN博客
事务: 是一组操作的集合,是一个不可分割的工作单位,这些操作要么同时成功,要么同时失败
事务的操作
SpringBoot中的事务是指一组操作数据库的动作集合,事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做。
声明式事务管理建立在 AOP 之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行目标方法之后根据执行的情况提交或者回滚事务。声明式事务管理不需要入侵代码,通过 @Transactional 就可以进行事务的操作,推荐使用。
@Transactional
注解,表示该方法需要被事务管理。Propagation
属性指定事务的传播行为,默认是REQUIRED
,即如果当前存在事务,则加入该事务,如果没有事务,则新建一个事务。Isolation
属性指定事务的隔离级别,默认是数据库的默认隔离级别。readOnly
属性指定事务是否为只读,如果为只读,则在事务中不允许进行写操作,默认为false
。rollbackFor
属性指定哪些异常触发事务回滚,默认为RuntimeException
。示例代码:
- @Service
- @Transactional
- public class UserService {
- @Autowired
- private UserRepository userRepository;
-
- public User createUser(User user) {
- // 业务逻辑
- return userRepository.save(user);
- }
-
- public void updateUser(User user) {
- // 业务逻辑
- userRepository.save(user);
- }
- }
编程式事务管理使用 TransactionTemplate或者直接使用底层的 PlatformTransactionManager。对于编程式事务管理,spring 推荐使用 TransactionTemplate。
TransactionTemplate
手动控制事务的开始和提交/回滚。execute
方法内部执行需要被事务管理的业务逻辑。示例代码:
- @Service
- public class UserService {
- @Autowired
- private TransactionTemplate transactionTemplate;
-
- @Autowired
- private UserRepository userRepository;
-
- public User createUser(User user) {
- return transactionTemplate.execute(status -> {
- try {
- // 业务逻辑
- return userRepository.save(user);
- } catch (Exception e) {
- status.setRollbackOnly();
- throw e;
- }
- });
- }
-
- public void updateUser(User user) {
- transactionTemplate.execute(status -> {
- try {
- // 业务逻辑
- userRepository.save(user);
- } catch (Exception e) {
- status.setRollbackOnly();
- throw e;
- }
- return null;
- });
- }
- }
@Fransactional 可以用来修饰方法或类:
参数 | 作用 |
---|---|
value | 当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。 |
transactionManager | 当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。 |
propagation | 事务的传播行为,默认值为 Propagation.REQUIRED。 |
isolation | 事务的隔离级别,默认值为 Isolation.DEFAULT。 |
timeout | 事务的超时时间,默认值为-1(表示没有超时时间)。如果超过该超时时间显示但事务还没有完成,则自动回滚事务。 |
readOnly | 指定事务是否为只读事务,默认值为 false。为了忽略那些不需要事务的方法,比如读取数据,可以设置为 true。 |
rollbackFor | 用于指定能够被触发事务回滚的异常类型,可以指定多个异常类型。 |
rollbackForClassName | 用于指定能够被触发事务回滚的异常类型,可以指定多个异常类型。 |
noRollbackFor | 抛出异常的类型,不回滚事务,也可以指定多个异常类型。 |
noRollbackForClassName | 抛出异常的类型,不回滚事务,也可以指定多个异常类型。 |
参数名称功能描述readOnly该属性用于设置当前事务是否为只读模式,设置为 true 表示只读,false 表示可读可写,默认情况下是 false。
例如: @Transactional(readOnly = true)rollbackFor该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。
例如:
指定单一异常类:@Transactional(rollbackFor = Exception.class)
指定多个异常类:@Transactional(rollbackFor ={RuntimeException.class,Exception.class})rollbackForClassName该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。
例如:
指定单一异常类名:@Transactional(rollbackForClassName = "RuntimeException")
指定多个异常类名:@Transactional(rollbackForClassName = {"RuntimeException","Exception"})noRollbackFor该属性用于设置不需要进行回滚的异常类数组。使用方法同 rollbackFornoRollbackForClassName该属性用于设置不需要进行回滚的异常类名称数组。使用方法同 rollbackForClassNamepropagation该属性用于设置事务的传播行为。isolation该属性用于设置事务的隔离级别。timeout该属性用于设置事务的超时秒数,默认值为 -1 表示永不超时。
1.@Transactional 只能放在 public 修饰的方法上。
2.@Transactional,不加任何参数时,默认会回滚运RuntimeException 及其子类,其它范围之外的异常 Spring 不会帮我们去回滚数据。
3.@Transactional(rollbackFor = Exception.class),如果加上rollbackFor 参数,会回滚所指定的异常类,前提下一定要在catch中抛出相关异常类,否则事务还是失效的。
4.@Transactional 在异常被捕获的情况下,不会进行事务的自动回滚。
默认情况下,Spring 中的事务如果遇到运行时异常,事务是会进行回滚的,但遇到非运行时异常,事务不会自动回滚。可以设置 rollbackFor 来解决非运行时异常不会被回滚的问题。示例代码如下:
- @RequestMapping("/test3")
- @Transactional
- public String test3(@RequestParam String username, @RequestParam String pwd) {
- // 插⼊数据库
- int result = userService.addUser(username, pwd);
- try {
- // 执⾏了异常代码
- int i = 10 / 0;
- } catch (Exception e) {
-
-
- }
以上代码虽然出现了算数异常,但是由于主动捕获了,且没有进行抛出,因此不会进行事物的回滚,数据库中会插入该条数据。
如果要解决出现异常事务不能自动回滚的问题,以下提供两种解决方案:
(1)方案一:对于捕获的异常,事务是不会自动回滚的,因此可以在捕获异常后主动将该异常重新抛出。默认只回滚RunTimeException,故我们这里演示抛出RunTimeException,示例代码如下:
- @RequestMapping("/test3")
- @Transactional
- public String test3(@RequestParam String username, @RequestParam String pwd) {
- // 插⼊数据库
- int result = userService.addUser(username, pwd);
- try {
- // 执⾏了异常代码
- int i = 10 / 0;
- } catch (Exception e) {
- // 将异常重新抛出
- throw new RunTimeException();
- }
- return "测试完成!";
- }
方案二(推荐):手动去回滚事务,可以通过方法 TransactionAspectSupport.currentTransactionStatus()
得到当前的事务,然后设置回滚方法 setRollbackOnly
就可以实现回滚。示例代码如下:
- @RequestMapping("/test3")
- @Transactional
- public String test3(@RequestParam String username, @RequestParam String pwd) {
- // 插⼊数据库
- int result = userService.addUser(username, pwd);
- try {
- // 执⾏了异常代码
- int i = 10 / 0;
- } catch (Exception e) {
- // 手动回滚
- TransactionStatus transactionStatus = TransactionAspectSupport.currentTransactionStatus();
- transactionStatus.setRollbackOnly();
- }
- return "测试完成!";
- }
使用了TransactionAspectSupport类来获取当前的事务状态,并通过调用setRollbackOnly()方法手动回滚事务。这是Spring框架中一种手动回滚事务的方式。
问题:方法上添加了 @Transactional 注解,为什么没有进行回滚?
排查一:是否使用了 try catch 进行了异常捕获,并且捕获之后,没有通过 throw new RuntimeException(); 进行异常抛出。因为对于 spring aop 异常捕获原理,被拦截的方法需要显示的抛出异常,并不能进行任何处理,这样 aop 代理才能捕获到方法的异常,才能进行事务的回滚操作;默认清空下,aop 只捕获 RuntimeException 的异常,但是可以通过配置来捕获特定的异常并回滚。
排查二:是否使用了 try catch 进行了异常信息捕获,如果是可以在 catch 语句中添加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(),手动回滚事务
排查三:排查是不是产生了自调用的问题。在 spring 的 aop 代理下,只有目标方法被外部调用,目标方法才由 spring 生成的代理对象来管理;若统一类中的其他没有用 @Transactional 注解进行修饰的方法内部调用了 用 @Transactional 注解进行修饰的方法,有 @Transactional 注解的方法的事务将会被忽略,不发生回滚。(周氏概括:即没有使用@Transactional 注解修饰的方法调用了用 @Transactional 注解进行修饰的方法,将会导致事务失效)
备注:如果确实需要这样操作,只需要把 @Transactional 注解加在当前的类名上就可以了 或者使用 AspectJ 取代 spring aop 进行代理。
排查四:@Transactional 注解只被应用到 public 修饰的方法上;如果在 protected、private等修饰的方法上,@Transactional 注解不会报错,但是这个注解的将不会生效。
排查五:@Transactional 注解不会对当前修饰的方法的处理异常的子方法生效。比如:我们在方法 A 中声明了 @Transactional 注解,但是 A 方法的内部调用的 方法 B 和 方法 C,其中方法 B 进行了 数据库的操作,但是该部分的异常被方法 B 进行了处理并且没有进行 抛出,这样的话事务是不会生效的。如果想要事务生效,需要将子方法的事务控制交给调用的方法,在子方法中使用 @Transactional注解并通过 rollbackFor 指定定回滚的异常 或者直接将异常抛出。(周氏概括:使用@Transactional 注解修饰的A方法 调用了 没有用@Transactional 注解修饰的B方法,但是B方法使用try catch处理了异常,这将导致事务失效。使用@Transactional 注解修饰的A方法 调用了 没有用@Transactional 注解修饰的B方法,但是B方法将异常向上抛出或使用@Transactional注解并通过 rollbackFor 指定定回滚的异常,事务不会失效。)
备注:在使用事务的时候,最好把子方法的异常进行抛出,交给调用的方法进行处理。
Spring 事务传播机制定义了多个包含了事务的方法在相互调用时,事务是如何在这些方法之间进行传递的。
嵌套事务和加入事务的区别:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。