@Transaction 方法内部调用事务方法导致事务失效

在 Spring 中,事务是通过 AOP 代理实现的,这意味着事务只会在外部调用方法时生效。如果你在同一个类中调用了另一个使用 @Transactional 注解的方法,Spring 并不会再次创建一个事务,而是直接执行该方法,导致事务失效。
解决方案:分离事务方法到不同的类

为了避免方法内部调用事务方法的问题,可以将事务方法拆分到不同的服务类中,让 Spring 通过 AOP 代理处理事务。这样,Spring 会在外部调用时正确地创建事务。

 

 

准备工作:

创建两张mysql表,id设置为自增。test1表有id和name字段,test2有id和name字段(且name字段不为空)

 

测试一:

controller层
@GetMapping("/test")
public Result test(String userName) {
    return service.test(userName);
}

 

serviceImpl层

@Override
@Transactional(rollbackFor = Exception.class)
public Result test(String userName) {
    testMapper.saveTest1("userName1");--没有回滚
    try {
        saveTest2(userName);
    } catch (Exception e) {
        log.warn("test报错{}", e.getMessage());
    }
    return Result.succeed("成功");
}

@Transactional(rollbackFor = Exception.class)
public void saveTest2(String userName) {
    testMapper.saveTest2(userName);--因为name字段设置了不能为空,所以这条数据没有添加成功
}

 

mapper层

@Insert("insert into test1(name) values(#{userName})")
void saveTest1(String userName);

@Insert("insert into test2(name) values(#{userName})")
void saveTest2(String userName);

调用/test接口,不传参数,最终结果:

test1表里有一条数据,test2表里没有数据,也就是事务没有回滚。

test1表数据如下

 

 

 

   

测试二:

controller层
@GetMapping("/test1")
public Result test1(String userName) {
    return service.test1(userName);
}

 

serviceImpl层

@Override
@Transactional(rollbackFor = Exception.class)
public Result test1(String userName) {
    testMapper.saveTest1("userName1");
    try {
        otherServiceImpl.saveTest2(userName); --回滚了
    } catch (Exception e) {
        log.warn("test报错{}", e.getMessage());
    }
    return Result.succeed("成功");
}

 

新建一个otherServiceImpl类,把测试一的saveTest2方法直接挪过来

@Transactional(rollbackFor = Exception.class)
public void saveTest2(String userName) {
    testMapper.saveTest2(userName); --回滚了
}

 

mapper层

@Insert("insert into test1(name) values(#{userName})")
void saveTest1(String userName);

@Insert("insert into test2(name) values(#{userName})")
void saveTest2(String userName);

调用/test1接口,不传参数,最终结果:

test1和test2表里都没有数据,也就是saveTest2方法被移到另一个service类的时候,代表事务回滚了。

 

 测试一和测试二得出结论:

在同一个类中,一个方法调用另一个带@Transaction注解的方法时,方法上的@Transaction注解不会生效。

解决这个问题,有一个简单的办法,就是新建一个类,将第二个方法移到新写的类里,此时@Transaction注解就可以生效了。

 

测试三:

controller层
@GetMapping("/test2")
public Result test2(String userName) {
    return service.test2(userName);
}

 

serviceImpl层

@Override
@Transactional(rollbackFor = Exception.class)
public Result test2(String userName) {
    testMapper.saveTest1("userName1");
    try {
        saveTest2(userName);
    } catch (Exception e) {
        log.warn("test报错{}", e.getMessage());
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
    return Result.succeed("成功");
}

@Transactional(rollbackFor = Exception.class)
public void saveTest2(String userName) {
    testMapper.saveTest2(userName);
}

 

mapper层

@Insert("insert into test1(name) values(#{userName})")
void saveTest1(String userName);

@Insert("insert into test2(name) values(#{userName})")
void saveTest2(String userName);

调用/test3接口,不传参数,最终结果:

test1和test2表里都没有数据。

即:在同一个类中,一个方法调用另一个带@Transaction注解的方法时,加上“TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();”,方法上的@Transaction注解就会生效。

 

posted @ 2021-10-13 19:29  素净  阅读(360)  评论(0)    收藏  举报