Spring service层的事务探究(Transaction rolled back because it has been marked as rollback-only)
1 背景
代码在一个service层的事务方法中捕获并解决另一个事务方法抛出的异常,想要以一个正常信息返回到事务外,即controller层。
示例代码如下:
controller层代码
@Controller
public class TestController {
@Resource
private TestService testService;
@RequestMapping(value="/test", produces="application/json;charset=UTF-8")
@ResponseBody
public String test() {
try {
String mes = testService.testService();
return "{\"code\":1, \"message\":mes}";
} catch (ParameterException e) {
return "{\"code\":0, \"message\":e.getMessage()}";
} catch (Exception e) {
return "{\"code\":0, \"message\":\"失败\"}";
}
}
}
service层代码
@Service
@Transactional
public class TestService {
@Resource
private TestService testService;
public String testService() {
try {
testService.testException();
} catch (ParameterException e) {
return e.getMessage();
} catch (Exception e) {
return "报错返回";
}
return "成功返回";
}
public String testException() throws Exception {
if (true) {
throw new ParameterException("报参数错误返回");
}
return "gg";
}
}
ParameterException是我自己定义的异常类,从上面代码可以看到,我们预期的结果应该是这样的:
1.在 testException() 方法中抛出 ParameterException 异常开始,异常信息是:报参数错误返回。
2.异常抛出到 testService() 方法中就给消化解决了,并且返回抛上来的异常信息:报参数错误返回。
3.返回的异常信息到controller层,并被成功返回到页面上: {“code”:1, “message”:”报参数错误返回”} 。
然而事实并非如此,而是直接报错: Transaction rolled back because it has been marked as rollback-only ,最后返回:{“code”:0, “message”:”失败”}
2 原因说明
在service层抛出异常,到返回数据到controller层,Spring到底做了什么呢?我们可以自己从 throw new ParameterException(“报参数错误返回”); 处开始打断点一个一个地看,这里就不仔细分析了。
将代码分析转变为文字,在全局配置了事务的传播机制是REQUIRED(或明确将事务设置为了RollbackOnly),在 testException() 方法以事务方式运行时抛出的 非受检异常 被上一层的事务方法 testService() 消化解决了,没有继续抛出去,而运行完 testService() 方法并准备正常返回信息时,Spring会将这个事务进行一次commit,但是此时并不能正常commit,而是进行了rollback操作,并且在事务边界到达时抛出了UnexpectedRollbackException异常。
总的来说就是:抛出的非受检异常被处理了,导致设置了RollbackOnly的事务无法正常commit和rollback操作。
3 解决方法
这里的解决方法有如下几种:
1.在事务中的非受检异常(即自定义的事务异常)不要在事务中自己解决掉,而是继续抛出去到事务边界外(这里即controller层)再来解决。
2.若确实需要在事务中消化它,可以解决一个正常受检异常(如:Exception),让事务commit操作可以正常进行。
3.将处理非受检异常的事务方法用注解 @Transactional(propagation = Propagation.NOT_SUPPORTED) 配置为此方法以非事务方式运行,这样就不会另外执行其他操作了。
4 事务扩展
4.1 事务传播方式
@Transactional注解中可以配置propagation指定事务传播方式,spring的Propagation提供了如下事务传播属性:
- REQUIRED
进入当前事务,如果没有事务则创建新的事务 - SUPPORTS
支持当前事务状态,如果有事务则进入,没有则无事务方式运行 - MANDATORY
当前必须有事务,如果没有则抛异常 - REQUIRES_NEW
无论如何都创建新的事务 - NOT_SUPPORTED
非事务方式运行 - NEVER
非事务方式运行,如果当前有事务则抛出异常 - NESTED
新建一个事务,如果当前事务存在,则以嵌套事务运行
还没有评论,来说两句吧...