🎓博主介绍:Java、Python、js全栈开发 “多面手”,精通多种编程语言和技术,痴迷于人工智能领域。秉持着对技术的热爱与执着,持续探索创新,愿在此分享交流和学习,与大家共进步。
📖DeepSeek-行业融合之万象视界(附实战案例详解100+)
📖全栈开发环境搭建运行攻略:多语言一站式指南(环境搭建+运行+调试+发布+保姆级详解)
👉感兴趣的可以先收藏起来,希望帮助更多的人
SpringBoot事务管理陷阱:@Transactional失效的8个隐藏场景
一、引言
在Spring Boot应用开发中,事务管理是确保数据一致性和完整性的关键机制。@Transactional 注解作为Spring提供的强大工具,极大地简化了事务管理的操作。然而,在实际使用过程中,开发者可能会遇到 @Transactional 注解失效的情况,导致数据不一致等严重问题。本文将详细介绍 @Transactional 失效的8个隐藏场景,帮助技术人员避免这些陷阱。
二、@Transactional 注解基础回顾
2.1 注解作用
@Transactional 注解可以应用在类或方法上,用于声明该类或方法需要进行事务管理。当方法被调用时,Spring会自动开启事务,在方法执行完毕后根据执行结果进行事务的提交或回滚。
2.2 示例代码
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Transactional
public void saveUser(User user) {
// 保存用户的业务逻辑
}
}
三、@Transactional 失效的8个隐藏场景
一、方法不是public修饰
3.1.1 原因分析
@Transactional 注解只能应用在 public 方法上。这是因为Spring在创建代理对象时,会检查方法的访问修饰符,如果不是 public 方法,Spring不会为其创建事务代理,从而导致事务失效。
3.1.2 示例代码
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Transactional
private void saveUser(User user) {
// 保存用户的业务逻辑
}
}
3.1.3 解决方案
将方法的访问修饰符改为 public。
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Transactional
public void saveUser(User user) {
// 保存用户的业务逻辑
}
}
二、自身调用问题
3.2.1 原因分析
当一个类中的方法调用自身类的另一个带有 @Transactional 注解的方法时,事务会失效。这是因为Spring的事务管理是基于AOP代理实现的,而自身调用时并没有经过代理对象,所以事务不会生效。
3.2.2 示例代码
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
public void outerMethod() {
this.innerMethod();
}
@Transactional
public void innerMethod() {
// 业务逻辑
}
}
3.2.3 解决方案
通过注入自身的代理对象来调用方法。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Autowired
private UserService self;
public void outerMethod() {
self.innerMethod();
}
@Transactional
public void innerMethod() {
// 业务逻辑
}
}
三、异常被捕获但未抛出
3.3.1 原因分析
如果在带有 @Transactional 注解的方法中捕获了异常,但没有重新抛出,Spring无法感知到异常的发生,会认为事务执行正常,从而进行事务的提交,导致事务失效。
3.3.2 示例代码
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Transactional
public void saveUser(User user) {
try {
// 保存用户的业务逻辑
} catch (Exception e) {
// 捕获异常但未抛出
}
}
}
3.3.3 解决方案
在捕获异常后重新抛出异常,让Spring能够感知到异常的发生并进行事务回滚。
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Transactional
public void saveUser(User user) {
try {
// 保存用户的业务逻辑
} catch (Exception e) {
throw e;
}
}
}
四、异常类型不匹配
3.4.1 原因分析
@Transactional 注解默认只对 RuntimeException 及其子类和 Error 进行事务回滚。如果抛出的异常不是这些类型,事务不会回滚。
3.4.2 示例代码
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Transactional
public void saveUser(User user) throws Exception {
// 保存用户的业务逻辑
throw new Exception("自定义异常");
}
}
3.4.3 解决方案
在 @Transactional 注解中指定需要回滚的异常类型。
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Transactional(rollbackFor = Exception.class)
public void saveUser(User user) throws Exception {
// 保存用户的业务逻辑
throw new Exception("自定义异常");
}
}
五、数据库不支持事务
3.5.1 原因分析
不同的数据库对事务的支持情况不同。例如,MySQL的MyISAM引擎不支持事务,使用该引擎时,@Transactional 注解将失效。
3.5.2 解决方案
使用支持事务的数据库引擎,如MySQL的InnoDB引擎。可以通过以下SQL语句修改表的引擎:
ALTER TABLE table_name ENGINE = InnoDB;
六、事务传播行为配置错误
3.6.1 原因分析
@Transactional 注解的 propagation 属性用于指定事务的传播行为。如果配置不当,可能会导致事务失效。例如,将传播行为配置为 Propagation.NOT_SUPPORTED,表示以非事务方式执行操作,如果当前存在事务,将挂起当前事务。
3.6.2 示例代码
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void saveUser(User user) {
// 保存用户的业务逻辑
}
}
3.6.3 解决方案
根据业务需求选择合适的事务传播行为,如 Propagation.REQUIRED(默认值,表示如果当前存在事务,则加入该事务;如果不存在,则创建一个新事务)。
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Transactional(propagation = Propagation.REQUIRED)
public void saveUser(User user) {
// 保存用户的业务逻辑
}
}
七、多数据源问题
3.7.1 原因分析
在使用多数据源的Spring Boot应用中,如果没有正确配置事务管理器,@Transactional 注解可能会失效。因为Spring需要知道使用哪个事务管理器来管理事务。
3.7.2 解决方案
配置多个事务管理器,并在 @Transactional 注解中指定使用的事务管理器。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
@Configuration
public class TransactionConfig {
@Bean(name = "transactionManager1")
public PlatformTransactionManager transactionManager1(DataSource dataSource1) {
return new DataSourceTransactionManager(dataSource1);
}
@Bean(name = "transactionManager2")
public PlatformTransactionManager transactionManager2(DataSource dataSource2) {
return new DataSourceTransactionManager(dataSource2);
}
}
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Transactional(value = "transactionManager1")
public void saveUser(User user) {
// 保存用户的业务逻辑
}
}
八、代理对象未正确创建
3.8.1 原因分析
如果Spring的AOP代理机制没有正确配置,或者代理对象没有正确创建,@Transactional 注解将无法生效。
3.8.2 解决方案
确保在Spring Boot应用中正确配置了AOP自动代理。可以在启动类上添加 @EnableAspectJAutoProxy 注解。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
四、总结
@Transactional 注解在Spring Boot应用中是一个非常强大的事务管理工具,但在使用过程中需要注意上述8个隐藏场景,避免事务失效。通过深入理解这些场景的原因和解决方案,技术人员可以更好地使用 @Transactional 注解,确保数据的一致性和完整性。


860

被折叠的 条评论
为什么被折叠?



