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@Servicepublic class UserService {@Transactionalpublic void add(UserModel userModel) {try {saveData(userModel);updateData(userModel);} catch (Exception e) {log.error(e.getMessage(), e);}}}
即使开发者没有手动捕获异常,但如果抛的异常不正确Spring事务也不会回滚。
因为Spring事务默认情况下只会回滚RuntimeException和Error,对于RuntimeException的父类Exception默认并不会回滚。
@Slf4j@Servicepublic class UserService {@Transactionalpublic 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@Servicepublic class UserService {@Transactional(rollbackFor = BusinessException.class)public void add(UserModel userModel) throws Exception {saveData(userModel);updateData(userModel);}}
:::info
正确的异常处理
:::
忽略声明式注解@Transactional,手动硬编码TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()开启Spring事务管理
@Override@Transactionalpublic 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@Transactionalpublic 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)
