Java所有的异常都有一个共同的祖先类Throwable
,Throwable
类有两个重要的子类Exception和Error
Exception属于程序本身可以处理的异常,可以通过catch来进行捕获。Exception 又可以分为受检查异常CheckException
和不受检查异常即RuntimeException
CheckException
:受检查代码比如IO操作没有被 catch/throw 处理的话,就没办法通过编译 ,有IOException
、ClassNotFoundException
、SQLException
等。RuntimeException
:即使不处理不受检查异常也可以正常通过编译。RuntimeException及其子类都统称为非受检查异常,有空指针异常NullPointerException
、字符串转换为数字异常NumberFormatException
、数组越界异常ArrayIndexOutOfBoundsException
、类型转换异常ClassCastException
、算术异常ArithmeticException
等
Error属于程序无法处理的错误无法通过catch来进行捕获 ,例如Java 虚拟机运行错误Virtual MachineError
、虚拟机内存不够错误OutOfMemoryError
、类定义错误NoClassDefFoundError
等 。当Error发生时,JVM一般会选择线程终止
Spring事务只有遇到RuntimeException
和Error
时才会回滚,但是在遇到检查型异常CheckException
时不会回滚。
:::info
错误的异常处置
:::
被try…catch捕获的事务不会回滚,比如:
@Slf4j
@Service
public class UserService {
@Transactional
public void add(UserModel userModel) {
try {
saveData(userModel);
updateData(userModel);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
即使开发者没有手动捕获异常,但如果抛的异常不正确Spring事务也不会回滚。
因为Spring事务默认情况下只会回滚RuntimeException和Error,对于RuntimeException的父类Exception默认并不会回滚。
@Slf4j
@Service
public class UserService {
@Transactional
public void add(UserModel userModel) throws Exception {
try {
saveData(userModel);
updateData(userModel);
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new Exception(e);
}
}
}
声明异常与业务异常不匹配,Spring事务也不会回滚。
在执行下面这段代码保存和更新数据时,程序报错了,抛了SqlException、DuplicateKeyException等异常,报错异常不属于自定义异常BusinessException,所以事务也不会回滚。因此即使rollbackFor有默认值,但阿里巴巴开发者规范中,还是要求开发者重新指定该参数。因为如果使用默认值,一旦程序抛出了Exception,事务不会回滚,这会出现很大的bug。所以,建议一般情况下,将该参数设置成:Exception
或Throwable
@Slf4j
@Service
public class UserService {
@Transactional(rollbackFor = BusinessException.class)
public void add(UserModel userModel) throws Exception {
saveData(userModel);
updateData(userModel);
}
}
:::info
正确的异常处理
:::
忽略声明式注解@Transactional
,手动硬编码TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
开启Spring事务管理
@Override
@Transactional
public Json addOrder(TOrderAddReq tOrderAddReq) {
try{
//增删改方法
} catch (Exception e) {
// 手动硬编码开启spring事务管理
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
e.printStackTrace();}
// }
return json;
}
Spring的事务边界是在调用业务方法之前开始的,业务方法执行完毕之后Spring事务来执行commit or rollback
。
- 一般不需要在业务方法中catch异常,运行时异常
RuntimeException
正常抛出,事务会回滚。 如果非要catch的话一定要抛出
throw new RuntimeException()
,否则Spring会将你的catch业务操作commit,这样就会产生脏数据@Override
@Transactional
public Json addOrder(TOrderAddReq tOrderAddReq) {
try{
//增删改方法
} catch (Exception e) {
//catch业务
throw new RuntimeException();
}
return json;
}
为了使所有异常都触发事务控制,可以将回滚策略
rollbackFor
配置为Exception.class
或者Throwable.class
,最大化Spring事务识别能力@Transactional(rollbackFor = Exception.class)
@Transactional(rollbackFor = Throwable.class)