记录几条SpringBoot事务管理中踩过的坑及解决办法:
1. 自调用问题
问题描述
在同一个类中,一个非事务方法调用另一个有 @Transactional
注解的事务方法,事务不会生效。因为 Spring 的事务管理是基于 AOP 代理实现的,自调用时不会经过代理对象,所以事务注解不起作用。
示例代码
@Service
public class UserService {public void nonTransactionalMethod() {// 调用事务方法this.transactionalMethod(); }@Transactionalpublic void transactionalMethod() {// 数据库操作}
}
解决办法
可以通过注入自身的代理对象来解决自调用问题,或者将事务方法提取到另一个服务类中。
@Service
public class UserService {@Autowiredprivate UserService self;public void nonTransactionalMethod() {// 通过代理对象调用事务方法self.transactionalMethod(); }@Transactionalpublic void transactionalMethod() {// 数据库操作}
}
2. 异常捕获问题
问题描述
在事务方法中捕获了异常但没有重新抛出,会导致事务不会回滚。因为 Spring 默认只对未检查异常(如 RuntimeException
及其子类)进行回滚,捕获异常后没有抛出,Spring 无法感知到异常,就不会触发回滚机制。
示例代码
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;@Transactionalpublic void saveUser(User user) {try {userRepository.save(user);// 模拟异常int result = 1 / 0; } catch (Exception e) {// 捕获异常但未重新抛出e.printStackTrace(); }}
}
解决办法
在捕获异常后,根据业务需求重新抛出未检查异常,或者在 @Transactional
注解中指定需要回滚的异常类型。
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;@Transactional(rollbackFor = Exception.class)public void saveUser(User user) {try {userRepository.save(user);// 模拟异常int result = 1 / 0; } catch (Exception e) {// 重新抛出异常throw new RuntimeException(e); }}
}
3. 事务传播行为误用
问题描述
在嵌套事务中,如果错误地使用了事务传播行为,可能会导致事务管理不符合预期。例如,在需要独立事务的场景下使用了 REQUIRED
传播行为,会使内层事务加入到外层事务中,当外层事务回滚时,内层事务也会回滚。
示例代码
@Service
public class OrderService {@Autowiredprivate PaymentService paymentService;@Transactionalpublic void createOrder(Order order) {// 创建订单// 调用支付服务paymentService.processPayment(order); }
}@Service
public class PaymentService {@Transactional(propagation = Propagation.REQUIRED)public void processPayment(Order order) {// 处理支付}
}
解决办法
根据业务需求选择合适的事务传播行为。如果需要独立事务,可以使用 REQUIRES_NEW
传播行为。
@Service
public class PaymentService {@Transactional(propagation = Propagation.REQUIRES_NEW)public void processPayment(Order order) {// 处理支付}
}
4. 数据库隔离级别不匹配
问题描述
在不同的数据库和业务场景下,如果使用了不匹配的事务隔离级别,可能会出现数据不一致的问题。例如,在高并发场景下使用了较低的隔离级别,可能会导致脏读、不可重复读和幻读问题。
示例代码
@Service
public class ProductService {@Transactional(isolation = Isolation.READ_UNCOMMITTED)public Product getProductById(Long id) {// 查询产品信息return productRepository.findById(id).orElse(null);}
}
解决办法
根据业务需求和数据库特性选择合适的隔离级别。在高并发场景下,为了保证数据一致性,可以使用较高的隔离级别,如 REPEATABLE_READ
或 SERIALIZABLE
,但要注意可能会影响并发性能。
@Service
public class ProductService {@Transactional(isolation = Isolation.REPEATABLE_READ)public Product getProductById(Long id) {// 查询产品信息return productRepository.findById(id).orElse(null);}
}
5. 多数据源事务问题
问题描述
在使用多数据源的 Spring Boot 应用中,如果没有正确配置事务管理器,可能会导致事务管理混乱。不同数据源需要不同的事务管理器来管理事务。
解决办法
为每个数据源配置独立的事务管理器,并在 @Transactional
注解中指定使用的事务管理器。
@Configuration
public class DataSourceConfig {@Bean(name = "dataSource1")public DataSource dataSource1() {// 配置数据源 1return DataSourceBuilder.create().build();}@Bean(name = "dataSource2")public DataSource dataSource2() {// 配置数据源 2return DataSourceBuilder.create().build();}@Bean(name = "transactionManager1")public PlatformTransactionManager transactionManager1(@Qualifier("dataSource1") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}@Bean(name = "transactionManager2")public PlatformTransactionManager transactionManager2(@Qualifier("dataSource2") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}
}@Service
public class MultiDataSourceService {@Transactional("transactionManager1")public void operationOnDataSource1() {// 对数据源 1 进行操作}@Transactional("transactionManager2")public void operationOnDataSource2() {// 对数据源 2 进行操作}
}