产生原因  ,

1 serviceA 调用 serviceB 然后 B  抛出异常 ,B 所在的 事物 回滚,B 把当前可写 事物标记成 只读事物 ,

2 如果 A 和B 是在 同一个事物环境,并且 A 抓了 B 抛出的异常,没有和 B 一起回滚,

3 然后 A 方法 完成,把当前事物 当成 写事物提交。就会出上面的问题。

上代码:

解释: 可以看出  上面代码 问题有点多, 方法 A  是 没有 事物环境的,也就是说 调用方法A 的 前面的方法如果有事物环境, 方法A 就 依赖前面的事物环境, 没有 方法A 就 是以非事物的方式执行( 级联操作 ,如果 在非事物 环境执行,就不能级联回滚,正常必须要在事物环境 ),然后 就是 随意的抓异常,让本来应该回滚的 事物没有回滚,依旧在向下执行。

正确的处理方法:  如果是级联操作,那么 应该处于同一个 事物环境,并且不应该 随意的抓异常,只有 自己 能处理 ,并且不影响事物回滚的异常才能抓,别的异常统统不允许抓。并且 默认的 回滚需要 runtimexception ,所以只能抛出这个异常的子类 ,如果 出现别的异常, 非 runtimexception  那么 应该抓了 抛出 runtimexception 。

另外 说一句 spring有完善的全局 异常处理体系, 正常来说不需要 随意的 try 异常。 我们需要处理的只有 非 runtimexception ,别的异常直接跑就是了。

推荐的异常处理方法:

1  定义 全局异常处理,然后根据 不同异常 ,返回不同的 提示信息。

全局异常处理类:

package com.hs.backend.controller.exception;

import java.util.Map;
import java.util.Set; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import com.hs.commons.exception.CanRedirectExcetion;
import com.hs.commons.utils.ClassUtils; /**
* 统一异常处理
*
* @author ZHANGYUKUN
*
*/
@Component
public class GlobalExceptionResolver implements HandlerExceptionResolver { Logger logger = LoggerFactory.getLogger(GlobalExceptionResolver.class); @Autowired
RequestMappingHandlerMapping requestMappingHandlerMapping; /**
* 异常前缀
*/
private String prefix = "/global/exception/"; /**
* 异常后缀
*/
private String suffix = "Result"; /**
* 自定义异常列表
*
*/
//private List<Class<?>> exceptionClsList = new ArrayList<>(); /**
* 子包名
*/
//private String subPackage = "exception"; /**
* 默认的处理结果
*/
private String defaultResult = "unknownException"; @Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception exception) {
ModelAndView mv = new ModelAndView();
mv.setViewName(getForwardViewName(getViewName(defaultResult)));
mv.addObject("exception", exception);
Class<?> cls = exception.getClass();
String viewName = getViewName(ClassUtils.getObjectName(cls.getSimpleName()));
if (CanRedirectExcetion.class.isInstance(exception)) { Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();
Set<RequestMappingInfo> set = map.keySet();
for (RequestMappingInfo requestMappingInfo : set) {
if (!requestMappingInfo.getPatternsCondition().getMatchingPatterns( viewName ).isEmpty()) {
mv.setViewName(getForwardViewName(getViewName(ClassUtils.getObjectName(cls.getSimpleName()))));
return mv;
}
} }
System.out.println( exception.toString() );
return mv;
} /**
* 通过 异常对象名 得到 异常视图名
* @param exceptionObjectName
* @return
*/
private String getViewName(String exceptionObjectName) {
return prefix + exceptionObjectName + suffix;
} /**
* 得到 forward 格式 的viewName
* @param viewName
* @return
*/
private String getForwardViewName(String viewName) {
return "forward:" + viewName;
} }

异常信息返回:  上面的 异常处理类会 调用下面这个类的不同方法 返回 前端 不同错误码 ,并且 统一格式的 信息。

package com.hs.backend.controller.exception;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import com.hs.backend.exception.GlobalExceptionResolver;
import com.hs.common.result.CommonErrorInfo;
import com.hs.common.result.CommonResult;
import com.hs.commons.exception.AlreadyExistRecord;
import com.hs.commons.exception.ApiInvokingException;
import com.hs.commons.exception.DataErrorException;
import com.hs.commons.exception.NoPermissionException;
import com.hs.commons.exception.NoTokenErrorException;
import com.hs.commons.exception.ParameterErrorException;
import com.hs.commons.exception.TokenExpiredException;
import com.hs.commons.exception.UnLoginException; import springfox.documentation.annotations.ApiIgnore; /**
* 定义异常的返回处理策略,这个路劲不应该添加热河权限限制
*
* @author ZHANGYUKUN
*/
@ApiIgnore
@RestController
@RequestMapping("global")
public class GlobalController { private static Logger logger = LoggerFactory.getLogger(GlobalExceptionResolver.class); /**
* 未知异统一处理
*
* @return
*/
@RequestMapping("exception/unknownExceptionResult")
public CommonResult<Void> unknownExceptionResult() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Exception exception = (Exception) request.getAttribute("exception"); Throwable throwable = null;
if (logger.isErrorEnabled()) {
logger.error("未知异常发生了:", exception);
throwable = findFinalCause( exception );;
} //如果取不到异常堆站里面的 信息 ,就向前端提示未知异常
String message = "未知的异常";
if( throwable.getMessage() != null ) {
message = throwable.getMessage();
}
return CommonResult.getFaiInstance(CommonErrorInfo.code_3001, message);
} /**
* 参错误统一返回
*
* @return
*/
@RequestMapping("exception/parameterErrorExceptionResult")
public CommonResult<Void> parameterErrorExceptionResult() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
ParameterErrorException exception = (ParameterErrorException)request.getAttribute("exception"); return CommonResult.getFaiInstance(CommonErrorInfo.code_1001, exception.getMessage() );
} /**
* 没有权限统一返回
* @return
*/
@RequestMapping("exception/noPermissionExceptionResult")
public CommonResult<Void> noPermissionException() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
NoPermissionException exception = (NoPermissionException)request.getAttribute("exception");
return CommonResult.getFaiInstance( CommonErrorInfo.code_2001 , exception.getMessage() == null ? "没有权限" : exception.getMessage());
} /**
* 未登录统一返回
*
* @return
*/
@RequestMapping("exception/unLoginExceptionResult")
public CommonResult<Void> unLoginExceptionResult() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
UnLoginException exception = (UnLoginException)request.getAttribute("exception"); return CommonResult.getFaiInstance(CommonErrorInfo.code_4001, exception.getMessage());
} /**
* 已存在的记录错误
* @return
*/
@RequestMapping("exception/alreadyExistRecordResult")
public CommonResult<Void> alreadyExistRecordResult() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
AlreadyExistRecord exception = (AlreadyExistRecord)request.getAttribute("exception"); return CommonResult.getFaiInstance(CommonErrorInfo.code_5001, exception.getMessage() );
} /**
* 没有token
* @return
*/
@RequestMapping("exception/noTokenErrorExceptionResult")
public CommonResult<Void> noTokenErrorExceptionResult() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
NoTokenErrorException exception = (NoTokenErrorException)request.getAttribute("exception"); return CommonResult.getFaiInstance(CommonErrorInfo.code_2101, exception.getMessage() );
} /**
* token过期
* @return
*/
@RequestMapping("exception/tokenExpiredExceptionResult")
public CommonResult<Void> tokenExpiredExceptionResult() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
TokenExpiredException exception = (TokenExpiredException)request.getAttribute("exception"); return CommonResult.getFaiInstance(CommonErrorInfo.code_2102, exception.getMessage() );
} /**
* 数据错误
* @return
*/
@RequestMapping("exception/dataErrorExceptionResult")
public CommonResult<Void> dataErrorException() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
DataErrorException exception = (DataErrorException)request.getAttribute("exception"); return CommonResult.getFaiInstance(CommonErrorInfo.code_6001, exception.getMessage() );
} /**
* 访问频率过高
* @return
*/
@RequestMapping("exception/requestFrequencyExceptionResult")
public CommonResult<Void> requestFrequencyException() {
return CommonResult.getFaiInstance(CommonErrorInfo.code_8001, "访问频率过高" );
} /**
* api调用异常错误
* @return
*/
@RequestMapping("exception/apiInvokingExceptionResult")
public CommonResult<Void> apiInvokingException() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
ApiInvokingException exception = (ApiInvokingException)request.getAttribute("exception"); return CommonResult.getFaiInstance(CommonErrorInfo.code_9001, exception.getMessage() );
} /**
* 得到最根上的异常
*
* @param throwable
* @return
*/
private Throwable findFinalCause(Throwable throwable) {
Throwable rootThrowable = null;
String msg = null; if( throwable.getCause() == null ) {
rootThrowable = throwable;
}else {
rootThrowable = findFinalCause( throwable.getCause() );
}
msg = rootThrowable.getMessage();
System.out.println( msg ); return rootThrowable;
} }

正常的方法调用:  保持事物环境,不管是注解 还是 aop ,然后  不合理的 操作 ,直接 抛出异常 ,让整个事物环境回滚。(这里只有单个方法,如果 A 调用B ,B 里面抛出异常,B 的 事物传播 参数 指定的  是 REQUIRED(默认的),那么 就可以 正常回滚  )

Transaction rolled back because it has been marked as rollback-only 原因 和解决方案的更多相关文章

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

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

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

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

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

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

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

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

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

  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. Junit4测试报错

    1.字符串数组越界 java.lang.String IndexOutOfBounds Exception:String index out of range:-1 导致: Transaction r ...

随机推荐

  1. 使用Rancher和私有仓库快速搭建Kubernetes集群

    来来来,先出题:Rancher可以快速部署Kubernetes,但其使用的gcr.io上的镜像无法下载怎么办?使用Rancher可以快速部署Kubernetes,但我们需要下载哪些镜像?Rancher ...

  2. docker 安装mysql示例

    docker pull mysql 错误的启动: [root@localhost ~]# docker run --name mysql01 -d mysql 42f09819908bb72dd99a ...

  3. dedecmsv5.7 ueditor编辑器上传视频/修改,视频显示空白,解决方案

    dedecmsv5.7 ueditor 在上传视频之后,显示空白.其实是有视频的,就是显示空白.找了资料,记录一下. 解决方案: 找到include下面的ueditor下面的ueditor.all.j ...

  4. 锯齿状优惠券css绘制

    对于图上优惠券左右两侧的半圆锯齿效果,两种处理方式,一种直接使用切图进行处理,一种是纯css进行效果绘制.切图的就不再赘述,主要说纯css效果绘制 绘制的结果如下图: 难点在于两侧的半圆孔是透明色,不 ...

  5. 关于vs调用数据库存储过程 返回输出参数的一些总结

    1.直接上练习的存储过程,方便回想 create proc proc_output @totlecount int output, @pageIndex int, @pageSize intas de ...

  6. keras在win下的安装,使用等

    http://cache.baiducontent.com/c?m=9d78d513d99a16ef4fece42d4c01d6160e2482744cd7c7637ac3e34a84652b5637 ...

  7. LeetCode 33 搜索旋转排序数组

    题目: 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] ). 搜索一个给定的目标值,如果数组中存在这个 ...

  8. VS--------实用快捷键

    Ctrl + M + O : 折叠所有方法 Ctrl + Tab: 切换不同窗口 ------------------------------开发--------------------------- ...

  9. NSIS脚本 打包安装程序

    相关工具 nsis http://nsis.sourceforge.net/Special_Builds HM NIS Edit http://hmne.sourceforge.net/ 例子: ; ...

  10. 异常处理:try - except 和 try finally。

    异常处理:try-except语句 1)      此处:as reason为可选参数,reason是一个变量. 2)      使用try—except语句时,检测范围内出现错误,不会有红色的报错提 ...