栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

spring事务失效的情况和原因分析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

spring事务失效的情况和原因分析

平时我们开发需要满足事务时,我们就会想到@Transactional注解,但是添加这个注解后也可能也不会满足我们的需求,甚至事务不生效。下面我就分析下事务失效的原因及事务如何使用。

下面开始介绍失效原因及解决方案。

目录

目录

1.service没有被 Spring 管理

2.异常被捕捉了(异常被吃了)

3.调用本类方法,事务失效

4.多线程调用

5.异常类型错误

6.非public方法修饰

7.方法用final修饰

8.方法用static修饰

9.选用了数据库引擎不支持事务的存储引擎


1.service没有被 Spring 管理
// @Service 必须要添加注解,被spring管理
public class TestTransactionalServiceImpl implements TestTransactionalService{
       
        @Override
        @Transactional
        public void testTransactional() {
        }
}

解决方案:添加@Service注解,使得事务类被spring管理

2.异常被捕捉了(异常被吃了)
@Transactional
public void testTransactional() {
    try {
        // 修改数据===1===
        AdCourse adCourse = new AdCourse();
        adCourse.setId(5203314);
        adCourse.setName("计算机科学");
        int i = adCourseMapper.updateById(adCourse);

        // 修改数据===2===
        adCourse.setId(5219314);
        adCourse.setName("数据2");
        int result = adCourseMapper.updateById(adCourse);

        // 手动制造异常
        result = 1/0;
    } catch(Exception e){  // 本质:异常被捕捉,事务检测不到有错误的存在
        e.printStackTrace();
        log.error("程序出现异常了");
    }
}

本质:异常被捕捉,事务检测不到有错误的存在

解决方案1:把异常再次抛出 throw e;

} catch(Exception e){
    e.printStackTrace();
    log.error("程序出现异常了");
    throw e;  // 把异常再次抛出
}

解决方案2:在捕捉的异常中,手动添加回滚

} catch(Exception e){
    e.printStackTrace();
    log.error("程序出现异常了");
    // 手动回滚
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}

解决方案3(推荐):将业务代码单独抽出,在抽出的方法上加事务

@Override
public void testTransactional() {
    try {
        testRollback();
    } catch(Exception e){
        e.printStackTrace();
        log.error("程序出现异常了");
    }
}

@Transactional
public void testRollback(){
    // 修改数据===1===
    AdCourse adCourse = new AdCourse();
    adCourse.setId(5203314);
    adCourse.setName("计算机科学");
    int i = adCourseMapper.updateById(adCourse);

    // 修改数据===2===
    adCourse.setId(5219314);
    adCourse.setName("数据2");
    int result = adCourseMapper.updateById(adCourse);

    // 手动制造异常
    result = 1/0;
}

3.调用本类方法,事务失效
@Override
public void testTransactional() {
    try {
        testRollback();
    } catch(Exception e){
        e.printStackTrace();
    }
}

@Transactional  // 事务未生效
public void testRollback(){
    // 修改数据===1===
    AdCourse adCourse = new AdCourse();
    adCourse.setId(5203314);
    adCourse.setName("计算机科学");
    int i = adCourseMapper.updateById(adCourse);

    // 修改数据===2===
    adCourse.setId(5219314);
    adCourse.setName("数据2");
    int result = adCourseMapper.updateById(adCourse);

    // 手动抛出异常
    result = 1/0;
}

本质原因:因为发生了自身调用,调类自己的方法,而没有经过 Spring 的代理类,默认只有在外部调用事务才会生效。

解决方案:需要在testTransactional()方法上加@Transactional注解,testRollback()方法可以不用加(本类方法加了也不起作用)。

@Override
@Transactional
public void testTransactional() {
    try {
        testRollback();
    } catch(Exception e){
        e.printStackTrace();
    }
}

// @Transactional(propagation = Propagation.REQUIRES_NEW) 加了也不起作用
public void testRollback(){
    // 修改数据===1===
    AdCourse adCourse = new AdCourse();
    adCourse.setId(5203314);
    adCourse.setName("计算机科学");
    int i = adCourseMapper.updateById(adCourse);

    // 修改数据===2===
    adCourse.setId(5219314);
    adCourse.setName("数据2");
    int result = adCourseMapper.updateById(adCourse);

    // 手动抛出异常
    result = 1/0;
}

4.多线程调用
@Override
@Transactional
public void testTransactional() {
    ExecutorService executorService = Executors.newCachedThreadPool();
    Future future = executorService.submit(() -> {
        // 修改数据===1===
        AdCourse adCourse = new AdCourse();
        adCourse.setId(5203314);
        adCourse.setName("计算机科学");
        int i = adCourseMapper.updateById(adCourse);

        // 修改数据===2===
        adCourse.setId(5219314);
        adCourse.setName("数据2");
        int result = adCourseMapper.updateById(adCourse);

        // 手动抛出异常
        result = 1/0;
        return true;
    });
}

本质:spring的事务是通过数据库连接来实现,数据库连接spring是放在threadLocal里面。同一个事务,只能用同一个数据库连接。而多线程场景下,拿到的数据库连接是不一样的,即是属于不同事务。

解决方案:将业务代码单独抽出,在抽出的方法上加事务

5.异常类型错误
// @Transactional(rollbackFor = FdException.class)
public void testTransactional() {
    try {
        // 修改数据===1===
        AdCourse adCourse = new AdCourse();
        adCourse.setId(5203314);
        adCourse.setName("计算机科学");
        int i = adCourseMapper.updateById(adCourse);

        // 修改数据===2===
        adCourse.setId(5219314);
        adCourse.setName("数据2");
        int result = adCourseMapper.updateById(adCourse);

    }catch(FdException e){
        throw e;
    }
}

这样事务是不生效的,因为默认回滚的是:RuntimeException及其子类,如果你想触发其他异常的回滚,需要在注解上配置一下:(rollbackFor = FdException.class)

6.非public方法修饰
@Transactional
protected void testTransactional() {
    ExecutorService executorService = Executors.newCachedThreadPool();
    Future future = executorService.submit(() -> {
        // 修改数据===1===
        AdCourse adCourse = new AdCourse();
        adCourse.setId(5203314);
        adCourse.setName("计算机科学");
        int i = adCourseMapper.updateById(adCourse);

        // 修改数据===2===
        adCourse.setId(5219314);
        adCourse.setName("数据2");
        int result = adCourseMapper.updateById(adCourse);

        // 手动抛出异常
        result = 1/0;
        return true;
    });
}

spring事务默认生效的方法权限都必须是public修饰

7.方法用final修饰
@Transactional
public final void testRollback(){
    // 修改数据===1===
    AdCourse adCourse = new AdCourse();
    adCourse.setId(5203314);
    adCourse.setName("计算机科学");
    int i = adCourseMapper.updateById(adCourse);

    // 修改数据===2===
    adCourse.setId(5219314);
    adCourse.setName("数据2");
    int result = adCourseMapper.updateById(adCourse);

    // 手动抛出异常
    result = 1/0;
}

原因:因为spring事务是用动态代理实现,因此如果方法使用了final修饰,则代理类无法对目标方法进行重写,所以就不存在事务功能

解决方案:去掉final修饰

8.方法用static修饰
@Transactional
public static void testRollback(){
    // 修改数据===1===
    AdCourse adCourse = new AdCourse();
    adCourse.setId(5203314);
    adCourse.setName("计算机科学");
    int i = adCourseMapper.updateById(adCourse);

    // 修改数据===2===
    adCourse.setId(5219314);
    adCourse.setName("数据2");
    int result = adCourseMapper.updateById(adCourse);

    // 手动抛出异常
    result = 1/0;
}

原因:因为spring事务是用动态代理实现,因此如果方法使用了static修饰,则代理类无法对目标方法进行重写,所以就不存在事务功能

解决方案:去掉static修饰


​​​​​​​9.选用了数据库引擎不支持事务的存储引擎

选用了数据库引擎不支持事务的存储引擎。比如mysql中选用MyISAM

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/986881.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号