目录
1. 声明式事务概述
1.1 什么是声明式事务
声明式事务是Spring通过AOP(面向切面编程)实现的事务管理功能。通过@Transactional注解,可以将事务管理代码与业务代码分离,降低代码耦合度。
1.2 基础配置
@Configuration
@EnableTransactionManagement // 启用事务管理
public class TransactionConfig {
// 事务管理器配置已经由Spring Boot自动完成
}
2. @Transactional注解详解
2.1 注解属性说明
@Service
public class TransactionAttributesDemo {
@Transactional(
// 1. 事务传播行为
propagation = Propagation.REQUIRED, // 默认值
// 2. 事务隔离级别
isolation = Isolation.READ_COMMITTED,
// 3. 事务超时时间(秒)
timeout = 30,
// 4. 是否只读事务
readOnly = false,
// 5. 回滚规则
rollbackFor = {BusinessException.class}, // 导致回滚的异常类
noRollbackFor = {ValidationException.class}, // 不回滚的异常类
// 6. 事务管理器
transactionManager = "transactionManager"
)
public void businessMethod() {
// 业务逻辑
}
}
2.2 事务传播行为详解
@Service
@Slf4j
public class TransactionPropagationDemo {
@Autowired
private UserService userService;
// 1. REQUIRED(默认):支持当前事务,如果不存在则创建新事务
@Transactional(propagation = Propagation.REQUIRED)
public void required() {
// 业务逻辑
}
// 2. REQUIRES_NEW:创建新事务,挂起当前事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requiresNew() {
// 业务逻辑
}
// 3. NESTED:如果当前存在事务,则创建嵌套事务
@Transactional(propagation = Propagation.NESTED)
public void nested() {
// 业务逻辑
}
// 4. SUPPORTS:支持当前事务,如果不存在则以非事务方式执行
@Transactional(propagation = Propagation.SUPPORTS)
public void supports() {
// 业务逻辑
}
// 5. NOT_SUPPORTED:以非事务方式执行,如果存在事务则挂起
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void notSupported() {
// 业务逻辑
}
// 6. MANDATORY:必须在事务中运行,如果不存在事务则抛出异常
@Transactional(propagation = Propagation.MANDATORY)
public void mandatory() {
// 业务逻辑
}
// 7. NEVER:以非事务方式执行,如果存在事务则抛出异常
@Transactional(propagation = Propagation.NEVER)
public void never() {
// 业务逻辑
}
}
2.3 事务隔离级别详解
@Service
public class TransactionIsolationDemo {
// 1. READ_UNCOMMITTED:读未提交
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void readUncommitted() {
// 可能读取到其他事务未提交的数据(脏读)
}
// 2. READ_COMMITTED:读已提交
@Transactional(isolation = Isolation.READ_COMMITTED)
public void readCommitted() {
// 只能读取已经提交的数据,但可能出现不可重复读
}
// 3. REPEATABLE_READ:可重复读
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void repeatableRead() {
// 同一事务内多次读取结果一致,但可能出现幻读
}
// 4. SERIALIZABLE:串行化
@Transactional(isolation = Isolation.SERIALIZABLE)
public void serializable() {
// 完全串行化执行,性能最差但是最安全
}
}
3. 事务失效场景
3.1 常见事务失效情况
@Service
public class TransactionInvalidDemo {
// 1. 非public方法
@Transactional
private void privateMethod() { // 事务无效
// 业务逻辑
}
// 2. 同类内部调用
public void outerMethod() {
innerMethod(); // 事务无效
}
@Transactional
public void innerMethod() {
// 业务逻辑
}
// 3. 异常被捕获但未抛出
@Transactional
public void catchException() {
try {
// 业务逻辑
throw new RuntimeException();
} catch (Exception e) {
log.error("异常", e);
// 异常被吃掉,事务无法回滚
}
}
// 4. 数据库引擎不支持事务(如MySQL的MyISAM)
@Transactional
public void mysqlEngine() {
// 使用不支持事务的表引擎,事务无效
}
// 5. 异常类型不匹配
@Transactional // 默认只回滚RuntimeException
public void exceptionType() throws Exception {
throw new Exception("检查异常不会回滚");
}
// 6. 事务传播行为设置错误
@Transactional(propagation = Propagation.NEVER)
public void wrongPropagation() {
// 在已有事务的情况下调用,将抛出异常
}
// 7. 未被Spring管理
class NonSpringBean {
@Transactional
public void method() { // 事务无效,因为不是Spring Bean
}
}
// 8. 方法final修饰
@Transactional
public final void finalMethod() { // 事务无效,因为final方法无法被代理
// 业务逻辑
}
}
4. 事务使用最佳实践
4.1 合理设置事务边界
@Service
@Slf4j
public class TransactionBoundaryDemo {
@Autowired
private UserRepository userRepository;
@Autowired
private OrderRepository orderRepository;
// 错误示例:事务边界太大
@Transactional
public void wrongBoundary() {
// 非事务操作:查询统计
long count = userRepository.count();
log.info("用户总数: {}", count);
// 事务操作:创建订单
createOrder();
// 非事务操作:发送通知
sendNotification();
}
// 正确示例:精确的事务边界
public void rightBoundary() {
// 非事务操作
long count = userRepository.count();
log.info("用户总数: {}", count);
// 事务操作
createOrderWithTransaction();
// 非事务操作
sendNotification();
}
@Transactional
public void createOrderWithTransaction() {
// 只包含需要事务的操作
createOrder();
}
}
4.2 正确处理事务异常
@Service
@Slf4j
public class TransactionExceptionDemo {
// 1. 定义自定义业务异常
public class BusinessException extends RuntimeException {
public BusinessException(String message) {
super(message);
}
}
// 2. 使用明确的回滚规则
@Transactional(rollbackFor = Exception.class)
public void handleBusinessException() {
try {
// 业务逻辑
riskyOperation();
} catch (BusinessException e) {
// 记录业务异常
log.error("业务异常", e);
throw e; // 重新抛出确保回滚
} catch (Exception e) {
// 记录系统异常
log.error("系统异常", e);
throw new RuntimeException("系统错误", e);
}
}
// 3. 处理特定异常不回滚
@Transactional(noRollbackFor = {ValidationException.class})
public void handleValidationException() {
try {
// 业务逻辑
validate();
} catch (ValidationException e) {
// 验证异常,记录但不回滚
log.warn("验证失败", e);
}
}
}
5. 实战示例
5.1 转账业务实现
@Service
@Slf4j
public class TransferService {
@Autowired
private AccountRepository accountRepository;
@Transactional(rollbackFor = Exception.class)
public void transfer(String fromAccount, String toAccount, BigDecimal amount) {
// 1. 参数校验
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("转账金额必须大于0");
}
// 2. 查询账户
Account from = accountRepository.findByAccountNo(fromAccount)
.orElseThrow(() -> new BusinessException("转出账户不存在"));
Account to = accountRepository.findByAccountNo(toAccount)
.orElseThrow(() -> new BusinessException("转入账户不存在"));
// 3. 余额检查
if (from.getBalance().compareTo(amount) < 0) {
throw new BusinessException("余额不足");
}
try {
// 4. 执行转账
from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));
// 5. 保存更改
accountRepository.save(from);
accountRepository.save(to);
// 6. 记录交易日志
logTransaction(from, to, amount);
} catch (Exception e) {
log.error("转账失败", e);
throw new BusinessException("转账操作失败");
}
}
private void logTransaction(Account from, Account to, BigDecimal amount) {
// 记录交易日志的逻辑
}
}
5.2 订单处理实现
@Service
@Slf4j
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private PaymentService paymentService;
@Autowired
private InventoryService inventoryService;
@Transactional(rollbackFor = Exception.class)
public Order createOrder(OrderRequest request) {
// 1. 创建订单
Order order = new Order();
order.setUserId(request.getUserId());
order.setAmount(calculateAmount(request));
order.setStatus(OrderStatus.CREATED);
// 2. 保存订单
order = orderRepository.save(order);
try {
// 3. 扣减库存(使用REQUIRES_NEW确保独立事务)
inventoryService.deductInventory(request.getItems());
// 4. 处理支付(使用REQUIRES_NEW确保独立事务)
paymentService.processPayment(order);
// 5. 更新订单状态
order.setStatus(OrderStatus.PAID);
order = orderRepository.save(order);
return order;
} catch (Exception e) {
// 6. 异常处理
order.setStatus(OrderStatus.FAILED);
orderRepository.save(order);
throw new BusinessException("订单处理失败", e);
}
}
private BigDecimal calculateAmount(OrderRequest request) {
// 计算订单金额的逻辑
return BigDecimal.ZERO;
}
}
关键点:
- 正确使用@Transactional注解及其属性
- 理解并避免事务失效的场景
- 合理设置事务边界和传播行为
- 正确处理事务中的异常
- 遵循事务使用的最佳实践
在实际开发中,声明式事务能满足大多数场景的需求,它提供了一种优雅的方式来管理事务,使开发者可以专注于业务逻辑的实现。