- 业务场景
一个上报数据的接口,因为上报数据需要快速返回,上报方不关心上报结果,并且,需要同时进行多张表的存储操作,要保证多张表同时存储成功,否则将会产生脏数据。 - 事务为什么会失效
在 Java 中,`@Transactional` 注解通常与事务管理器一起使用,用于标记一个方法应该被包装在一个事务中执行。在同一类中,`@Transactional` 注解可能会失效或不起作用的一些常见情况包括:
1. **自调用问题:** 如果一个带有 `@Transactional` 注解的方法在同一个类中被另一个方法内部直接调用(而不是通过代理对象调用),事务注解可能会失效。这是因为 Spring 使用代理对象来实现事务管理,如果方法直接被调用而不是通过代理对象,事务注解将不会生效。
2. **非公共方法调用问题:** 如果带有 `@Transactional` 注解的方法是私有的、受保护的或默认访问权限的,并且被同一类中的公共方法调用,事务注解可能会失效。因为 Spring AOP 代理只会围绕公共方法创建代理。
3. **异步方法问题:** 如果带有 `@Transactional` 注解的方法是异步方法(使用 `@Async` 注解标记),事务注解可能会失效。这是因为异步方法会在一个新的线程中执行,而事务上下文可能无法正确传播到新的线程中。
4. **事务传播问题:** 如果同一类中的方法使用不同的事务传播行为(如 `REQUIRES_NEW`),事务注解可能会失效。这是因为事务传播行为会影响事务的范围和生命周期。
5. **编译时代理问题:** 如果在同一类中使用了 `this` 关键字来调用带有 `@Transactional` 注解的方法,事务可能会失效。因为 Spring 默认使用基于代理的 AOP,而 `this` 关键字会绕过代理对象。
在这些情况下,`@Transactional` 注解可能会失效,因此在设计和使用事务时需要注意以上情况,确保事务能够正确生效并达到预期的效果。
- 在启动类添加注解
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement // 事务托管注解
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 在主方法
@Transactional
public void master(Request req) {
// 异步处理数据,自定义事务,防止事务失效
CompletableFuture.runAsync(() -> {
// 这行代码将获取到的事务状态对象赋值给 transactionStatus 变量,以便后续对事务的操作和管理,这里的事务指spring事务
TransactionStatus transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
// 保存数据
this.save(req);
// 提交事务
transactionManager.commit(transactionStatus);
} catch (Exception e) {
// 如果有异常事务失效要回滚
transactionManager.rollback(transactionStatus);
// 这里可以自定义异常抛出去
throw new BusinessLogicException(e.getMessage());
}
});
}
- 在被调用的方法(想保证事务的方法,例如mysql的存储)
/**
* Propagation.REQUIRES_NEW 表示当前方法会挂起当前事务(如果存在的话),并开启一个新的事务来执行。这样无论外部事务是否成功,内部事务都会独立执行,互不影响。
* 配置为 Exception.class 表示任何继承自 Exception 的异常都会触发事务回滚操作。如果方法中抛出了这些异常之一,事务会回滚到之前的状态。
*/
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void save(Request req) {
// 执行表一存储
// 执行表二存储
}
}