问题现象比较奇怪,具体描述如下假如有三个业务接口 ServiceA 、ServiceB和ServiceC, 其中 ServiceA 中有一个方法实现如下 /** 
* 事务属性配置为 PROPAGATION_REQUIRED 
*/ 
void methodA() { 
    //其他业务操作    // 调用 ServiceB 的方法 
    try{
        ServiceB.methodB(); 
    }catch(Exception e){
    }    //其他业务操作
} ServiceB的methodB方法实现如下:/** 
* 事务属性配置为 PROPAGATION_NESTED 
*/ 
void methodB(){
    ServiceC.methodC();
}ServiceC的methodC方法实现如下:/** 
* 事务属性配置为 PROPAGATION_REQUIRED 
*/ 
void methodC(){
    //直接抛出异常
    throw new RuntimeException();
}
此时调用methodA,则methodA执行结束spring框架代码提交事务时报如下异常:
[2018-02-24 17:02:29,444] (GlobalExceptionHandler.java:handle:35) [ERROR]: 运行时异常:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been ed as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:720)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:521)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:291)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)但我觉得外部事务应当可以成功提交,不应当抛出异常(内部事务methodB出异常后不应当影响外部事务methodA的提交,因为methodB是嵌套事务,并且在methodA中进行了try/catch)
奇怪的是如果将methodB的实现改为:/** 
* 事务属性配置为 PROPAGATION_NESTED 
*/ 
void methodB(){
    //ServiceC.methodC();
    //不再调用methodC,直接抛异常
    throw new RuntimeException();
}则最外层事务methodA可以成功提交,不知道为什么多了一层methodC调用就会抛出异常。
静等大神提示~

解决方案 »

  1.   

    在哪个进行catch?我在methodA中已经进行try/catch了
      

  2.   

    在B中捕获一下serviceC.methodC试试
      

  3.   

    就像这样:
    /** 
    * 事务属性配置为 PROPAGATION_REQUIRED 
    */ 
    void methodC(){
    try {
    //直接抛出异常
    throw new RuntimeException();
    } catch (Exception e) {
    System.out.println("C抛出了异常");
    }
    }
      

  4.   

    不能在B中捕获serviceC.methodC,因为methodB出异常后我希望能够回滚,捕获后就不能回滚了
      

  5.   

    就像这样:
    /** 
    * 事务属性配置为 PROPAGATION_REQUIRED 
    */ 
    void methodC(){
    try {
    //直接抛出异常
    throw new RuntimeException();
    } catch (Exception e) {
    System.out.println("C抛出了异常");
    }
    }这个方法的异常我需要throw出去,不能这样catch住
      

  6.   

    spring的事务管理最好不要try catch,如果try catch也得抛出异常
    你这个异常大概原因是,C抛出异常,AOP已经标记事务要回滚,但异常到A却被捕获,代码继续执行,然后提交事务,就会抛出该异常
      

  7.   

    对,从异常信息看原因是这样的,但是aop标记要回滚的事务是methodB这个内层的嵌套事务,methodA捕获了异常,methodA去提交事务时不应当影响methodB事务的回滚才对。
    像在题目中描述的那样,如果在methodB中直接抛出异常而不调用methodC,则methodA可以提交成功,想不通为什么
      

  8.   


    那得看看那spring事务传播机制配的什么了
      

  9.   


    那得看看那spring事务传播机制配的什么了methodA配置的REQUIRED,methodB配置的NESTED,methodC配置的REQUIRED,大神请进一步指导
      

  10.   

    因为a b c三个方法用的都是同一个连接,c异常的时候事务状态的rollback-only就被标注为true了
      

  11.   

    是这样的,跟到源码debug了下是这个原因
      

  12.   

    你把方法后上都加上throws Exception,这样你说的第二种情况应该不会提交成功
      

  13.   

    你可以捕获异常后手动回滚B中的事物。B用独立的transaction,手动控制。