Spring事务默认回滚规则

Spring事务默认回滚规则

薛定谔的汪

背景

今天搭建公司SpringBoot基础框架时,在设计自定义异常阶段,参考其他框架优秀源码发现都是继承了 RuntimeException,为何不直接继承 Exception 呢?特此记录一下这样做的好处。

Exception

我们知道异常分为两类:检查异常CheckedException 和运行时异常RuntimeException

CheckedException

Java认为Checked异常都是可以被处理的异常,所以Java程序必须显式的处理Checked异常,如果程序没有处理checked异常,程序在编译时候将发生错误。

我们比较熟悉的Checked异常有 :

java.lang.ClassNotFoundException

java.lang.NoSuchMetodException

java.io.IOException 

RunTimeException

Runtime如除数是0和数组下标越界等,其产生频繁,处理麻烦,若显示申明或者捕获将会对程序的可读性和运行效率影响很大。所以由系统自动检测并将它们交给缺省的异常处理程序。当然如果你有处理要求也可以显示捕获它们。

我们比较熟悉的RumtimeException类的子类有:

java.lang.ArithmeticException

java.lang.ArrayStoreExcetpion

java.lang.ClassCastException

java.lang.IndexOutOfBoundsException

java.lang.NullPointerException 

自定义异常

在框架中我自定了个异常,方便异常处理维护,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Getter
@Setter
public class CustomException extends RuntimeException {
private static final long serialVersionUID = 1L;

private String msg;
private int code = 500;

public CustomException(String msg) {
super(msg);
this.msg = msg;
}

public CustomException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}

public CustomException(String msg, int code) {
super(msg);
this.msg = msg;
this.code = code;
}

public CustomException(String msg, int code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}
}

自定义异常继承自 RuntimeException,这样有两个好处:

1.方法中抛出此自定义异常,调用此方法的方法不用显示地 throws 该异常。

2.Spring 事务回滚默认只支持 RuntimeException。

Spring 事务默认回滚规则

我们在使用Spring时候一般都知道事务在遇到异常的时候会回滚,岂不知Spring的事务默认只有在发生运行时异常即:RunTimeException时才会发生事务,如果一个方法抛出Exception或者Checked异常Spring的事务并不会回滚。 

查看 Spring 注解@Transactional源码:

1
2
3
4
5
6
/**
* <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}.
* @see #rollbackForClassName
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
*/
Class<? extends Throwable>[] rollbackFor() default {};

通过注释可以知道RollbackRuleAttribute类是配置 Spring事务回滚规则的类,查看此类源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class RollbackRuleAttribute implements Serializable{


public static final RollbackRuleAttribute ROLLBACK_ON_RUNTIME_EXCEPTIONS =
new RollbackRuleAttribute(RuntimeException.class);

private final String exceptionName;

public RollbackRuleAttribute(Class<?> clazz) {
Assert.notNull(clazz, "'clazz' cannot be null");
if (!Throwable.class.isAssignableFrom(clazz)) {
throw new IllegalArgumentException(
"Cannot construct rollback rule from [" + clazz.getName() + "]: it's not a Throwable");
}
this.exceptionName = clazz.getName();
}

public String getExceptionName() {
return exceptionName;
}
......
}

通过源码我们知道 Spring默认是只对 RuntimeException 进行回滚,这就要求我们自定义异常的时候,让其继承 RuntimeException,这样异常抛出时才会被Spring默认的事务回滚规则处理。

spring的原则之一就是基层异常就应该是非检查性异常. 原因如下:

  1. 基层异常通常来说是不可恢复的。 
  2. 检查性异常将会降低异常层次结构的价值.如果底层异常是检查性的, 那么就需要在所有地方添加catch语句进行捕获。

 

如何改变 Spring 默认事务规则?

在@Transaction注解中定义noRollbackForRollbackFor指定某种异常是否回滚。

@Transaction(noRollbackFor=RuntimeException.class)

@Transaction(RollbackFor=Exception.class) 

  • Title: Spring事务默认回滚规则
  • Author: 薛定谔的汪
  • Created at : 2018-06-22 18:01:54
  • Updated at : 2023-11-17 19:37:37
  • Link: https://www.zhengyk.cn/2018/06/22/spring/Rollback-rule/
  • License: This work is licensed under CC BY-NC-SA 4.0.