spring事务失效的场景

x33g5p2x  于2022-07-10 转载在 Spring  
字(4.0k)|赞(0)|评价(0)|浏览(544)

spring事务是项目中经常使用的场景,但是不正确使用spring的事务会造成事务失效。

spring 事务失效的7种场景:

1、 注解@Transactional配置的方法非public权限修饰

参考Spring官方文档介绍

When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.

@Transactional 只能用于 public 的方法上,否则事务会失效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。

2、 注解@Transactional所在类非Spring容器管理的bean;

//@Service
public class xxServiceImpl implements XXService {

  
    @Override
    @Transactional
    public void insertClassByException(StudentDo studentDo) throws CustomException {
        DBMapper.insertStudent(studentDo);
        throw new CustomException();
    }
}

如果@service 被注释,这个bean不能被spring托管, @Transactional将会失效。

3、 注解@Transactional所在类中,注解修饰的方法被类内部方法调用;

在类A里面有方法a 和方法b, 然后方法b上面用 @Transactional加了方法级别的事务,在方法a里面 调用了方法b, 方法b里面的事务不会生效。为什么会失效呢?:

其实原因很简单,Spring在扫描Bean的时候会自动为标注了@Transactional注解的类生成一个代理类(proxy),当有注解的方法被调用的时候,实际上是代理类调用的,代理类在调用之前会开启事务,执行事务的操作,但是同类中的方法互相调用,相当于this.B(),此时的B方法并非是代理类调用,而是直接通过原有的Bean直接调用,所以注解会失效。

@Service
public class XXServiceImpl implements XXService {

    @Autowired
    private DBMapper dbMapper;

    public void insertXX(XXDo xxDo) throws Exception {
        insertXXByBB(classDo);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void insertXXByBB(XXDo xxDo) throws Exception {
        dbMapper.insertBB(xxDo);
        throw new RuntimeException();
    }
}

4、 业务代码抛出异常类型非RuntimeException,事务失效

@Service
public class ClassServiceImpl implements ClassService {

    @Autowired
    private ClassMapper classMapper;

//    @Override
//    @Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
    public void insertClass(ClassDo classDo) throws Exception {
//        即使此处使用代理对象调用内部事务方法,数据依然未发生回滚,事务机制亦然失效
        ((ClassServiceImpl)AopContext.currentProxy()).insertClassByException(classDo);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void insertClassByException(ClassDo classDo) throws Exception {
        classMapper.insertClass(classDo);
        //抛出非RuntimeException类型,事务失效
        throw new Exception();
    }
//测试用例:
 @Test
    public void insertInnerExceptionTest() throws Exception {
       classDo.setClassId(3);
       classDo.setClassName("java_3");
       classDo.setClassNo("java_3");

       classService.insertClass(classDo);
    }
}

解决方案:
@Transactional注解修饰的方法,加上rollbackfor属性值,指定回滚异常类型:@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)

@Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void insertClassByException(ClassDo classDo) throws Exception {
        classMapper.insertClass(classDo);
        throw new Exception();
    }

5、 业务代码中存在异常时,使用try…catch…语句块捕获,而catch语句块没有throw new RuntimeExecption异常;

@Service
public class ClassServiceImpl implements ClassService {

    @Autowired
    private ClassMapper classMapper;

//    @Override
    public void insertClass(ClassDo classDo) {
        ((ClassServiceImpl)AopContext.currentProxy()).insertClassByException(classDo);

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void insertClassByException(ClassDo classDo) {
        classMapper.insertClass(classDo);
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

// 测试用例:
 @Test
    public void insertInnerExceptionTest() {
       classDo.setClassId(4);
       classDo.setClassName("java_4");
       classDo.setClassNo("java_4");

       classService.insertClass(classDo);
    }

解决方案:
可以捕获异常,打印出异常信息,并再次抛出异常

@Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void insertClassByException(ClassDo classDo) {
        classMapper.insertClass(classDo);
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

6、 注解@Transactional中Propagation属性值设置错误即Propagation.NOT_SUPPORTED

此种事务传播行为不是特殊自定义设置,基本上不会使用Propagation.NOT_SUPPORTED,不支持事务

@Transactional(propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
    public void insertClassByException(ClassDo classDo) {
        classMapper.insertClass(classDo);
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

7、 mysql关系型数据库,且存储引擎是MyISAM而非InnoDB,则事务会不起作用

参考文献:https://zhuanlan.zhihu.com/p/371606658

相关文章