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分析解决方法的更多相关文章

  1. 【Spring】21、用spring目标对象处理Transaction rolled back because it has been marked as rollback-only

    在使用spring做事务管理时,很多人都会遇到这样一段异常: org.springframework.transaction.UnexpectedRollbackException: Transact ...

  2. “Transaction rolled back because it has been marked as rollback-only”

    spring的声明事务提供了强大功能,让我们把业务关注和非业务关注的东西又分离开了.好东西的使用,总是需要有代价的.使用声明事务的时候,一 个不小心经常会碰到“Transaction rolled b ...

  3. 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接口的事物( ...

  4. 【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 ...

  5. [转]Spring事务嵌套引发的血案---Transaction rolled back because it has been marked as rollback-only

    原文地址:https://blog.csdn.net/f641385712/article/details/80445912 1.概述 想必大家一想到事务,就想到ACID,或者也会想到CAP.但笔者今 ...

  6. Transaction rolled back because it has been marked as rollback-only 原因 和解决方案

    产生原因  , 1 serviceA 调用 serviceB 然后 B  抛出异常 ,B 所在的 事物 回滚,B 把当前可写 事物标记成 只读事物 , 2 如果 A 和B 是在 同一个事物环境,并且 ...

  7. Spring事务嵌套引发的问题--Transaction rolled back because it has been marked as rollback-only

    转载https://blog.csdn.net/f641385712/article/details/80445912 读了两边才找到问题

  8. 记一次org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only异常

    @Transactional(rollbackFor = Exception.class) @Overridepublic DubboResult<Boolean> productAddO ...

  9. spring事务(Transaction )报 marked as rollback-only异常的原因及解决方法

    很多朋友在使用spring+hibernate或mybatis等框架时经常遇到报Transaction rolled back because it has been marked as rollba ...

随机推荐

  1. 2017.07.07【NOIP提高组】模拟赛B组

    Summary 因为某种无法抗拒的原因,今天没有打比赛,所以也就没有那种心态.今天的题目有状压DP和二分,这套题不难也不简单,适中,适合我这种渣渣来做.在改题时,发现了许多问题.我连欧拉函数的计算都记 ...

  2. 移动游戏ui设计(二)--游戏世界的基本法则

    游戏世界的基本法则游戏体验的层次感很重要,制作一款趣味性强,宗旨明确的游戏并不简单. l  风格统一性:要先明白游戏的game genres,因为游戏类型与风格密切联系.常见游戏类型有:动作类.冒险类 ...

  3. NodeJS缓冲区

    NodeJS缓冲区 JavaScript语言本身在I/O时只有字符串数据类型,没有二进制数据类型,但在处理流数据时,必须用到二进制数据,因此在Node中,定义了一个Buffer类作为存放二进制数据的缓 ...

  4. JS_高程6.面向对象的程序设计(2)创建对象_2 构造函数也是一般函数

    1.构造函数也是一般函数,以下创建一个构造函数. var Person=function(name,age,job){ this.name=name; this.age=age; this.job=j ...

  5. openstack之Neutron网络虚拟化

    第一:为什么需要网络虚拟化? 一.数据中心的现有网络不能满足云计算的物理需求: 互联网行业数据中心的基本特征就是服务器的规模偏大.进入云计算时代后,其业务特征变得更加复杂,包括:虚拟化支持.多业务承载 ...

  6. set 容器的用法

    1.set容器的理解 所有元素都会根据元素的键值自动排序,set元素的键值就是实值,实值就是键值.set不允许两个元素有相同的键值.(set的元素不像map那样可以同时拥有实值(value)和键值(k ...

  7. NoSQL简单介绍

    这里介绍一下如今经常使用的NoSQL以及各自的特点. NoSQL是2009年突然发展起来的.如今趋于稳定的状态,市场上也有了一些比較成熟的产品. 传统的关系型数据库为了保证通用性的设计而带来了功能复杂 ...

  8. 【jquery采坑】Ajax配合form的submit提交(微擎表单提交,ajax验证,submit提交)

    1.采坑:实现form的submit提交,在提交之前,进行ajax的不同校验,然后onsubmit=return check(),进行提交 1/1 目的:可以实现以 from的submit提交,然后还 ...

  9. Linux-进程描述符 task_struct 详解

    为了描述控制进程的运行,系统中存放进程的管理和控制信息的数据结构称为进程控制块 PCB(Process Control Block),它是进程实体的一部分,是操作系统中最重要的记录性数据结构.它是进程 ...

  10. centos7设置服务为开机自启动(以crond.serivce为例)

    本文转自:https://blog.51cto.com/mrxiong2017/2084790 一.设置crond.serivice服务为开机自启动 步骤1:查看crond.serivce服务的自启动 ...