Java所有的异常都有一个共同的祖先类ThrowableThrowable类有两个重要的子类Exception和Error
    Exception属于程序本身可以处理的异常,可以通过catch来进行捕获。Exception 又可以分为受检查异常CheckException和不受检查异常即RuntimeException

    • CheckException:受检查代码比如IO操作没有被 catch/throw 处理的话,就没办法通过编译 ,有IOExceptionClassNotFoundExceptionSQLException等。
    • RuntimeException:即使不处理不受检查异常也可以正常通过编译。RuntimeException及其子类都统称为非受检查异常,有空指针异常NullPointerException、字符串转换为数字异常NumberFormatException、数组越界异常ArrayIndexOutOfBoundsException、类型转换异常ClassCastException、算术异常ArithmeticException

    Error属于程序无法处理的错误无法通过catch来进行捕获 ,例如Java 虚拟机运行错误Virtual MachineError、虚拟机内存不够错误OutOfMemoryError、类定义错误NoClassDefFoundError等 。当Error发生时,JVM一般会选择线程终止
    image.png
    Spring事务只有遇到RuntimeExceptionError时才会回滚,但是在遇到检查型异常CheckException时不会回滚。
    事务回滚策略rollbackFor - 图2 :::info 错误的异常处置 ::: 被try…catch捕获的事务不会回滚,比如:

    1. @Slf4j
    2. @Service
    3. public class UserService {
    4. @Transactional
    5. public void add(UserModel userModel) {
    6. try {
    7. saveData(userModel);
    8. updateData(userModel);
    9. } catch (Exception e) {
    10. log.error(e.getMessage(), e);
    11. }
    12. }
    13. }

    即使开发者没有手动捕获异常,但如果抛的异常不正确Spring事务也不会回滚。
    因为Spring事务默认情况下只会回滚RuntimeException和Error,对于RuntimeException的父类Exception默认并不会回滚。

    1. @Slf4j
    2. @Service
    3. public class UserService {
    4. @Transactional
    5. public void add(UserModel userModel) throws Exception {
    6. try {
    7. saveData(userModel);
    8. updateData(userModel);
    9. } catch (Exception e) {
    10. log.error(e.getMessage(), e);
    11. throw new Exception(e);
    12. }
    13. }
    14. }

    声明异常与业务异常不匹配,Spring事务也不会回滚。
    在执行下面这段代码保存和更新数据时,程序报错了,抛了SqlException、DuplicateKeyException等异常,报错异常不属于自定义异常BusinessException,所以事务也不会回滚。因此即使rollbackFor有默认值,但阿里巴巴开发者规范中,还是要求开发者重新指定该参数。因为如果使用默认值,一旦程序抛出了Exception,事务不会回滚,这会出现很大的bug。所以,建议一般情况下,将该参数设置成:ExceptionThrowable

    1. @Slf4j
    2. @Service
    3. public class UserService {
    4. @Transactional(rollbackFor = BusinessException.class)
    5. public void add(UserModel userModel) throws Exception {
    6. saveData(userModel);
    7. updateData(userModel);
    8. }
    9. }

    :::info 正确的异常处理 ::: 忽略声明式注解@Transactional,手动硬编码TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()开启Spring事务管理

    1. @Override
    2. @Transactional
    3. public Json addOrder(TOrderAddReq tOrderAddReq) {
    4. try{
    5. //增删改方法
    6. } catch (Exception e) {
    7. // 手动硬编码开启spring事务管理
    8. TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    9. e.printStackTrace();}
    10. // }
    11. return json;
    12. }

    Spring的事务边界是在调用业务方法之前开始的,业务方法执行完毕之后Spring事务来执行commit or rollback

    • 一般不需要在业务方法中catch异常,运行时异常RuntimeException正常抛出,事务会回滚。
    • 如果非要catch的话一定要抛出throw new RuntimeException(),否则Spring会将你的catch业务操作commit,这样就会产生脏数据

      1. @Override
      2. @Transactional
      3. public Json addOrder(TOrderAddReq tOrderAddReq) {
      4. try{
      5. //增删改方法
      6. } catch (Exception e) {
      7. //catch业务
      8. throw new RuntimeException();
      9. }
      10. return json;
      11. }

      为了使所有异常都触发事务控制,可以将回滚策略rollbackFor配置为Exception.class或者Throwable.class,最大化Spring事务识别能力

    • @Transactional(rollbackFor = Exception.class)

    • @Transactional(rollbackFor = Throwable.class)