UnexpectedRollbackException会覆盖我自己的异常

Yon*_*oni 5 java spring transactions

我对spring的事务管理有以下奇怪的情况:

我有方法A,它调用方法B,它调用方法C,每个方法都在不同的类中.方法B和C都包含事务.两者都使用PROPAGATION_REQUIRED,因此当spring创建两个逻辑事务时,db中有一个物理事务.

现在,在方法CI中抛出一个RuntimeException.这将内部逻辑事务设置为rollbackOnly和物理事务.在方法B中,我知道UnexpectedRollbackException的可能性,所以我不进行正常提交.我从C中捕获异常,然后抛出另一个RuntimeException.

我希望外部RuntimeException将导致回滚到外部事务,但实际行为是这样的:

  • 外部事务似乎尝试提交,或者至少检查其状态,然后它抛出UnexpectedRollbackException,因为物理事务已经标记为rollbackOnly.
  • 抛出该异常之前,它会向日志打印另一个异常,指出"由提交异常覆盖的应用程序异常".因此,调用者A接收UnexpectedRollbackException,而不是B抛出的异常.

我找到了一个解决方法,即在抛出异常之前将外部事务主动设置为回滚

public ModelAndView methodB(HttpServletRequest req, HttpServletResponse resp) {
  try{
    other.methodC();
  } catch (RuntimeException e){
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    throw new RuntimeException ("outer exception");
  }
  return handleGetRequest(req, resp);
}
Run Code Online (Sandbox Code Playgroud)

但是,这种解决方法强烈地将代码与事务api结合在一起,我想避免这种情况.有什么建议?

ps这两个事务都是为了回滚运行时异常.我没有定义任何rollbackFor异常或类似的东西

Yon*_*oni 7

我找到了这个问题的原因.事实证明,在包装事务之前,methodB被一个基于cglib的代理(使用spring old方式,pre 2.0)包装.因此,当我从methodB抛出RuntimeException时,cglib最终会抛出一个InvocationTargetException,这实际上是一个已检查的异常.

Spring的事务管理器最终捕获已检查的异常并尝试提交事务,而不知道methodB抛出的嵌套运行时异常.一旦我发现了这一点,我就设置了事务包装器以回滚已检查的异常,现在它按预期工作.


wax*_*wax 5

您可以尝试在继承者中设置failEarlyOnGlobalRollbackOnly标志(例如).这是它的描述:trueAbstractPlatformTransactionManagerHibernateTransactionManager

设置是否在事务全局标记为仅回滚的情况下提前失败.

默认值为"false",仅在最外层的事务边界处导致UnexpectedRollbackException.早在第一次检测到全局回滚标记时,即使在内部事务边界内,也会切换此标志以导致UnexpectedRollbackException.注意,从Spring 2.0开始,全局回滚标记的失败早期行为已经统一:默认情况下,所有事务管理器只会在最外层的事务边界处导致UnexpectedRollbackException.例如,即使在操作失败并且事务永远不会完成之后,这也允许继续单元测试.如果此标志已明确设置为"true",则所有事务管理器只会提前失败.