宁武皇仁光九年锦文轩刻本《异闻录》载: 扶桑画师浅溪,居泰安,喜绘鲤。院前一方荷塘,锦鲤游曳,溪常与嬉戏。 其时正武德之乱,潘镇割据,战事频仍,魑魅魍魉,肆逆于道。兵戈逼泰安,街邻皆逃亡,独溪不舍锦鲤,未去。 是夜,院室倏火。有人入火护溪,言其本鲤中妖,欲取溪命,却生情愫,遂不忍为之。翌日天明,火势渐歇,人已不见。 溪始觉如梦,奔塘边,但见池水干涸,莲叶皆枯,塘中鲤亦不知所踪。 自始至终,未辨眉目,只记襟上层迭莲华,其色魅惑,似血着泪。 后有青岩居士闻之,叹曰:魑祟动情,必作灰飞。犹蛾之投火耳,非愚,乃命数也。 ————《锦鲤抄》

【注解@Transactional 的事务回滚测试】

通常,在项目中可能看到注解@Transactional(rollbackFor=Exception.class),如果类加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚(也可以细分颗粒度,到需要事务监控的方法上添加此注解而非类上)。

在@Transactional注解中如果不配置rollbackFor属性,那么事物只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class,可以让事物在遇到非运行时异常时也回滚

默认spring事务只在发生未被捕获的 RuntimeExcetpion 时才回滚。也可以说spring会对unchecked异常进行事务回滚;如果是checked异常则不回滚。

一般将派生于Error或者RuntimeException的异常称为unchecked异常,所有其他的异常成为checked异常。

那么我是不是可以理解为 不加注解,发生空指针而不捕获时会发生回滚?(下面进行验证)

 

PS: @Transactional注解 应当应用于service层实现类或实现类中的public方法上,且如果为只读(查询)方法无需添加注解。

PSS:着重测试注解属性:rollbackFor :用于指定能够触发事务回滚的异常类型,可以指定多个异常类型,常见的runtime异常:空指针异常,数组越界异常

 

现将进行测试,测试的控制变量如下:

  1.添加注解@Transactional(rollbackFor = Exception.class)

  2.添加注解@Transactional(norollbackFor = Exception.class)

  3.捕获 runtime 异常

  4.不捕获 runtime 异常

  

 好了,开测:

  测试用例1:不添加@Transactional注解,测试异常为空指针异常,而捕获的是runtime异常,结果是 无回滚,无异常抛出,数据顺利更新。

   测试用例2:不添加@Transactional注解,测试异常为空指针异常,而捕获的是数组越界异常,结果是 无回滚,抛出空指针异常,数据可顺利更新。

  测试用例3:和测试用例2类似,但没有使用try-catch去捕获runtime异常,结果依旧是 没有回滚。

 

   测试用例4:添加@Transactional(rollbackFor = NullPointerException.class)注解,指定空指针类型异常,测试异常为空指针异常,而捕获的是数组越界异常,结果是 发生回滚, 数据无更新。

 

    测试用例5:添加@Transactional(rollbackFor = NullPointerException.class)注解,测试异常为空指针异常,而捕获的是Exception异常(包含空指针异常),结果是 不发生回滚, 数据有更新。

 

 

     测试用例6:添加@Transactional(norollbackFor = NullPointerException.class)注解,测试异常为空指针异常,而捕获的ArrayIndexOutOfBoundsException

异常(包含空指针异常),结果是 不发生回滚, 数据有更新。

 

 好了,以上列举了6种情况,总结如下:

  1.当没有配置注解@Transactional时,spring默认是不开启事物管理的。

  2.配置注解后,回滚的前提是,try-catch捕获情况符合注解中参数的规范,且方法用public修饰,要求mysql数据库的引擎是支持事物的比如InnoDB( 可以用show variables like 'default_storage_engine'语句来查看数据库当前引擎)

  3.配置注解后,如果catch中捕获到了对应异常,那么即使配置注解也不会发生回滚。

  3.再次声明,unchecked exception–编译器不要求强制处置的异常,可以理解为 java.lang.RuntimeException类及它的子类都是非受查异常,如错误的类型转换异常:ClassCastException,下标越界异常:ArrayIndexOutOfBoundsException,空指针访问异常:NullPointerException,除零溢出异常:ArithmeticException等;

  4.注解属性:

 

【关于事务嵌套】

有两个事务方法,父方法中调用子方法,想实现子方法回滚而父方法不触发回滚,可以设置如下:

  @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
  // 子方法触发事务时会暂时挂起父事务;

 

 

【关于同一类中非事务方法A调用 事务方法B,事务失效问题】

// 因为 B方法被A调用,而事务需要切面实现,这时对B方法的切入失效,我们需要新建一个代理对象去调用方法B,这样事务不会失效。(XxxxService为业务类对象)
XxxxxxService self = (XxxxxxxService) AopContext.currentProxy();

 需要依赖:

        <!-- 切点表达式依赖 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
            <scope>runtime</scope>
        </dependency>

 

 

附:

参考博客:https://www.cnblogs.com/clwydjgs/p/9317849.html

spring aop 异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeexception的异常,但可以通过配置来捕获特定的异常并回滚。

在Spring FrameWork 的事务框架中推荐的事务回滚方法是,在当前执行的事务上下文中抛出一个异常。如果异常未被处理,当抛出异常调用堆栈的时候,Spring FrameWork 的事务框架代码将捕获任何未处理的异常,然后并决定是否将此事务标记为回滚。

在默认配置中,Spring FrameWork 的事务框架代码只会将出现runtime, unchecked 异常的事务标记为回滚;也就是说事务中抛出的异常时RuntimeException或者是其子类,这样事务才会回滚(默认情况下Error也会导致事务回滚)。在默认配置的情况下,所有的 checked 异常都不会引起事务回滚。

注:Unchecked Exception包括Error与RuntimeException. RuntimeException的所有子类也都属于此类。另一类就是checked Exception。

 

Spring的事务管理默认是针对unchecked exception回滚,也就是默认对Error异常和RuntimeException异常以及其子类进行事务回滚,且必须对抛出异常,若使用try-catch对其异常捕获则不会进行回滚!(Error异常和RuntimeException异常抛出时不需要方法调用throws或try-catch语句);
checked异常,checked异常必须由try-catch语句包含或者由方法throws抛出,且事务默认对checked异常不进行回滚。
在service层若不加@Transactional则spring默认是不开启事物管理的。

从源码来看,默认回滚的 类型确实是runtime异常和error

 

 

 

posted @ 2021-12-19 18:04  哒布溜  阅读(1134)  评论(0编辑  收藏  举报