前言
最近项目中发生了一个事务异常:UnexpectedRollBackException,然后重温一下Spring的事务管理方面的知识
**
Spring事务捕获到异常以后的操作
**
首先是Spring的事务机制,当一个方法开启事务后,如果代码中发生了异常后,将会进入
TransactionAspectSupport中,代码大概如下
捕获异常后经过一些处理后交给TransactionAspectSupport.completeTransactionAfterThrowing方法处理,在图中2处判断该异常是否要进行回滚,默认情况下运行时异常回滚,非运行时异常不回滚直接提交,然后在图3处进行事务回滚
具体的事务回滚操作是在上图中所示代码进行的,首先判断该事务是否是一个新的事务,如果是一个新事务,则直接回滚,如果不是新的事务,则打上回滚标记,如下图所示
新事务是指新起的事务,如果是加入的别的方法的事务,则不为新事务
注意,如果异常发生在try…catch代码块中,将不会执行上述流程
如果该方法所起的不是外层事务,则捕获到的异常会向上抛给上层事务、
commit时候的操作
只有最外层事务在代码执行成功后(没有异常,或者异常被try…catch了,或者出现的异常为非运行时异常)才会commit
commit时主要执行的代码有这两个分支,当该事务完全执行成功,并且嵌套事务也完全成功时,会直接commit(图中2)
但是如果内层事务出现异常打上全局回滚标志以后,外层事务用try…catch捕获了内层事务抛出的异常,并在catch里没有继续向外抛出,在外层事务成功执行后commit时,就会走到上图中的1所示位置,这个时候就会回滚,并且抛出异常
throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");
避免UnexpectedRollbackException
由此可见发生UnexpectedRollbackException的主要原因就是,内存事务发生异常后打上了全局回滚标记,而外层事务通过try catch捕获以后进行了不处理的操作,所以要避免UnexpectedRollbackException的话,
1.外层事务调用内层事务时,不能忽略内层事务抛出的异常
2.把两个方法分别起两个事务,分别commit,同一事务嵌套内,一旦打上全局回滚标记,那么最外层事务commit时必然会进行回滚操作