@Transactional方法调用@Async方法导致@Transactional注解失效

测试1: @Transactional方法调用 非@Async 方法

第一个类,一个普通的方法

@Service
@Slf4j
public class AsyncTest {
    public void testAsync(){
        if(log.isInfoEnabled()){
            log.info("testAsync线程名字:{}",Thread.currentThread().getName());
        }
        // 模拟出现异常,观察第二个类中的相关方法是否会回滚
        throw new RuntimeException("模拟异常");
    }
}

第二个类,有一个加了@Transactional(rollbackFor = Exception.class)的方法,调用个第一个类中的@Async注解的方法

@Slf4j
@Service
public class TransactionalTest {
    @Autowired
    private CommodityService commodityService;

    @Autowired
    private AsyncTest asyncTest;

    @Transactional(rollbackFor = Exception.class)
    public void testTransactional(){
        CommodityVO test = new CommodityVO("测试商品biubiubiubiubiu", new BigDecimal("10.00"), 10);
        // 数据库操作
        commodityService.addCommodity(test);
        if(log.isInfoEnabled()){
            log.info("testTransactional线程名字:{}",Thread.currentThread().getName());
        }
        // 执行第一个类中的方法
        asyncTest.testAsync();
    }
}

测试方法:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MybatisApplicationTests {
    @Autowired
    private TransactionalTest annotationTest;

    @Test
    public void contextLoads() {
        annotationTest.testTransactional();
    }
}

结果:

数据库中不会插入新数据,证明事务回滚

测试2: @Transactional方法调用@Async方法

第一个类,有一个加了@Async注解的方法

@Service
@Slf4j
public class AsyncTest {
    @Async
    public void testAsync(){
        if(log.isInfoEnabled()){
            log.info("testAsync线程名字:{}",Thread.currentThread().getName());
        }
        // 模拟出现异常,观察第二个类中的相关方法是否会回滚
        throw new RuntimeException("模拟异常");
    }
}

第二个类,有一个加了@Transactional(rollbackFor = Exception.class)的方法,调用个第一个类中的@Async注解的方法

@Slf4j
@Service
public class TransactionalTest {
    @Autowired
    private CommodityService commodityService;

    @Autowired
    private AsyncTest asyncTest;

    @Transactional(rollbackFor = Exception.class)
    public void testTransactional(){
        CommodityVO test = new CommodityVO("测试商品1号", new BigDecimal("10.00"), 10);
        // 数据库操作
        commodityService.addCommodity(test);
        if(log.isInfoEnabled()){
            log.info("testTransactional线程名字:{}",Thread.currentThread().getName());
        }
        // 执行第一个类中的方法
        asyncTest.testAsync();
    }
}

测试方法:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MybatisApplicationTests {
    @Autowired
    private TransactionalTest annotationTest;

    @Test
    public void contextLoads() {
        annotationTest.testTransactional();
    }
}

结果:

数据库中仍然插入了新数据,回滚失败

原因(From ChatGPT4.0):

当一个带有 @Transactional 注解的方法直接调用一个带有 @Async 注解的方法时,可能会出现事务失效和异常回滚不生效等问题。

这是因为异步执行(即在新线程中执行)导致原始调用线程的事务上下文无法传递到新线程中。所以,在新线程进行数据库操作时将不受原有事务控制。

要避免这些问题,请确保需要保证事务性和需要异步处理之间没有直接依赖关系,并尽量将它们拆分成独立功能模块或服务。

在Spring框架中,使用@Async注解可以将一个方法标记为异步执行,该方法会在一个新的线程中执行。而@Transactional注解用于开启事务,用于保证数据的一致性。 当一个被@Async注解方法调用一个被@Transactional注解方法时,会出现事务失效的情况。这是因为@Transactional注解只能在当前线程中开启事务,而异步方法是在新的线程中执行的,与当前线程不在同一个线程中,因此无法获取当前线程中的事务上下文。 为了解决这个问题,可以使用Spring提供的异步事务处理机制。具体来说,可以在@Async注解方法上添加@Transaction注解,并设置propagation属性为REQUIRES_NEW,表示开启一个新的事务。这样,在异步方法中执行的数据库操作就可以在新的事务中进行,不会影响到当前线程中的事务。 举个例子: ``` @Service public class UserService { @Autowired private UserRepository userRepository; @Async @Transactional(propagation = Propagation.REQUIRES_NEW) public void updateUser(User user) { userRepository.save(user); } } ``` 在上述例子中,updateUser方法使用了@Async@Transactional注解@Async注解表示该方法是异步执行的,@Transactional注解中设置propagation属性为REQUIRES_NEW,表示开启一个新的事务。 总之,为了避免@Async修饰的方法@Transactional事务失效,需要在异步方法中使用@Transaction注解开启一个新的事务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝灰鼓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值