Spring Boot 事物回滚

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot 事物回滚相关的知识,希望对你有一定的参考价值。

参考技术A Springboot中事务的使用:

1、启动类加上@EnableTransactionManagement注解,开启事务支持(其实默认是开启的)。

2、在使用事务的public(只有public支持事务)方法(或者类-相当于该类的所有public方法都使用)加上@Transactional注解。

在实际使用中一般是在service中使用@Transactional,那么对于controller->service流程中:

如果controller未开启事务,service中开始了事务,service成功执行,controller在之后的运行中出现异常(错误),不会自动回滚。

也就是说,只有在开启事务的方法中出现异常(默认只有非检测性异常才生效-RuntimeException )(错误-Error)才会自动回滚。

 如果想要对抛出的任何异常都进行自动回滚(而不是只针对RuntimeException),只需要在使用@Transactional(rollbackFor = Exception.class)即可。

开启事务的方法中事务回滚的情况:

①未发现的异常,程序运行过程中自动抛出RuntimeException或者其子类,程序终止,自动回滚。

②使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();进行手动回滚。

③注意:如果在try-catch语句中对可能出现的异常(RuntimeException)进行了处理,没有再手动throw异常,spring认为该方法成功执行,不会进行回滚,此时需要调用②中方法进行手动回滚 (java 框架项目案例:www.fhadmin.cn)

另外,如果try-catch语句在finally中进行了return操作,那么catch中手动抛出的异常也会被覆盖,同样不会自动回滚。

//不会自动回滚

try

    throw new RuntimeException();

catch(RuntimeException e)

    e.printStackTrace();

finally



//会自动回滚

try

    throw new RuntimeException();

catch(RuntimeException e)

    e.printStackTrace();

    throw new RuntimeException();

finally

SpringBoot事务注解@Transactional 事物回滚手动回滚事物

处理springboot 下提交事务异常,数据库没有回滚的问题。

spring的文档中说道,spring声明式事务管理默认对非检查型异常和运行时异常进行事务回滚,而对检查型异常则不进行回滚操作。

什么是检查型异常什么又是非检查型异常?
最简单的判断点有两个:
1.继承自runtimeexception或error的是非检查型异常,而继承自exception的则是检查型异常(当然,runtimeexception本身也是exception的子类)。

 

2.对非检查型类异常可以不用捕获,而检查型异常则必须用try语句块进行处理或者把异常交给上级方法处理总之就是必须写代码处理它。所以必须在service捕获异常,然后再次抛出,这样事务方才起效。

 

结论:

在spring的事务管理环境下,使用unckecked exception可以极大地简化异常的处理,只需要在事务层声明可能抛出的异常(这里的异常可以是自定义的unckecked exception体系),在所有的中间层都只是需要简单throws即可,不需要捕捉和处理,直接到最高层,比如UI层再进行异常的捕捉和处理。

 

 

默认规则:

1 让checked例外也回滚: @Transactional(rollbackFor=Exception.class),一般只需添加这个即可

2 让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)

3 不需要事务管理的(只查询的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED),或者不添加

 

注意: 如果异常被try{}catch{}了,事务就不回滚了,如果想让事务回滚必须再往外抛try{}catch{throw Exception}。因为一旦你try{}catch{}了。系统会认为你已经手动处理了异常,就不会进行回滚操作。

 

示例:

 

1 @Override  
2   @Transactional(rollbackFor = Exception.class)  
3   public Integer submitOrder(FlashbuyOrder flashbuyOrder, List<FlashbuyOrderItem> itemList)  
4       throws Exception  
5   {  
6   
7       return null;  
8   
9   }  
示例

 

 spring 事务控制 设置手动回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

https://www.cnblogs.com/liuzhenlei/p/6777644.html

 1  //假设这是一个service类的片段
 2   
 3  try{ 
 4      //出现异常
 5   } catch (Exception e) {
 6               e.printStackTrace();
 7             //设置手动回滚
 8               TransactionAspectSupport.currentTransactionStatus()
 9                      .setRollbackOnly();
10          }
11  //此时return语句能够执行
12  return  xxx;
View Code

如上:

  当我们需要在事务控制的service层类中使用try catch 去捕获异常后,就会使事务控制失效,因为该类的异常并没有抛出,就不是触发事务管理机制。怎样才能即使用try catch去捕获异常,而又让出现异常后spring回滚呢,这里就要用到

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

完美解决问题。并且能够使该方法执行完。

 

这个需要注意两点:

1. 方法上要加上    @Transactional(rollbackFor = Exception.class)   再配合TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 才可以,

否则会报错  NoTransactionException: No transaction aspect-managed TransactionStatus in scope
    at

@Transactional(propagation = Propagation.REQUIRES_NEW,
            rollbackFor = Exception.class, isolation = Isolation.REPEATABLE_READ)
    public void testInsert(int index) throws Exception {
        OrderDO orderDO = new OrderDO();
        orderDO.setThirdOrderId("1000");
        int rowCount = this.orderMapper.insertOrder(orderDO);
        if (index == 1) {
            throw new RuntimeException("wrong");
        }
        if (index == 2) {
            throw new Exception("wrong");
        }
        if (index == 3) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }
View Code

2.  Spring Transactional一直是RD的事务神器,但是如果用不好,反会伤了自己。下面总结@Transactional经常遇到的几个场景:

@Transactional 加于private方法, 无效
@Transactional 加于未加入接口的public方法, 再通过普通接口方法调用, 无效
@Transactional 加于接口方法, 无论下面调用的是private或public方法, 都有效
@Transactional 加于接口方法后, 被本类普通接口方法直接调用, 无效
@Transactional 加于接口方法后, 被本类普通接口方法通过接口调用, 有效
@Transactional 加于接口方法后, 被它类的接口方法调用, 有效
@Transactional 加于接口方法后, 被它类的私有方法调用后, 有效
View Code

Transactional是否生效, 仅取决于是否加载于接口方法, 并且是否通过接口方法调用(而不是本类调用)。

https://segmentfault.com/a/1190000014617571

 

 ProductService.java
/**********************************************************************/
public interface ProductService{
    Integer getPrice(ProductInfo p);
    Integer compute(ProductInfo p);
}
/**********************************************************************/
 
 
ProductServiceImpl.java
/**********************************************************************/
@Service
public class ProductServiceImpl implements ProductService{
    @Autowired
    private ProductService productService;
    
    public Integer getPrice(ProductInfo p){
        productService.compute(p);
    }
 
    @Transactional(rollbackFor = Exception.class)
    public Integer compute(ProductInfo p){ 
        try{
            ...
        }catch(Exception e){
             e.printStackTrace();
             TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
             return 0;
        }
    }
}
 
是可以的,因为是通过接口调用了
View Code
 ProductService.java
/**********************************************************************/
public interface ProductService{
    Integer getPrice(ProductInfo p);
  
}
 
public interface MyService{
  
    Integer compute(ProductInfo p);
}
/**********************************************************************/
 
 
ProductServiceImpl.java
/**********************************************************************/
@Service
public class ProductServiceImpl implements ProductService{
    @Autowired
    private MyService   myService   ;
    
    public Integer getPrice(ProductInfo p){
        myService.compute(p);
    }
 
  
}
 
 
@Service
public class MyServiceImpl implements MyService{
   
 
    @Transactional(rollbackFor = Exception.class)
    public Integer compute(ProductInfo p){ 
        try{
            ...
        }catch(Exception e){
             e.printStackTrace();
             TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
             return 0;
        }
    }
}
 
放在两个接口类里面,然后通过接口方法调用,是可以生效的
View Code

 

3.在web项目中,很多时候要用到@Transactional 注解方法或者类进行事务处理,自动事务提交有时候就会有问题,这个时候就要用到手动进行事务提交 ,在try catch 异常抛出里面手动回滚事务处理TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

使用Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint(); 设置回滚点,

使用TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);回滚到savePoint。


转自:

https://blog.csdn.net/zhuchunyan_aijia/article/details/80191534

https://blog.csdn.net/xuhaogang3/article/details/82190026

 

以上是关于Spring Boot 事物回滚的主要内容,如果未能解决你的问题,请参考以下文章

spring全注解事务管理中怎么手动回滚事物

Spring事务管理----事物回滚

spring事物回滚机制 (事务异常回滚,捕获异常不抛出就不会回滚)

Spring boot,测试后事务不回滚

Spring Boot @Transcational 没有回滚

spring 嵌套事务问题