本文将全面剖析Spring事务失效的各种场景,提供完整的代码示例和解决方案,帮你彻底掌握事务管理的精髓。
1. 事务管理基础:Spring事务工作原理深度解析
在深入问题之前,我们先完整理解Spring声明式事务的工作机制:
1.1 Spring事务架构概览
java
// Spring事务管理的核心组件关系
@Component
public class TransactionArchitecture {
/*
* 事务管理核心流程:
* 1. 解析@Transactional注解
* 2. 创建AOP代理
* 3. 事务拦截器处理
* 4. 事务管理器协调
* 5. 连接资源管理
*/
}
// 事务拦截器核心逻辑(简化版)
public class TransactionInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 1. 获取事务属性
TransactionAttribute txAttr = getTransactionAttributeSource()
.getTransactionAttribute(invocation.getMethod(), invocation.getClass());
// 2. 创建或加入事务
TransactionStatus status = transactionManager.getTransaction(txAttr);
try {
// 3. 执行业务方法
Object result = invocation.proceed();
// 4. 提交事务
transactionManager.commit(status);
return result;
} catch (Exception ex) {
// 5. 回滚处理
completeTransactionAfterThrowing(txAttr, status, ex);
throw ex;
}
}
}
1.2 完整的事务配置示例
java
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager =
new DataSourceTransactionManager(dataSource);
// 完整的事务管理器配置
transactionManager.setNestedTransactionAllowed(true);
transactionManager.setRollbackOnCommitFailure(false);
transactionManager.setDefaultTimeout(30); // 30秒超时
return transactionManager;
}
@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
TransactionTemplate template = new TransactionTemplate(transactionManager);
template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
template.setTimeout(30);
return template;
}
}
2. 陷阱一:同类方法调用(最常见的坑)
2.1 问题完整复现
java
@Service
@Slf4j
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private InventoryService inventoryService;
/**
* 创建订单的主方法
*/
public OrderCreateResult createOrder(OrderCreateRequest request) {
log.info("开始创建订单: {}", request.getOrderNo());
// 1. 验证订单
validateOrder(request);
// 2. 保存订单(期望在事务中)
Order order = saveOrder(request); // ❌ 事务失效点
// 3. 更新库存
updateInventory(order);
// 4. 记录日志
logOrderCreate(order);
return new OrderCreateResult(true, "创建成功", order.getId());
}
/**
* 保存订单数据 - 期望在事务中执行
*/
@Transactional
public Order saveOrder(OrderCreateRequest request) {
log.info("保存订单数据");
Order order = convertToOrder(request);
order.setStatus(OrderStatus.CREATED);
// 保存订单主表
Order savedOrder = orderRepository.save(order);
// 保存订单明细
saveOrderItems(request.getItems(), savedOrder.getId());
// 模拟一个业务异常
if (request.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new BusinessException("订单金额必须大于0");
}
return savedOrder;
}
/**
* 保存订单明细
*/
@Transactional(propagation = Propagation.REQUIRED)
private void saveOrderItems(List<OrderItem> items, Long orderId) {
for (OrderItem item : items) {
item.setOrderId(orderId);
orderRepository.saveItem(item);
}
}
/**
* 更新库存
*/
private void updateInventory(Order order) {
inventoryService.deductInventory(order);
}
/**
* 记录订单创建日志
*/
private void logOrderCreate(Order order) {
// 记录操作日志
System.out.println("订单创建完成: " + order.getId());
}
}
2.2 问题深度分析
java
/**
* 问题分析服务 - 通过AOP原理解释为什么事务失效
*/
@Component
@Aspect
@Slf4j
public class TransactionAnalysisAspect {
/**
* 模拟Spring AOP代理的工作方式
*/
public void demonstrateProxyMechanism() {
/*
* 实际Spring创建的代理对象结构:
*
* ProxyOrderService (Spring AOP代理)
* - target: RealOrderService (原始对象)
* - advisors: [TransactionInterceptor]
*
* 当调用 proxy.saveOrder() 时:
* 1. 代理拦截方法调用
* 2. 执行TransactionInterceptor
* 3. 开启事务
* 4. 调用target.saveOrder()
* 5. 提交/回滚事务
*
* 当在同一个类中调用 this.saveOrder() 时:
* 1. 直接调用原始对象的方法
* 2. 绕过代理拦截器
* 3. 事务注解失效
*/
}
@Around("@annotation(transactional)")
public Object analyzeTransaction(ProceedingJoinPoint joinPoint,
Transactional transactional) throws Throwable {
String methodName = joinPoint.getSignature().getName();
boolean isTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
log.info("方法 {} 调用前,事务状态: {}", methodName, isTransactionActive);
Object result = joinPoint.proceed();
isTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
log.info("方法 {} 调用后,事务状态: {}", methodName, isTransactionActive);
return result;
}
}
2.3 完整解决方案
java
@Service
@Slf4j
public class CorrectOrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private InventoryService inventoryService;
@Autowired
private ApplicationContext applicationContext;
/**
* 方案1:通过ApplicationContext获取代理对象
*/
public OrderCreateResult createOrderV1(OrderCreateRequest request) {
validateOrder(request);
// 获取代理对象
CorrectOrderService proxy = applicationContext.getBean(CorrectOrderService.class);
Order order = proxy.saveOrder(request); // ✅ 通过代理调用
updateInventory(order);
logOrderCreate(order);
return new OrderCreateResult(true, "创建成功", order.getId());
}
/**
* 方案2:自我注入代理对象
*/
@Autowired
@Lazy // 避免循环依赖问题
private CorrectOrderService self;
public OrderCreateResult createOrderV2(OrderCreateRequest request) {
validateOrder(request);
Order order = self.saveOrder(request); // ✅ 通过注入的代理调用
updateInventory(order);
logOrderCreate(order);
return new OrderCreateResult(true, "创建成功", order.getId());
}
/**
* 方案3:重构方法结构,事务方法作为入口
*/
@Transactional
public OrderCreateResult createOrderV3(OrderCreateRequest request) {
validateOrder(request);
Order order = saveOrderInternal(request); // ✅ 在事务方法内调用
updateInventory(order);
logOrderCreate(order);
return new OrderCreateResult(true, "创建成功", order.getId());
}
/**
* 方案4:使用编程式事务
*/
@Autowired
private TransactionTemplate transactionTemplate;
public OrderCreateResult createOrderV4(OrderCreateRequest request) {
validateOrder(request);
Order order = transactionTemplate.execute(status -> {
try {
return saveOrderInternal(request);
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
});
updateInventory(order);
logOrderCreate(order);
return new OrderCreateResult(true, "创建成功", order.getId());
}
@Transactional
public Order saveOrder(OrderCreateRequest request) {
return saveOrderInternal(request);
}
/**
* 内部保存逻辑 - 不添加事务注解
*/
private Order saveOrderInternal(OrderCreateRequest request) {
log.info("保存订单数据");
Order order = convertToOrder(request);
order.setStatus(OrderStatus.CREATED);
Order savedOrder = orderRepository.save(order);
saveOrderItems(request.getItems(), savedOrder.getId());
if (request.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new BusinessException("订单金额必须大于0");
}
return savedOrder;
}
// 其他辅助方法...
private void saveOrderItems(List<OrderItem> items, Long orderId) {
for (OrderItem item : items) {
item.setOrderId(orderId);
orderRepository.saveItem(item);
}
}
private void validateOrder(OrderCreateRequest request) {
// 验证逻辑
}
private void updateInventory(Order order) {
inventoryService.deductInventory(order);
}
private void logOrderCreate(Order order) {
// 日志记录
}
}
3. 陷阱二:异常处理不当
3.1 异常处理完整示例
java
@Service
@Slf4j
public class ExceptionHandlingService {
@Autowired
private UserRepository userRepository;
@Autowired
private EmailService emailService;
/**
* 场景1:捕获异常导致事务不回滚
*/
@Transactional
public void registerUserV1(User user) {
try {
// 保存用户
userRepository.save(user);
// 发送欢迎邮件(可能抛出异常)
emailService.sendWelcomeEmail(user.getEmail());
} catch (Exception e) {
// ❌ 捕获了所有异常,事务不会回滚
log.error("用户注册失败: {}", user.getUsername(), e);
// 用户记录已保存,但邮件发送失败,数据不一致
}
}
/**
* 场景2:检查异常默认不回滚
*/
@Transactional
public void uploadUserAvatarV1(User user, InputStream avatarStream) throws IOException {
userRepository.update(user);
// 上传头像(抛出IOException)
fileService.uploadAvatar(user.getId(), avatarStream); // ❌ IOException不会触发回滚
// 如果上传失败,用户信息已更新,但头像缺失
}
/**
* 场景3:错误的异常类型指定
*/
@Transactional(rollbackFor = RuntimeException.class) // 默认值,但业务异常可能继承Exception
public void processBusinessV1(BusinessRequest request) {
userRepository.updateStatus(request.getUserId(), "PROCESSING");
// 业务处理,抛出自定义业务异常
businessService.process(request); // ❌ 如果BusinessException继承Exception,不会回滚
userRepository.updateStatus(request.getUserId(), "COMPLETED");
}
/**
* 正确解决方案
*/
/**
* 方案1:正确配置回滚异常
*/
@Transactional(rollbackFor = Exception.class) // ✅ 所有异常都回滚
public void registerUserV2(User user) {
try {
userRepository.save(user);
emailService.sendWelcomeEmail(user.getEmail());
} catch (EmailException e) {
// 只捕获邮件发送异常,不影响事务
log.warn("欢迎邮件发送失败: {}", user.getEmail(), e);
// 邮件发送失败不影响用户注册,事务继续提交
} catch (Exception e) {
// 其他异常重新抛出,触发回滚
log.error("用户注册失败: {}", user.getUsername(), e);
throw new BusinessException("用户注册失败", e);
}
}
/**
* 方案2:分层异常处理
*/
@Transactional(rollbackFor = BusinessException.class)
public void uploadUserAvatarV2(User user, InputStream avatarStream) {
try {
userRepository.update(user);
fileService.uploadAvatar(user.getId(), avatarStream);
} catch (IOException e) {
// 将检查异常转换为运行时异常
throw new BusinessException("头像上传失败", e);
}
}
/**
* 方案3:编程式异常控制
*/
@Transactional
public void processBusinessV2(BusinessRequest request) {
TransactionStatus status = TransactionAspectSupport.currentTransactionStatus();
try {
userRepository.updateStatus(request.getUserId(), "PROCESSING");
businessService.process(request);
userRepository.updateStatus(request.getUserId(), "COMPLETED");
} catch (BusinessException e) {
// 手动标记回滚
status.setRollbackOnly();
log.error("业务处理失败: {}", request.getRequestId(), e);
throw e;
}
}
/**
* 方案4:自定义异常回滚策略
*/
@Component
public class CustomRollbackPolicy {
@Autowired
private PlatformTransactionManager transactionManager;
public void executeWithRollbackPolicy(Runnable businessLogic,
Class<? extends Exception>... rollbackExceptions) {
DefaultTransactionAttribute transactionAttribute = new DefaultTransactionAttribute();
transactionAttribute.setRollbackRules(Arrays.asList(rollbackExceptions));
TransactionStatus status = transactionManager.getTransaction(transactionAttribute);
try {
businessLogic.run();
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
}
}
}
4. 陷阱三:事务传播机制误用
4.1 传播机制完整示例
java
@Service
@Slf4j
public class PropagationService {
@Autowired
private UserRepository userRepository;
@Autowired
private OrderRepository orderRepository;
@Autowired
private LogRepository logRepository;
@Autowired
private PropagationService self;
/**
* 错误示例:错误使用REQUIRED传播
*/
@Transactional
public void processBatchOrdersV1(List<Order> orders) {
int successCount = 0;
int failureCount = 0;
for (Order order : orders) {
try {
// ❌ 每个订单处理在同一个事务中
processSingleOrder(order);
successCount++;
} catch (Exception e) {
failureCount++;
log.error("订单处理失败: {}", order.getOrderNo(), e);
// 一个订单失败会导致整个批次回滚
}
}
log.info("批次处理完成: 成功={}, 失败={}", successCount, failureCount);
}
@Transactional(propagation = Propagation.REQUIRED)
public void processSingleOrder(Order order) {
// 订单处理逻辑
orderRepository.save(order);
inventoryService.deductStock(order);
if (order.getAmount().compareTo(BigDecimal.valueOf(10000)) > 0) {
throw new BusinessException("订单金额超限");
}
}
/**
* 正确使用传播机制
*/
/**
* 方案1:使用REQUIRES_NEW实现事务隔离
*/
public BatchProcessResult processBatchOrdersV2(List<Order> orders) {
BatchProcessResult result = new BatchProcessResult();
for (Order order : orders) {
try {
// ✅ 每个订单在独立事务中处理
self.processSingleOrderInNewTx(order);
result.incrementSuccess();
} catch (Exception e) {
result.incrementFailure();
log.error("订单处理失败: {}", order.getOrderNo(), e);
// 单个订单失败不影响其他订单
}
}
// 记录批次日志(在独立事务中)
self.logBatchResult(result);
return result;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void processSingleOrderInNewTx(Order order) {
orderRepository.save(order);
inventoryService.deductStock(order);
if (order.getAmount().compareTo(BigDecimal.valueOf(10000)) > 0) {
throw new BusinessException("订单金额超限");
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logBatchResult(BatchProcessResult result) {
logRepository.saveBatchLog(result);
}
/**
* 方案2:使用NESTED传播(需要支持保存点)
*/
@Transactional
public void processOrderWithNestedOperations(Order order) {
// 主订单处理
orderRepository.save(order);
try {
// ✅ 嵌套事务:可以独立回滚
self.processOrderItems(order.getItems());
} catch (Exception e) {
log.error("订单明细处理失败,继续处理其他逻辑", e);
// 明细处理失败不影响主订单
}
// 继续处理其他逻辑
updateOrderStatistics(order);
}
@Transactional(propagation = Propagation.NESTED)
public void processOrderItems(List<OrderItem> items) {
for (OrderItem item : items) {
itemRepository.save(item);
if (item.getQuantity() <= 0) {
throw new BusinessException("商品数量必须大于0");
}
}
}
/**
* 方案3:使用NOT_SUPPORTED暂停事务
*/
@Transactional
public void generateReportWithHeavyOperation() {
// 轻量级数据库操作
List<ReportData> data = reportRepository.getReportData();
// ✅ 暂停事务,执行重量级操作
String reportFile = self.generateLargeReportFile(data);
// 恢复事务,保存报告记录
reportRepository.saveReportRecord(reportFile);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public String generateLargeReportFile(List<ReportData> data) {
// 生成大型报告文件(耗时操作)
// 不在事务中,避免长事务和锁竞争
return reportService.generateExcelReport(data);
}
/**
* 传播机制使用指南
*/
public class PropagationGuide {
/*
* REQUIRED(默认):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
* 适用场景:大多数业务方法
*
* REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起
* 适用场景:需要独立提交/回滚的子任务
*
* NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则创建一个新的事务
* 适用场景:可部分回滚的子操作
*
* SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
* 适用场景:查询方法,可有可无事务
*
* NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起
* 适用场景:非事务性操作,如文件处理、远程调用
*
* NEVER:以非事务方式运行,如果当前存在事务,则抛出异常
* 适用场景:强制非事务环境
*
* MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
* 适用场景:强制要求事务环境
*/
}
}
5. 陷阱四:访问权限限制
5.1 访问权限完整示例
java
@Service
@Slf4j
public class AccessControlService {
/**
* 错误示例:非public方法使用事务注解
*/
@Transactional
void internalProcessV1() { // ❌ package-private方法
// 事务不会生效
repository.updateData();
}
@Transactional
protected void protectedProcessV1() { // ❌ protected方法
// 事务不会生效
repository.updateData();
}
@Transactional
private void privateProcessV1() { // ❌ private方法
// 事务不会生效
repository.updateData();
}
/**
* 解决方案
*/
/**
* 方案1:正确的访问权限
*/
@Transactional
public void publicProcess() { // ✅ public方法
repository.updateData();
}
/**
* 方案2:门面模式 + 内部方法调用
*/
public void complexBusinessProcess() {
// 步骤1:非事务性预处理
preProcess();
// 步骤2:事务性核心处理
transactionalCoreProcess();
// 步骤3:非事务性后处理
postProcess();
}
@Transactional
public void transactionalCoreProcess() {
// 核心业务逻辑
step1();
step2();
step3();
}
// 内部方法 - 不添加事务注解
private void preProcess() {
// 数据验证、参数校验等
}
private void step1() {
// 步骤1逻辑
}
private void step2() {
// 步骤2逻辑
}
private void step3() {
// 步骤3逻辑
}
private void postProcess() {
// 清理、通知等
}
/**
* 方案3:使用AspectJ模式(需要额外配置)
*/
@Configuration
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ) // 使用AspectJ代理
public class AspectJTransactionConfig {
// AspectJ模式下,非public方法的事务注解也会生效
// 但需要编译时或加载时织入
}
@Transactional
protected void aspectJProtectedProcess() {
// 在AspectJ模式下,protected方法的事务会生效
repository.updateData();
}
}
6. 陷阱五:数据库配置问题
6.1 数据库配置完整示例
java
@Service
@Slf4j
public class DatabaseConfigService {
/**
* 错误示例:使用不支持事务的存储引擎
*/
@Entity
@Table(name = "operation_logs")
public class OperationLog {
// 如果数据库表使用MyISAM引擎,事务将失效
}
@Transactional
public void batchInsertLogsV1(List<OperationLog> logs) {
for (OperationLog log : logs) {
logRepository.save(log); // ❌ MyISAM不支持事务
}
// 即使发生异常,已插入的数据也不会回滚
}
/**
* 解决方案
*/
/**
* 方案1:确保使用InnoDB引擎
*/
@Entity
@Table(name = "operation_logs",
options = "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4")
public class OperationLog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String content;
@CreationTimestamp
private LocalDateTime createTime;
}
/**
* 方案2:数据库表DDL验证
*/
@Component
public class TableEngineValidator {
@Autowired
private JdbcTemplate jdbcTemplate;
@PostConstruct
public void validateTableEngines() {
String sql = "SELECT TABLE_NAME, ENGINE FROM information_schema.TABLES " +
"WHERE TABLE_SCHEMA = DATABASE() AND ENGINE != 'InnoDB'";
List<Map<String, Object>> results = jdbcTemplate.queryForList(sql);
if (!results.isEmpty()) {
log.warn("发现非InnoDB引擎的表: {}", results);
// 可以抛出异常或记录警告
}
}
}
/**
* 方案3:编程式事务 + 手动回滚补偿
*/
@Service
public class CompensationService {
@Autowired
private JdbcTemplate jdbcTemplate;
public void batchInsertWithCompensation(List<OperationLog> logs) {
List<Long> insertedIds = new ArrayList<>();
try {
for (OperationLog log : logs) {
// 手动插入并记录ID
Long id = insertLogAndReturnId(log);
insertedIds.add(id);
}
// 业务验证
validateBusinessRules(logs);
} catch (Exception e) {
// 手动回滚:删除已插入的记录
rollbackInsertedLogs(insertedIds);
throw new BusinessException("批量插入失败,已回滚", e);
}
}
private Long insertLogAndReturnId(OperationLog log) {
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(
"INSERT INTO operation_logs (content) VALUES (?)",
Statement.RETURN_GENERATED_KEYS
);
ps.setString(1, log.getContent());
return ps;
}, keyHolder);
return keyHolder.getKey().longValue();
}
private void rollbackInsertedLogs(List<Long> ids) {
if (!ids.isEmpty()) {
String deleteSql = "DELETE FROM operation_logs WHERE id IN (" +
ids.stream().map(String::valueOf).collect(Collectors.joining(",")) + ")";
jdbcTemplate.update(deleteSql);
}
}
}
}
7. 陷阱六:连接池配置问题
7.1 连接池配置完整示例
java
@Configuration
public class DataSourceConfig {
/**
* 错误示例:自动提交设置冲突
*/
@Bean
public DataSource wrongDataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("password");
// ❌ 连接池自动提交与事务管理冲突
dataSource.setAutoCommit(true);
return dataSource;
}
/**
* 正确配置
*/
@Bean
@Primary
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("password");
// ✅ 正确的连接池配置
dataSource.setAutoCommit(false); // 让Spring管理事务提交
dataSource.setConnectionTimeout(30000);
dataSource.setIdleTimeout(600000);
dataSource.setMaxLifetime(1800000);
dataSource.setMaximumPoolSize(20);
dataSource.setMinimumIdle(5);
// 事务相关配置
dataSource.setTransactionIsolation("TRANSACTION_READ_COMMITTED");
dataSource.setLeakDetectionThreshold(60000);
return dataSource;
}
/**
* 连接池监控
*/
@Component
public class DataSourceMonitor {
@Autowired
private DataSource dataSource;
@Scheduled(fixedRate = 30000) // 每30秒监控一次
public void monitorDataSource() {
if (dataSource instanceof HikariDataSource) {
HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
HikariPoolMXBean pool = hikariDataSource.getHikariPoolMXBean();
log.info("连接池状态: 活跃={}, 空闲={}, 等待={}, 总数={}",
pool.getActiveConnections(),
pool.getIdleConnections(),
pool.getThreadsAwaitingConnection(),
pool.getTotalConnections());
// 检查连接泄漏
if (pool.getActiveConnections() > 15) {
log.warn("活跃连接数过高,可能存在连接泄漏");
}
}
}
}
}
8. 陷阱七:多数据源事务混淆
8.1 多数据源完整配置
java
@Configuration
public class MultiDataSourceConfig {
/**
* 主数据源
*/
@Bean
@Primary
@ConfigurationProperties("spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
/**
* 从数据源
*/
@Bean
@ConfigurationProperties("spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
/**
* 主数据源事务管理器
*/
@Bean
@Primary
public PlatformTransactionManager primaryTransactionManager() {
return new DataSourceTransactionManager(primaryDataSource());
}
/**
* 从数据源事务管理器
*/
@Bean
public PlatformTransactionManager secondaryTransactionManager() {
return new DataSourceTransactionManager(secondaryDataSource());
}
/**
* JPA实体管理器工厂
*/
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(primaryDataSource())
.packages("com.example.primary.entity")
.persistenceUnit("primary")
.build();
}
@Bean
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(secondaryDataSource())
.packages("com.example.secondary.entity")
.persistenceUnit("secondary")
.build();
}
}
/**
* 多数据源服务示例
*/
@Service
@Slf4j
public class MultiDataSourceService {
@Autowired
@Qualifier("primaryTransactionManager")
private PlatformTransactionManager primaryTxManager;
@Autowired
@Qualifier("secondaryTransactionManager")
private PlatformTransactionManager secondaryTxManager;
@Autowired
private PrimaryRepository primaryRepository;
@Autowired
private SecondaryRepository secondaryRepository;
/**
* 错误示例:错误的事务管理器使用
*/
@Transactional // ❌ 使用默认事务管理器,只能管理主数据源
public void crossDataSourceOperationV1(CrossDataRequest request) {
// 操作主数据源
primaryRepository.save(request.getPrimaryData());
// 操作从数据源 - 不在事务管理中!
secondaryRepository.save(request.getSecondaryData());
// 如果这里发生异常,主数据源会回滚,但从数据源不会
}
/**
* 解决方案1:分别使用不同的事务管理器
*/
public void crossDataSourceOperationV2(CrossDataRequest request) {
// 主数据源操作
primaryTxManager.execute(status -> {
primaryRepository.save(request.getPrimaryData());
return null;
});
// 从数据源操作
secondaryTxManager.execute(status -> {
secondaryRepository.save(request.getSecondaryData());
return null;
});
// 每个操作在独立事务中,但无法保证原子性
}
/**
* 解决方案2:使用分布式事务(如Atomikos)
*/
@Configuration
@EnableTransactionManagement
public static class JtaTransactionConfig {
@Bean
public JtaTransactionManager transactionManager() {
UserTransactionManager userTransactionManager = new UserTransactionManager();
UserTransaction userTransaction = new UserTransactionImp();
return new JtaTransactionManager(userTransaction, userTransactionManager);
}
}
@Transactional // 使用JTA事务管理器
public void crossDataSourceOperationV3(CrossDataRequest request) {
// 在分布式事务中,两个数据源的操作会一起提交或回滚
primaryRepository.save(request.getPrimaryData());
secondaryRepository.save(request.getSecondaryData());
}
/**
* 解决方案3:最终一致性模式
*/
public void crossDataSourceOperationV4(CrossDataRequest request) {
// 步骤1:主数据源操作
Long primaryId = savePrimaryData(request.getPrimaryData());
try {
// 步骤2:从数据源操作
saveSecondaryData(request.getSecondaryData(), primaryId);
// 步骤3:标记主数据完成
markPrimaryDataCompleted(primaryId);
} catch (Exception e) {
// 步骤4:补偿操作
compensatePrimaryData(primaryId);
throw new BusinessException("跨数据源操作失败", e);
}
}
@Transactional("primaryTransactionManager")
public Long savePrimaryData(PrimaryData data) {
PrimaryData saved = primaryRepository.save(data);
return saved.getId();
}
@Transactional("secondaryTransactionManager")
public void saveSecondaryData(SecondaryData data, Long primaryId) {
data.setPrimaryId(primaryId);
secondaryRepository.save(data);
}
@Transactional("primaryTransactionManager")
public void markPrimaryDataCompleted(Long primaryId) {
primaryRepository.updateStatus(primaryId, "COMPLETED");
}
@Transactional("primaryTransactionManager")
public void compensatePrimaryData(Long primaryId) {
primaryRepository.updateStatus(primaryId, "FAILED");
// 可以记录补偿日志等
}
}
9. 陷阱八:异步执行事务丢失
9.1 异步事务完整示例
java
@Service
@Slf4j
@EnableAsync
public class AsyncTransactionService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private EmailService emailService;
@Autowired
private AsyncTransactionService self;
/**
* 错误示例:异步方法中的事务注解
*/
@Transactional
@Async
public void asyncProcessOrderV1(Order order) {
// ❌ 异步方法中的事务注解通常不会按预期工作
orderRepository.updateStatus(order.getId(), "PROCESSING");
// 复杂的业务处理
processComplexBusiness(order);
orderRepository.updateStatus(order.getId(), "COMPLETED");
// 事务上下文可能丢失,回滚机制不可靠
}
/**
* 解决方案1:同步处理核心事务,异步处理非核心操作
*/
@Transactional
public void processOrderV2(Order order) {
// 步骤1:同步处理核心业务(在事务中)
orderRepository.updateStatus(order.getId(), "PROCESSING");
processCoreBusiness(order);
orderRepository.updateStatus(order.getId(), "COMPLETED");
// 步骤2:异步处理非核心操作(不在事务中)
self.asyncSendNotifications(order);
}
@Async
public void asyncSendNotifications(Order order) {
try {
// 发送邮件通知
emailService.sendOrderCompleteEmail(order);
// 发送短信通知
smsService.sendOrderCompleteSms(order);
// 其他非核心操作
logService.recordOperationLog(order);
} catch (Exception e) {
// 异步操作失败不影响主流程
log.error("异步通知发送失败: {}", order.getId(), e);
}
}
/**
* 解决方案2:使用@TransactionalEventListener
*/
@Transactional
public void processOrderV3(Order order) {
// 核心业务处理
orderRepository.updateStatus(order.getId(), "PROCESSING");
processCoreBusiness(order);
orderRepository.updateStatus(order.getId(), "COMPLETED");
// 发布事务事件
applicationEventPublisher.publishEvent(new OrderCompletedEvent(this, order));
}
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Async
public void handleOrderCompletedEvent(OrderCompletedEvent event) {
// 只有在主事务提交后才会执行
Order order = event.getOrder();
try {
emailService.sendOrderCompleteEmail(order);
smsService.sendOrderCompleteSms(order);
} catch (Exception e) {
log.error("订单完成后续处理失败: {}", order.getId(), e);
}
}
/**
* 解决方案3:编程式事务 + 异步执行
*/
@Autowired
private TransactionTemplate transactionTemplate;
@Async
public void asyncProcessWithProgrammaticTx(Order order) {
// 在异步线程中创建新事务
transactionTemplate.execute(status -> {
try {
orderRepository.updateStatus(order.getId(), "PROCESSING");
processCoreBusiness(order);
orderRepository.updateStatus(order.getId(), "COMPLETED");
return null;
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
});
}
/**
* 异步配置
*/
@Configuration
@EnableAsync
public static class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("AsyncTx-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex, method, params) -> {
log.error("异步方法执行异常: {}.{}", method.getDeclaringClass().getName(), method.getName(), ex);
};
}
}
}
10. 陷阱九:事务超时与只读设置
10.1 超时与只读配置完整示例
java
@Service
@Slf4j
public class TimeoutReadonlyService {
@Autowired
private UserRepository userRepository;
@Autowired
private OrderRepository orderRepository;
/**
* 错误示例:长事务无超时设置
*/
@Transactional
public void generateComplexReportV1(ReportRequest request) {
// 步骤1:查询大量数据
List<User> users = userRepository.findAllActiveUsers(); // 可能很慢
// 步骤2:复杂计算
processComplexCalculation(users); // 可能很慢
// 步骤3:生成报告
generateReportFile(users); // 可能很慢
// ❌ 没有超时设置,可能长时间占用数据库连接
}
/**
* 错误示例:写操作使用只读事务
*/
@Transactional(readOnly = true) // ❌ 只读事务
public void updateUserStatisticsV1() {
// 尝试在只读事务中执行写操作
userRepository.updateStatistics(); // 可能失败或行为异常
}
/**
* 正确配置示例
*/
/**
* 方案1:合理设置超时时间
*/
@Transactional(timeout = 30) // ✅ 30秒超时
public void generateComplexReportV2(ReportRequest request) {
try {
List<User> users = userRepository.findAllActiveUsers();
processComplexCalculation(users);
generateReportFile(users);
} catch (TransactionTimedOutException e) {
log.error("报告生成超时: {}", request, e);
throw new BusinessException("报告生成超时,请重试");
}
}
/**
* 方案2:只读事务用于查询操作
*/
@Transactional(readOnly = true, timeout = 10)
public List<UserReport> getUserReports() {
// 纯查询操作,使用只读事务
return userRepository.generateUserReports();
}
/**
* 方案3:拆分长事务
*/
public void generateComplexReportV3(ReportRequest request) {
// 步骤1:快速查询必要数据
List<Long> userIds = userRepository.findActiveUserIds();
// 步骤2:分批处理
Lists.partition(userIds, 1000).forEach(batch -> {
processUserBatch(batch, request);
});
}
@Transactional(timeout = 10)
public void processUserBatch(List<Long> userIds, ReportRequest request) {
List<User> users = userRepository.findByIdIn(userIds);
processBatchCalculation(users);
// 每个批次在独立短事务中处理
}
/**
* 方案4:动态超时配置
*/
@Autowired
private TransactionTemplate transactionTemplate;
public void processWithDynamicTimeout(boolean isComplex) {
int timeout = isComplex ? 60 : 10;
TransactionTemplate dynamicTemplate = new TransactionTemplate(
transactionTemplate.getTransactionManager()
);
dynamicTemplate.setTimeout(timeout);
dynamicTemplate.execute(status -> {
// 业务逻辑
return null;
});
}
/**
* 事务监控和诊断
*/
@Component
@Aspect
@Slf4j
public class TransactionMonitorAspect {
private final ThreadLocal<Long> startTime = new ThreadLocal<>();
@Around("@annotation(transactional)")
public Object monitorTransaction(ProceedingJoinPoint joinPoint,
Transactional transactional) throws Throwable {
startTime.set(System.currentTimeMillis());
String methodName = joinPoint.getSignature().toShortString();
int timeout = transactional.timeout();
log.info("事务开始: {}, 超时时间: {}秒", methodName, timeout);
try {
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - startTime.get();
if (duration > timeout * 1000L) {
log.warn("事务执行时间接近超时: {}ms, 方法: {}", duration, methodName);
}
return result;
} catch (TransactionTimedOutException e) {
log.error("事务超时: {}, 配置超时: {}秒", methodName, timeout, e);
throw e;
} finally {
startTime.remove();
}
}
}
}
11. 完整的事务测试方案
11.1 事务测试完整示例
java
@SpringBootTest
@TestPropertySource(properties = {
"spring.datasource.url=jdbc:h2:mem:testdb",
"spring.jpa.hibernate.ddl-auto=create-drop"
})
@Slf4j
class TransactionalTest {
@Autowired
private OrderService orderService;
@Autowired
private OrderRepository orderRepository;
@Autowired
private PlatformTransactionManager transactionManager;
@Test
void testTransactionRollbackOnException() {
// 准备测试数据
OrderCreateRequest request = createTestRequest();
request.setAmount(BigDecimal.ZERO); // 触发异常
// 执行测试
assertThrows(BusinessException.class, () -> {
orderService.createOrder(request);
});
// 验证事务回滚
assertFalse(orderRepository.existsByOrderNo(request.getOrderNo()));
}
@Test
void testTransactionCommitSuccess() {
// 准备测试数据
OrderCreateRequest request = createTestRequest();
request.setAmount(BigDecimal.TEN);
// 执行测试
OrderCreateResult result = orderService.createOrder(request);
// 验证事务提交
assertTrue(result.isSuccess());
assertTrue(orderRepository.existsByOrderNo(request.getOrderNo()));
}
@Test
void testTransactionPropagation() {
TransactionStatus status = transactionManager.getTransaction(
new DefaultTransactionAttribute()
);
try {
// 验证事务传播
boolean isActive = TransactionSynchronizationManager.isActualTransactionActive();
assertTrue(isActive);
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
assertNotNull(transactionName);
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
}
@Test
void testTransactionTimeout() {
TransactionTemplate template = new TransactionTemplate(transactionManager);
template.setTimeout(1); // 1秒超时
assertThrows(TransactionTimedOutException.class, () -> {
template.execute(status -> {
// 模拟长时间操作
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return null;
});
});
}
/**
* 事务测试配置
*/
@TestConfiguration
static class TestConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.setName("testdb")
.build();
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
private OrderCreateRequest createTestRequest() {
OrderCreateRequest request = new OrderCreateRequest();
request.setOrderNo("TEST_" + System.currentTimeMillis());
request.setAmount(BigDecimal.valueOf(100));
request.setUserId(1L);
// 设置其他必要字段
return request;
}
}
12. 事务监控与运维
12.1 完整的事务监控方案
java
@Component
@Slf4j
public class TransactionMonitor {
@Autowired
private MeterRegistry meterRegistry;
private final Counter transactionCounter;
private final Timer transactionTimer;
private final Counter rollbackCounter;
public TransactionMonitor(MeterRegistry meterRegistry) {
this.transactionCounter = meterRegistry.counter("transaction.total");
this.transactionTimer = meterRegistry.timer("transaction.duration");
this.rollbackCounter = meterRegistry.counter("transaction.rollback");
}
@EventListener
public void monitorTransactionEvent(ApplicationEvent event) {
if (event instanceof TransactionCompletedEvent) {
transactionCounter.increment();
}
}
/**
* 事务健康检查
*/
@Component
public class TransactionHealthIndicator implements HealthIndicator {
@Autowired
private DataSource dataSource;
@Override
public Health health() {
try {
if (dataSource instanceof HikariDataSource) {
HikariDataSource hikari = (HikariDataSource) dataSource;
HikariPoolMXBean pool = hikari.getHikariPoolMXBean();
int activeConnections = pool.getActiveConnections();
int maxPoolSize = hikari.getMaximumPoolSize();
Health.Builder status = activeConnections > maxPoolSize * 0.8 ?
Health.down() : Health.up();
return status
.withDetail("activeConnections", activeConnections)
.withDetail("idleConnections", pool.getIdleConnections())
.withDetail("totalConnections", pool.getTotalConnections())
.withDetail("threadsAwaiting", pool.getThreadsAwaitingConnection())
.build();
}
return Health.unknown().build();
} catch (Exception e) {
return Health.down(e).build();
}
}
}
/**
* 长事务检测和告警
*/
@Component
public class LongTransactionDetector {
private final Map<String, Long> transactionStartTimes = new ConcurrentHashMap<>();
@Autowired
private PlatformTransactionManager transactionManager;
@Scheduled(fixedRate = 30000) // 每30秒检查一次
public void detectLongTransactions() {
long currentTime = System.currentTimeMillis();
long threshold = 30000; // 30秒阈值
transactionStartTimes.entrySet().removeIf(entry -> {
long duration = currentTime - entry.getValue();
if (duration > threshold) {
log.warn("发现长事务: {}, 持续时间: {}ms", entry.getKey(), duration);
// 发送告警
alertLongTransaction(entry.getKey(), duration);
return true;
}
return false;
});
}
public void recordTransactionStart(String transactionId) {
transactionStartTimes.put(transactionId, System.currentTimeMillis());
}
public void recordTransactionEnd(String transactionId) {
transactionStartTimes.remove(transactionId);
}
private void alertLongTransaction(String transactionId, long duration) {
// 发送邮件、短信、钉钉等告警
log.error("长事务告警: {}, 持续时间: {}ms", transactionId, duration);
}
}
}
13. 总结:事务管理最佳实践
13.1 事务设计原则
java
/**
* 事务管理最佳实践总结
*/
public class TransactionBestPractices {
/*
* 1. 事务边界原则
* - 事务应该围绕业务用例,而不是技术细节
* - 保持事务简短,避免长事务
* - 在服务层管理事务,而不是在DAO层
*/
/*
* 2. 异常处理原则
* - 明确指定回滚异常类型
* - 避免在事务中捕获所有异常
* - 将检查异常转换为非检查异常
*/
/*
* 3. 性能优化原则
* - 查询操作使用只读事务
* - 合理设置事务超时时间
* - 避免在事务中进行远程调用和IO操作
*/
/*
* 4. 复杂度控制原则
* - 避免嵌套事务的过度使用
* - 明确事务传播行为
* - 使用编程式事务处理复杂场景
*/
/*
* 5. 监控运维原则
* - 监控事务执行时间和成功率
* - 设置长事务告警
* - 定期审查事务配置
*/
}
13.2 事务配置检查清单
java
@Component
public class TransactionChecklist {
/**
* 事务配置验证
*/
public void validateTransactionConfiguration() {
checkPublicMethods();
checkExceptionConfiguration();
checkTimeoutSettings();
checkDataSourceConfiguration();
checkAopConfiguration();
}
private void checkPublicMethods() {
// 验证所有@Transactional方法都是public
}
private void checkExceptionConfiguration() {
// 验证回滚异常配置
}
private void checkTimeoutSettings() {
// 验证超时时间配置
}
private void checkDataSourceConfiguration() {
// 验证数据源和连接池配置
}
private void checkAopConfiguration() {
// 验证AOP代理配置
}
}
通过以上完整的示例和解决方案,相信你已经对Spring事务失效的各种场景有了全面的理解。记住,事务管理不仅是技术问题,更是架构设计和业务理解的体现。
在你的项目中还遇到过哪些有趣的事务问题?欢迎在评论区分享交流! 🚀