Transaction rolled back because it has been marked as rollback-only分析解决方法
1.
Transaction rolled back because it has been marked as rollback-only
事务已回滚,因为它被标记成了只回滚
<prop key="query*">PROPAGATION_REQUIRED,readOnly</prop>
query开头的方法readOnly,所以只能select,抛出异常,insert/update/delete操作必然回滚
2.
发现selectA调用selectB,如果selectB抛出Exception,selectA中捕获Exception但是并不继续向外抛出,最后会出现错误。
纠其原理其实很简单,在selectB返回的时候,transaction被设置为rollback-only了,但是selectA正常消化掉,没有继续向外抛。
那么selectA结束的时候,transaction会执commit操作,但是 transaction已经被设置为 rollback-only了。
所以会出现这个错误。
有的同学说了,那不是没得搞了,service不能抛出异常,或者不能拦截异常了?
其实不然,其实错误不在这里,而是select这种操作为什么要启动事务呢?
3.demo示例代码
1.applicationContext.xml配置事务
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- <tx:method name="sendIllegalMessage" read-only="false" rollback-for="Exception" propagation="REQUIRES_NEW" /> -->
<tx:method name="get*" read-only="true" />
<tx:method name="find*" read-only="true" />
<tx:method name="load*" read-only="true" />
<tx:method name="query*" read-only="true" />
<tx:method name="add*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
<tx:method name="batchAdd*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
<tx:method name="save*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
<tx:method name="insert*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
<tx:method name="update*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
<tx:method name="modify*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
<tx:method name="delete*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
<tx:method name="del*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
<tx:method name="registe*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
<tx:method name="approve*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
<tx:method name="clear*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
<tx:method name="set*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
<tx:method name="reset*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
<tx:method name="getUpdate*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
<tx:method name="updatedQuery*" read-only="false" rollback-for="Exception" propagation="REQUIRES_NEW" />
<!-- <tx:method name="*" read-only="true"/> -->
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor pointcut="execution(* com.xxx.service..*Service.*(..))" advice-ref="txAdvice"/>
<aop:advisor pointcut="execution(* com.xxx.v30.service..*Service.*(..))" advice-ref="txAdvice"/>
<aop:advisor pointcut="execution(* com.xxx.v31.service..*Service.*(..))" advice-ref="txAdvice"/>
<aop:advisor pointcut="execution(* com.xxx.v33.service..*Service.*(..))" advice-ref="txAdvice"/>
<aop:advisor pointcut="execution(* com.xxx.v34.service..*Service.*(..))" advice-ref="txAdvice"/>
<aop:advisor pointcut="execution(* com.xxx.limitCoupon.service..*Service.*(..))" advice-ref="txAdvice"/>
<aop:advisor pointcut="execution(* com.xxx.v35.service..*Service.*(..))" advice-ref="txAdvice"/>
<aop:advisor pointcut="execution(* com.xxx.v36.service..*Service.*(..))" advice-ref="txAdvice"/>
<aop:advisor pointcut="execution(* com.xxx.auth.*Service.*(..))" advice-ref="txAdvice"/>
<aop:advisor pointcut="execution(* com.xxx.notify.*Service.*(..))" advice-ref="txAdvice"/>
</aop:config>
2.junit测试代码
@Test
public void testCancelTask2(){
try {
transService.updateTransCancel2();
} catch (Exception e) {
e.printStackTrace();
}
}
public void updateTransCancel2() {
int upCount = transMapper.updateTransCancelStat(520657512071l, 1, 0, 0,-1,-1,-1,-1,-1,-1);
try {
cancelTransSendSms.cancelTransSendSms2();
} catch (Exception e) {
e.printStackTrace();
}
logger.info("upCount="+upCount);
}
public void cancelTransSendSms2() throws Exception{
aotoCancel2();
}
private void aotoCancel2() {
txtMap=smsConverUtil.getMessage(smsParamsMap, "RenterNoAuthDeposite", "RenterNoAuthDeposite0000");
}
public Map<String,String> getMessage(Map<String,Object> smsParamsMap,String smsContentKey,String pushKey){
Map<String,String> map=new LinkedHashMap<String, String>();
String smsContent="";
String jpushContent="";
String smsMessage="";
String flag="";
logger.info("in rentNo->smsContentKey is {}",smsContentKey);
if(StringUtils.isNotBlank(smsContentKey)){
smsContent=getContent(smsParamsMap,smsContentKey);
smsMessage=smsMsgDescMap.get(smsContentKey);
}
if(StringUtils.isNotBlank(pushKey)){
jpushContent=getPushContentTemplate(pushKey,smsParamsMap);
flag=pushMsgFlagMap.get(pushKey);
}
map.put("smsContent",smsContent);
map.put("jpushContent",jpushContent);
map.put("smsMessage",smsMessage);
map.put("flag",flag);
return map;
}
private String getPushContentTemplate(String contentKey,Map<String,Object> contentParamMap){
try {
String templateContent = operationService.getTemplateMsgByAppTypeAndCode(AppTypeConstant.JPUSH, contentKey);
if(StringUtils.isEmpty(templateContent)){
return null;
}
return replaceTemplateContent(templateContent,contentParamMap);
} catch (Exception e) {
logger.error("推送消息获取消息内容报错!",e);
}
return null;
}
public String getTemplateMsgByAppTypeAndCode(String appType, String textCode) {
try {
return operationTextCache.getUpdateOperateTextMsgByAppTypeAndTextCode(appType, textCode);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public String tgetTemplateMsgByAppTypeAndCode(String appType, String textCode) {
return operationTextCache.tfindOperateTextMsgByAppTypeAndTextCode(appType, textCode);
}
public String findOperateTextMsgByAppTypeAndTextCode(String appType,
String textCode) {
OperationText operationText = this.findOperationTextByAppTypeAndTextCode(appType, textCode);
if (operationText==null) {
throw new IllegalArgumentException("appType:"+appType+","+"textCode:"+textCode+",不存在文本模板消息");
}else {
return operationText.getTextMsg();
}
} public String getUpdateOperateTextMsgByAppTypeAndTextCode(String appType,
String textCode) {
OperationText operationText = this.findOperationTextByAppTypeAndTextCode(appType, textCode);
if (operationText==null) {
throw new IllegalArgumentException("appType:"+appType+","+"textCode:"+textCode+",不存在文本模板消息");
}else {
return operationText.getTextMsg();
}
} public String tfindOperateTextMsgByAppTypeAndTextCode(String appType,
String textCode) {
OperationText operationText = this.findOperationTextByAppTypeAndTextCode(appType, textCode);
if (operationText==null) {
throw new IllegalArgumentException("appType:"+appType+","+"textCode:"+textCode+",不存在文本模板消息");
}else {
return operationText.getTextMsg();
}
}
4.汇总(A调用B)
4.1 A无事务,B无事务(将find,get改成tfind,tget方法名) A不回滚,不报以上错误。
4.2 A无事务,B get,find只读事务,但是不抛出throw new IllegalArgumentException("appType:"+appType+","+"textCode:"+textCode+",不存在文本模板消息"); A不回滚,不报以上错误。
4.3 A update事务,B get,find只读事务且抛出异常 (间隔捕获) A回滚,报以上错误。
4.4 A无事务,B get,find只读事务且抛出异常 (间隔捕获) A回滚,报以上错误。
4.5 A update事务,B update事务且抛出异常 (间隔捕获) A回滚,报以上错误。
4.6 A update事务,B update事务且抛出异常且try..catch..B A不回滚,不报以上错误。
4.6 A无事务,B update事务且抛出异常且try..catch..B A不回滚,不报以上错误。
简单而言之:
方法1有try,方法2无try,方法3 find或get throws A回滚,报以上错误。 捕获的异常有间隔有问题。
方法1有try,方法2有try,方法3 find或get throws A不回滚,不报以上错误。 在抛出异常的上一级方法捕获没有问题。
基于以上的情况说明:类1方法1无事务,类2方法2有事务get/find无捕获,类3方法3无事务 --->报rollback-only错误。
基于以上的情况说明:类1方法1无事务,类2方法2有事务get/find有捕获,类3方法3无事务 --->不报rollback-only错误。 上文说的间隔try
类1方法1无事务,类2方法2有事务get/find有无捕获,类3方法3有事务 --->报rollback-only错误。 被spring标记了rollback位,这就是为什么要REQUIRES_NEW事务了。
类1方法1无事务,类2方法2有事务updatedQuery新建事务有捕获,类3方法3有事务 --->不报rollback-only错误。
类1方法1无事务,类2方法2有事务updatedQuery新建事务无捕获,类3方法3有事务 --->报rollback-only错误。
Transaction rolled back because it has been marked as rollback-only分析解决方法的更多相关文章
- 【Spring】21、用spring目标对象处理Transaction rolled back because it has been marked as rollback-only
在使用spring做事务管理时,很多人都会遇到这样一段异常: org.springframework.transaction.UnexpectedRollbackException: Transact ...
- “Transaction rolled back because it has been marked as rollback-only”
spring的声明事务提供了强大功能,让我们把业务关注和非业务关注的东西又分离开了.好东西的使用,总是需要有代价的.使用声明事务的时候,一 个不小心经常会碰到“Transaction rolled b ...
- Transaction rolled back because it has been marked as rollback-only
出现这种错误的原因 1.接口A 调用了接口B 2.接口B报异常了,没有在B里面进行try catch捕获 3.接口A对 接口B进行了try catch捕获 因为接口B报异常 会把当前事物A接口的事物( ...
- 【springcloud】Transaction rolled back because it has been marked as rollback-only
问题: 一个ajax请求,发生系统错误,错误内容:Transaction rolled back because it has been marked as rollback-only 原因是调用的s ...
- [转]Spring事务嵌套引发的血案---Transaction rolled back because it has been marked as rollback-only
原文地址:https://blog.csdn.net/f641385712/article/details/80445912 1.概述 想必大家一想到事务,就想到ACID,或者也会想到CAP.但笔者今 ...
- Transaction rolled back because it has been marked as rollback-only 原因 和解决方案
产生原因 , 1 serviceA 调用 serviceB 然后 B 抛出异常 ,B 所在的 事物 回滚,B 把当前可写 事物标记成 只读事物 , 2 如果 A 和B 是在 同一个事物环境,并且 ...
- Spring事务嵌套引发的问题--Transaction rolled back because it has been marked as rollback-only
转载https://blog.csdn.net/f641385712/article/details/80445912 读了两边才找到问题
- 记一次org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only异常
@Transactional(rollbackFor = Exception.class) @Overridepublic DubboResult<Boolean> productAddO ...
- spring事务(Transaction )报 marked as rollback-only异常的原因及解决方法
很多朋友在使用spring+hibernate或mybatis等框架时经常遇到报Transaction rolled back because it has been marked as rollba ...
随机推荐
- dd制作linux启动盘
1.fdisk /dev/sdb 删除分区,新建分区 2.mkfs.vfat /dev/sdb1 3.dd bs=4M if=CentOS.iso of=/dev/sdb
- 常见的git清单
我们每天使用 Git ,但是很多命令记不住. 一般来说,日常使用只要记住下图6个命令,就可以了.但是熟练使用,恐怕要记住60-100个命令. 这篇文章是从别人博客上copy重新整理出来的,作为笔记用, ...
- 页面嵌套iframe后,点击里面的链接,然后父窗口跳转(子窗口控制父窗口的链接跳转)
做app的时候遇到一个问题,一个页面,然后里面嵌套了一个另一个页面,想实现点击里面的链接,然后外面进行跳转,不然的话,里面的页面永远出不来, 后面想了个办法,app的页面都是打开打开,不关闭的,然后由 ...
- HTML5 学习07——Video(视频)Audio(音频)
<video> 元素:提供了 播放.暂停和音量控件来控制视频. width 和 height 属性:控制视频的尺寸 <video> 与</video> 标签之间插入 ...
- Android的Databinding-单向绑定
两种方式实现观察绑定. 一.POJO类实现android.databinding.Observable,在属性set方法中调用notifyPropertyChanged(BR.lastName); 其 ...
- Java Lambda 表达式 对 Map 对象排序
Map<String,String> mailParams = new LinkedHashMap<>(); mailParams.put("Action" ...
- 正則表達式 - C语言
http://blog.csdn.net/pipisorry/article/details/37073843 sscanf/scanf正则使用方法 %[ ] 的使用方法:%[ ]表示要读入一个字符集 ...
- flink 有状态udf 引起血案一
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/rlnLo2pNEfx9c/article/details/83422587 场景 近期在做一个画像的 ...
- [Vuex] Create a Vuex Store using TypeScript
A Vuex store centralizes the state of your app, making it easy to reason about your state flow. In t ...
- [Java] 绕过证书验证调 HTTPS 接口时报 “SSLHandshakeException: DHPublicKey does not comply to algorithm constraints”的解决办法
作者: zyl910 一.缘由 最近有在对接一个无证书的HTTPS接口时,总是收到"SSLHandshakeException: DHPublicKey does not comply to ...