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 ...
随机推荐
- BZOJ1889 : Maximal
二分答案,判断是否存在合法方案使得每个数都不超过$mid$. 考虑网络流建图: $i$点的流量下限为$\max(a_i-mid,0)$,费用为$1$,故拆点进行限制. $i$向$i+1$.$S$向$i ...
- HTML(三)
html图像.绝对路径和相对路径 html图像 <img>标签可以在网页上插入一张图片,它是独立使用的标签,通过“src”属性定义图片的地址,通过“alt”属性定义图片加载失败时显示的文字 ...
- Mac使用Clion配置OpenGL
Mac使用Clion配置OpenGL 之前博主是用Xcode配置的openGL,但后来因为要与非Mac队友合作,于是大家决定统一使用Clion,博主于是再次配置开发环境,这里记录一下 预先准备 Mac ...
- elasticsearch服务安装采坑
笔者这里下载的elastic search,是5.3.0版本,下载地址:https://www.elastic.co/downloads/past-releases/elasticsearch-5-3 ...
- C#_02.10_基础一_.NET框架
C#_02.10_基础一_.NET框架 一.概念: .NET框架是一个多语言组件开发和执行环境,它提供了一个跨语言的统一编程环境. 解读: 1..net框架是一个编程环境, 2.可以进行多语言的开发和 ...
- CentOS使用Ubuntu的start-stop-daemon来启动守护进程
在CentOS下使用守护进程启动有/etc/init.d/functions文件下的daemon方法,但如果要使用Ubuntu下的start-stop-daemon方法也可以实现. 安装如下: # 下 ...
- ssh远程登陆看不到用户名和主机名
使用secure crt远程登陆,发现看不到用户名和主机名,如下图所示 解决方法 sudo vim /etc/passwd root:x:::root:/root:/bin/bash sshd:x:: ...
- Javascript 字符串(二)常用操作整理
一.js获取字符串的字节数 这个好使--- function getBytesLength(str) { // 在GBK编码里,除了ASCII字符,其它都占两个字符宽 return str.repla ...
- FPGA基础之逻辑单元(LE or LC)的基本结构
原帖地址: https://blog.csdn.net/a8039974/article/details/51706906/ 逻辑单元在FPGA器件内部,是完成用户逻辑的最小单元.逻辑单元在ALTER ...
- spring boot使用TestRestTemplate集成测试 RESTful 接口
这篇文章没什么技术含量,只是单纯的记录一下如何用TestRestTemplate访问受security保护的api,供以后查阅. @Slf4j @RunWith(SpringRunner.class) ...