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
    新建一个事务,如果当前事务存在,则以嵌套事务运行

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值