本文首发于 http://www.YoungZY.com/
项目中遇到了这样的异常:
1
|
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
|
项目概要
调用逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// 最外层 Service public class MainService { public void executeAAA() { try { ... nestedService.executeBBB(); ... } catch (Exception e) { // 更新操作日志表 updateTblOperationLog(e); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 |
pulblic class NestedService { public void executeBBB() { try { ... nestedNestedService.sendMsg(); ... } catch (Exception e) { throws new BusinessException(e); } } } |
1 2 3 4 5 6 7 8 9 |
pulblic class NestedNestedService { public void sendMsg() throws BusinessException { try { ... } catch (Exception e) { throws new BusinessException(e); } } } |
事务配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<aop:config proxy-target-class="true"> <aop:pointcut id="servicePointcut" expression="execution(* com.XXX..*service..*(..))" /> <aop:advisor pointcut-ref="servicePointcut" advice-ref="txAdvice" order="1" /> </aop:config> <tx:annotation-driven transaction-manager = "transactionManager" /> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="save*" propagation="REQUIRED" rollback-for="BusinessException"/> <tx:method name="add*" propagation="REQUIRED" rollback-for="BusinessException"/> <tx:method name="insert*" propagation="REQUIRED" rollback-for="BusinessException"/> <tx:method name="update*" propagation="REQUIRED" rollback-for="BusinessException"/> <tx:method name="delete*" propagation="REQUIRED" rollback-for="BusinessException"/> <tx:method name="validate*" propagation="REQUIRED" rollback-for="BusinessException"/> <tx:method name="calculate*" propagation="REQUIRED" rollback-for="BusinessException"/> <tx:method name="execute*" propagation="REQUIRED" rollback-for="BusinessException"/> <tx:method name="find*" read-only="true" /> <tx:method name="get*" read-only="true" /> <tx:method name="load*" read-only="true" /> <tx:method name="*" read-only="true"/> </tx:attributes> </tx:advice> |
出现这种没碰过的问题,第一反应当然就是 Google 了。
翻了很多 Blog 和 论坛,也尝试了很多办法,但都失败了。
尝试
原因已经找到:通过查看日志,知道问题是出现在调用 nestedNestedService.sendMsg()
时。由于对方服务不通,导致这个方法出现异常。
尝试1
总的来说就是事务嵌套的问题。网上说可以为每一个方法都起一个事务,修改事务配置文件:
1
|
<tx:method name="execute*" propagation="REQUIRED" rollback-for="BusinessException"/>
|
改为
1
|
<tx:method name="execute*" propagation="REQUIRES_NEW" rollback-for="BusinessException"/>
|
这个不行,还是会报一样的错误。
尝试2
考虑到出错的那个方法 sendMsg()
不在事务的预定义里面,属于 *
的范畴。
1
|
<tx:method name="*" read-only="true"/>
|
改成开启独立事务的模式(挂起前一个,当前方法新起一个事务):
1
|
<tx:method name="*" propagation="REQUIRES_NEW"/>
|
不行。
改成不用事务的,因为该用事务的前面定义的差不多了:
1
|
<tx:method name="*" propagation="NOT_SUPPORTED"/>
|
还是不行。报错说“事务是必须的”……
那就改成 SUPPORTS
的吧,意思是有事务就用,没有也没关系
1
|
<tx:method name="*" propagation="SUPPORTS"/>
|
还是不行。跟一开始一样的错。
*
不行,那就专门给以 send 开头的方法设置一个。
跟上面一样,尝试了 REQUIRES_NEW
, NOT_SUPPORTED
,都以失败告终。
解决办法
修改配置不行。那就只能从代码入手了。
看了这么多资料,做了这么多尝试,问题点差不多找到了:方法 nestedService.executeBBB()
抛出了 BusinessException 异常导致的。即使最外层的 Service 的 mainService.executeAAA()
做了 try-catch 也不行。
由于项目中的 Service 类里有很多 execute
开头的方法,所以不能随意的更改 rollback-for
配置。
事务里定义了如果遇到 BusinessException
就回滚。
怎样让它不回滚呢?
那就新增一个异常类吧。
- 新增异常类:
1
public class ServiceException extends Exception {}
- 修改 NestedNestedService 类
1 2 3 4 5 6 7 8 9 10 11
pulblic class NestedNestedService { public void sendMsg() throws Exception { try { ... } catch (Exception e) { //throws new BusinessException(e); // 直接 throw ,就不再封装了 throws e; } } }
- 修改 NestedService 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
pulblic class NestedService { public void executeBBB() { try { ... nestedNestedService.sendMsg(); ... } catch (Exception e) { // throws new BusinessException(e); // 换成 ServiceException 异常 throws new ServiceException(e); } } }
其实之前都是抱着试试看的态度。
问题就这样解决了。
加入讨论