在Java企业级应用开发中,事务管理是保证数据一致性的关键技术。Spring框架通过统一的事务抽象层,为开发者提供了简洁而强大的事务控制能力。我经历过多个金融级项目,深刻体会到合理使用事务对系统稳定性的重要性。
Spring事务管理主要分为两种模式:
实际开发中,95%的场景都会选择声明式事务,因为它具有以下优势:
重要提示:声明式事务的底层实现基于Spring AOP,这意味着只有public方法才能被事务代理拦截,这是很多新手容易踩的坑。
最简单的使用方式就是在方法上添加@Transactional注解:
java复制@Transactional
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
accountMapper.debit(fromId, amount);
accountMapper.credit(toId, amount);
}
这个转账方法会默认启用事务,如果任一SQL执行失败,整个操作都会回滚。但要注意几个关键特性:
传播行为是事务最复杂的部分,也是面试高频考点。我们通过实际案例来理解:
java复制@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private InventoryService inventoryService;
@Transactional
public void createOrder(OrderDTO orderDTO) {
// 主事务逻辑
orderMapper.insert(orderDTO);
// 调用库存服务
try {
inventoryService.deductStock(orderDTO.getSkuId(), orderDTO.getQuantity());
} catch (BusinessException e) {
// 库存不足不影响订单创建
log.warn("库存不足", e);
}
}
}
@Service
public class InventoryServiceImpl implements InventoryService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void deductStock(String skuId, int quantity) {
// 独立事务逻辑
inventoryMapper.reduceStock(skuId, quantity);
}
}
这里使用了REQUIRES_NEW传播行为,使得库存扣减:
这样即使库存扣减失败,订单记录仍然可以保存,符合业务需求。
Spring支持标准SQL隔离级别,默认使用数据库默认级别。在MySQL中建议这样配置:
java复制@Transactional(isolation = Isolation.REPEATABLE_READ)
public void concurrentUpdate(Long id) {
// 可重复读能防止不可重复读问题
User user = userMapper.selectById(id);
// ...业务处理
}
不同隔离级别的适用场景:
这是最常见的问题场景:
java复制@Service
public class UserServiceImpl implements UserService {
public void batchCreate(List<User> users) {
users.forEach(this::createUser); // 自调用事务失效!
}
@Transactional
public void createUser(User user) {
userMapper.insert(user);
}
}
解决方案有三种:
推荐方案1,保持代码清晰:
java复制@Service
@RequiredArgsConstructor
public class UserFacadeService {
private final UserService userService;
public void batchCreate(List<User> users) {
users.forEach(userService::createUser);
}
}
错误示范:
java复制@Transactional
public void processOrder(Order order) {
try {
orderMapper.insert(order);
inventoryService.deduct(order);
} catch (Exception e) {
log.error("处理失败", e); // 吞掉异常导致事务不会回滚!
}
}
正确处理方式:
java复制@Transactional(rollbackFor = Exception.class)
public void processOrder(Order order) throws BusinessException {
try {
orderMapper.insert(order);
inventoryService.deduct(order);
} catch (BusinessException e) {
log.error("业务异常", e);
throw e; // 重新抛出
}
}
典型反模式:
java复制@Transactional
public void complexBusiness(Long id) {
// 1. 查询数据
List<Item> items = queryItems(id);
// 2. 远程调用
ThirdResult result = thirdPartyClient.call(items);
// 3. 计算处理
Report report = heavyCalculate(result);
// 4. 保存结果
saveReport(report);
}
优化方案:
当项目使用多个数据源时,需要特殊配置:
java复制@Configuration
public class TransactionConfig {
@Bean
@Primary
public PlatformTransactionManager primaryTxManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public PlatformTransactionManager secondaryTxManager(@Qualifier("secondaryDS") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
@Service
public class CrossDatabaseService {
@Transactional("primaryTxManager")
public void primaryOperation() {
// 操作主库
}
@Transactional("secondaryTxManager")
public void secondaryOperation() {
// 操作从库
}
}
Spring支持事务事件监听,非常有用:
java复制@Component
@RequiredArgsConstructor
public class TransactionListener {
private final EventPublisher eventPublisher;
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void afterCommit(OrderCreatedEvent event) {
// 事务提交后发送消息
eventPublisher.publish(event);
}
}
理解Spring事务的实现机制有助于深度排查问题:
关键源码片段:
java复制// TransactionInterceptor核心逻辑
public Object invoke(MethodInvocation invocation) throws Throwable {
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
try {
Object retVal = invocation.proceed();
commitTransactionAfterReturning(txInfo);
return retVal;
} catch (Throwable ex) {
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
}
经过多个项目实践,我总结出以下经验:
典型配置示例:
java复制@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 3,
rollbackFor = BusinessException.class,
noRollbackFor = SystemException.class
)
public void businessOperation() {
// 业务逻辑
}
最后提醒:事务不是银弹,分布式系统还需要考虑最终一致性方案如Saga、TCC等模式。