Spring事务-代理模式导致事务不回滚问题分析

博客探讨了事务回滚问题。默认事务遇运行期异常回滚,遇检查型异常不回滚。通过对比调用类自身事务方法和调用其他类事务方法,发现异常捕获时事务回滚情况不同。经分析,这是因为事务基于代理模式实现,调用其他类方法触发代理,而调用自身方法不触发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说明

  首先我们都了解事务为什么回滚,回滚的原因是什么。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚。

问题描述

我们定义两个类,一个类中有两个事务方法,如图:

package com.helu.samui.service;

import com.helu.samui.dao.UserInfoDao;
import com.helu.samui.entity.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service(value = "userInfoService")
public class UserInfoService {

    @Autowired
    private UserInfoDao userInfoDao;
    @Autowired
    private TestService testService;

    @Transactional
    public void save() {
        userInfoDao.insert(getUserInfo("a1"));
        try {

            //内部类调用不会触发代理模式
            this.test();
            //testService.test();
        } catch (Exception e) {
            System.out.println("aaaaaaaaaaaaaaa");
            e.printStackTrace();
        }
        userInfoDao.insert(getUserInfo("a2"));
    }

    @Transactional
    public void test() {
        throw new RuntimeException();
    }

    private UserInfo getUserInfo(String loginName) {
        UserInfo userInfo = new UserInfo();
        userInfo.setLoginName(loginName);
        return userInfo;
    }
}

如图: save()方法上加了@Transactional注解,表示开启了一个事务,在其中进行了两次插入操作,中间调用this.test()这个同一个类中方法,我们可以发现在代码执行到save()的时候,异常被捕获,事务未被回滚。
输出:

开始测试-----------------
catch the exception.
测试结束-----------------

数据库: 插入两条数据
在这里插入图片描述
注: 这种情况符合我们异常抛出回滚,异常被捕获,不回滚的逻辑判断。但是别急,我们看下面这种情况。我们将UserInfoService 中的save()方法中两次插入中间的调用改成testService.test(),去调用testService的test()方法。

package com.helu.samui.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service(value = "testService")
public class TestService {

    @Transactional
    public void test() {
        throw new RuntimeException();
    }
}

我们再来看下输出和数据库的情况:
输出:
在这里插入图片描述
数据库: 没有任何数据插入
  所以,这是为什么呢?调用类自己内部的事务方法的时候,被调用的事务方法抛出异常,异常被捕获了,不会回滚;调用其他类中的事务方法,被调用的事务方法抛出异常,异常也被捕获了,我们看到异常打印出了catch the exception,但是事务回滚了,这是什么原因呢?

分析

  我们从日志着手,首先,同样异常都被捕获住了,但是调用其他类中的事务方法的时候多抛出了一个异常,UnexpectedRollbackException,并且提示事务被回滚,因为rollback-only已经被标记成需要回滚了,那我们就根据异常一步一步分析吧。
  我们已经知道回滚标记已经被置成true,异常是testService中抛出,我们看看为什么回滚标记,什么时候被置为true的。首先,事务的实现是基于代理模式实现,当调用testService中的test方法的时候触发了代理模式,而直接调用this.test(),因为this是被真实对象本身,所以并不会触发代理模式,这就是这两者区别的真正原因。

源码分析可以参照:http://www.cnblogs.com/chihirotan/p/6739748.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值