16-Spring-Framework-事务管理详解

6 阅读14分钟

Spring 事务管理详解

一、知识概述

事务管理是企业级应用开发中不可或缺的部分,Spring 提供了强大且灵活的事务管理能力,支持声明式事务和编程式事务两种方式。声明式事务通过 AOP 实现,使得业务代码与事务管理代码完全分离,大大简化了开发。

Spring 事务管理的核心内容:

  • 事务管理器:PlatformTransactionManager
  • 事务定义:TransactionDefinition
  • 事务传播行为:Propagation
  • 事务隔离级别:Isolation
  • 事务回滚规则:Rollback rules

理解 Spring 事务管理,是开发企业级 Java 应用的必备技能。

二、知识点详细讲解

2.1 事务的基本概念

ACID 特性
  1. 原子性(Atomicity):事务是不可分割的工作单位
  2. 一致性(Consistency):事务使数据库从一个一致状态变到另一个一致状态
  3. 隔离性(Isolation):多个事务并发执行时互不干扰
  4. 持久性(Durability):事务一旦提交,对数据库的改变是永久的
事务问题
  1. 脏读:读到其他事务未提交的数据
  2. 不可重复读:同一事务两次读取结果不同(数据被修改)
  3. 幻读:同一事务两次读取记录数不同(数据被增删)

2.2 Spring 事务抽象

核心接口
// 事务管理器
public interface PlatformTransactionManager {
    TransactionStatus getTransaction(TransactionDefinition definition);
    void commit(TransactionStatus status);
    void rollback(TransactionStatus status);
}

// 事务定义
public interface TransactionDefinition {
    int getPropagationBehavior();  // 传播行为
    int getIsolationLevel();       // 隔离级别
    int getTimeout();              // 超时时间
    boolean isReadOnly();          // 是否只读
    String getName();              // 事务名称
}

// 事务状态
public interface TransactionStatus {
    boolean isNewTransaction();
    boolean hasSavepoint();
    void setRollbackOnly();
    boolean isRollbackOnly();
    boolean isCompleted();
}

2.3 事务传播行为

传播行为说明
REQUIRED有事务则加入,无则新建(默认)
SUPPORTS有事务则加入,无则以非事务运行
MANDATORY必须在事务中运行,否则抛异常
REQUIRES_NEW总是新建事务,挂起当前事务
NOT_SUPPORTED以非事务运行,挂起当前事务
NEVER以非事务运行,有事务则抛异常
NESTED有事务则嵌套事务,无则新建
传播行为详解
REQUIRED(默认):
调用者有事务 → 加入调用者事务
调用者无事务 → 新建事务

REQUIRES_NEW:
调用者有事务 → 挂起调用者事务,新建独立事务
调用者无事务 → 新建事务

NESTED:
调用者有事务 → 创建嵌套事务(savepoint)
调用者无事务 → 新建事务

2.4 事务隔离级别

隔离级别脏读不可重复读幻读
READ_UNCOMMITTED
READ_COMMITTED
REPEATABLE_READ
SERIALIZABLE

2.5 声明式事务

@Transactional 注解
@Transactional(
    propagation = Propagation.REQUIRED,    // 传播行为
    isolation = Isolation.DEFAULT,         // 隔离级别
    timeout = -1,                          // 超时时间(秒)
    readOnly = false,                      // 是否只读
    rollbackFor = {},                      // 回滚异常类型
    noRollbackFor = {},                    // 不回滚异常类型
    value = ""                             // 事务管理器名称
)
回滚规则
  • 默认:RuntimeException 和 Error 回滚
  • checked 异常默认不回滚
  • 通过 rollbackFor 指定回滚异常

2.6 事务失效场景

  1. 方法非 public
  2. 自调用问题:同类内部方法调用
  3. 异常被捕获:异常未抛出
  4. 异常类型错误:checked 异常未指定 rollbackFor
  5. 数据库不支持事务
  6. 事务传播行为设置错误
  7. 代理对象问题

三、代码示例

3.1 声明式事务基础

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;

// 实体类
public class User {
    private Long id;
    private String username;
    private BigDecimal balance;
    
    public User() {}
    
    public User(Long id, String username, BigDecimal balance) {
        this.id = id;
        this.username = username;
        this.balance = balance;
    }
    
    // getter/setter
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public BigDecimal getBalance() { return balance; }
    public void setBalance(BigDecimal balance) { this.balance = balance; }
    
    @Override
    public String toString() {
        return "User{id=" + id + ", username='" + username + "', balance=" + balance + "}";
    }
}

// DAO 层
@Repository
public class UserDao {
    
    private final Map<Long, User> database = new ConcurrentHashMap<>();
    
    public User findById(Long id) {
        return database.get(id);
    }
    
    public void save(User user) {
        database.put(user.getId(), user);
    }
    
    public void updateBalance(Long id, BigDecimal amount) {
        User user = database.get(id);
        if (user != null) {
            user.setBalance(user.getBalance().add(amount));
        }
    }
}

// Service 层 - 声明式事务
@Service
public class UserService {
    
    private final UserDao userDao;
    
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
    
    // 基本事务配置
    @Transactional
    public void createUser(User user) {
        userDao.save(user);
        System.out.println("创建用户: " + user);
    }
    
    // 只读事务
    @Transactional(readOnly = true)
    public User getUser(Long id) {
        return userDao.findById(id);
    }
    
    // 指定回滚异常
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(User user) throws Exception {
        userDao.save(user);
        // checked 异常也会回滚
    }
    
    // 指定不回滚异常
    @Transactional(noRollbackFor = BusinessException.class)
    public void processUser(Long id) {
        User user = userDao.findById(id);
        // BusinessException 不会触发回滚
    }
}

// 业务异常
public class BusinessException extends RuntimeException {
    public BusinessException(String message) {
        super(message);
    }
}

3.2 事务传播行为示例

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;

@Service
public class OrderService {
    
    private final OrderDao orderDao;
    private final InventoryService inventoryService;
    private final PaymentService paymentService;
    
    public OrderService(OrderDao orderDao, InventoryService inventoryService, 
                        PaymentService paymentService) {
        this.orderDao = orderDao;
        this.inventoryService = inventoryService;
        this.paymentService = paymentService;
    }
    
    // REQUIRED(默认):创建订单事务
    @Transactional
    public void createOrder(Order order) {
        orderDao.save(order);
        
        // 调用其他服务,加入当前事务
        inventoryService.deductStock(order.getProductId(), order.getQuantity());
        paymentService.processPayment(order.getUserId(), order.getAmount());
    }
}

@Service
public class InventoryService {
    
    private final InventoryDao inventoryDao;
    
    public InventoryService(InventoryDao inventoryDao) {
        this.inventoryDao = inventoryDao;
    }
    
    // REQUIRED:加入调用者事务
    @Transactional(propagation = Propagation.REQUIRED)
    public void deductStock(Long productId, int quantity) {
        Inventory inventory = inventoryDao.findByProductId(productId);
        if (inventory.getStock() < quantity) {
            throw new RuntimeException("库存不足");
        }
        inventory.setStock(inventory.getStock() - quantity);
        inventoryDao.save(inventory);
    }
    
    // REQUIRES_NEW:独立事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logInventoryChange(Long productId, int quantity, String operation) {
        // 即使外部事务回滚,这条日志也会保存
        InventoryLog log = new InventoryLog(productId, quantity, operation);
        inventoryDao.saveLog(log);
    }
}

@Service
public class PaymentService {
    
    private final PaymentDao paymentDao;
    private final NotificationService notificationService;
    
    public PaymentService(PaymentDao paymentDao, NotificationService notificationService) {
        this.paymentDao = paymentDao;
        this.notificationService = notificationService;
    }
    
    @Transactional
    public void processPayment(Long userId, BigDecimal amount) {
        Payment payment = new Payment(userId, amount);
        paymentDao.save(payment);
        
        // 发送通知(独立事务)
        notificationService.sendPaymentNotification(userId, amount);
    }
}

@Service
public class NotificationService {
    
    // NOT_SUPPORTED:不使用事务
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void sendPaymentNotification(Long userId, BigDecimal amount) {
        // 通知发送不需要事务
        System.out.println("发送支付通知: userId=" + userId + ", amount=" + amount);
    }
}

// NESTED 示例
@Service
public class BatchProcessService {
    
    private final ItemService itemService;
    
    public BatchProcessService(ItemService itemService) {
        this.itemService = itemService;
    }
    
    @Transactional
    public void batchProcess(List<Long> itemIds) {
        for (Long itemId : itemIds) {
            try {
                // 嵌套事务:单个失败不影响整体
                itemService.processItem(itemId);
            } catch (Exception e) {
                // 记录失败,继续处理下一个
                System.out.println("处理失败: " + itemId + ", 原因: " + e.getMessage());
            }
        }
    }
}

@Service
public class ItemService {
    
    @Transactional(propagation = Propagation.NESTED)
    public void processItem(Long itemId) {
        // 作为嵌套事务执行
        // 如果失败,只回滚这个嵌套事务
        // 外部事务可以继续
    }
}

3.3 事务隔离级别示例

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.annotation.Isolation;

@Service
public class AccountService {
    
    private final AccountDao accountDao;
    
    public AccountService(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    
    // 读已提交(Oracle 默认)
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Account getAccount(Long id) {
        return accountDao.findById(id);
    }
    
    // 可重复读(MySQL 默认)
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public Account getAccountForReport(Long id) {
        // 同一事务内多次读取结果一致
        Account account1 = accountDao.findById(id);
        // ... 其他操作
        Account account2 = accountDao.findById(id);
        // account1 == account2
        return account2;
    }
    
    // 串行化(最高隔离级别)
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void criticalOperation(Long id) {
        // 完全避免并发问题
        // 但性能最低
    }
}

// 脏读演示
@Service
public class DirtyReadDemo {
    
    private final AccountDao accountDao;
    
    public DirtyReadDemo(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    
    // 事务A:修改但未提交
    @Transactional
    public void updateBalance(Long id, BigDecimal amount) throws InterruptedException {
        Account account = accountDao.findById(id);
        account.setBalance(account.getBalance().add(amount));
        accountDao.save(account);
        
        // 模拟长时间未提交
        Thread.sleep(10000);
        
        // 可能回滚
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new RuntimeException("余额不足");
        }
    }
    
    // READ_UNCOMMITTED:可能读到未提交数据
    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    public Account readUncommitted(Long id) {
        // 可能读到脏数据
        return accountDao.findById(id);
    }
    
    // READ_COMMITTED:只能读到已提交数据
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Account readCommitted(Long id) {
        // 不会读到脏数据
        return accountDao.findById(id);
    }
}

3.4 事务回滚规则

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class TransferService {
    
    private final AccountDao accountDao;
    private final AuditService auditService;
    
    public TransferService(AccountDao accountDao, AuditService auditService) {
        this.accountDao = accountDao;
        this.auditService = auditService;
    }
    
    // 默认:RuntimeException 回滚
    @Transactional
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        Account from = accountDao.findById(fromId);
        Account to = accountDao.findById(toId);
        
        if (from.getBalance().compareTo(amount) < 0) {
            throw new RuntimeException("余额不足");  // 回滚
        }
        
        from.setBalance(from.getBalance().subtract(amount));
        to.setBalance(to.getBalance().add(amount));
        
        accountDao.save(from);
        accountDao.save(to);
    }
    
    // checked 异常默认不回滚
    @Transactional
    public void transferWithChecked(Long fromId, Long toId, BigDecimal amount) 
            throws InsufficientBalanceException {
        Account from = accountDao.findById(fromId);
        
        if (from.getBalance().compareTo(amount) < 0) {
            throw new InsufficientBalanceException("余额不足");  // 不回滚!
        }
        
        // 数据已修改但不会回滚
    }
    
    // 指定 checked 异常回滚
    @Transactional(rollbackFor = InsufficientBalanceException.class)
    public void transferWithRollback(Long fromId, Long toId, BigDecimal amount) 
            throws InsufficientBalanceException {
        Account from = accountDao.findById(fromId);
        
        if (from.getBalance().compareTo(amount) < 0) {
            throw new InsufficientBalanceException("余额不足");  // 会回滚
        }
    }
    
    // 指定所有异常回滚
    @Transactional(rollbackFor = Exception.class)
    public void safeOperation() throws Exception {
        // 任何异常都会回滚
    }
    
    // 指定特定异常不回滚
    @Transactional(noRollbackFor = BusinessException.class)
    public void businessOperation() {
        // BusinessException 不会触发回滚
        throw new BusinessException("业务异常");
    }
}

// checked 异常
public class InsufficientBalanceException extends Exception {
    public InsufficientBalanceException(String message) {
        super(message);
    }
}

3.5 编程式事务

import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.beans.factory.annotation.Autowired;

@Service
public class ProgrammaticTransactionService {
    
    private final TransactionTemplate transactionTemplate;
    private final UserDao userDao;
    
    // 方式1:使用 TransactionTemplate(推荐)
    public ProgrammaticTransactionService(
            PlatformTransactionManager transactionManager, 
            UserDao userDao) {
        this.transactionTemplate = new TransactionTemplate(transactionManager);
        this.userDao = userDao;
    }
    
    public void createUserWithTemplate(User user) {
        transactionTemplate.execute(status -> {
            try {
                userDao.save(user);
                System.out.println("用户创建成功: " + user);
            } catch (Exception e) {
                status.setRollbackOnly();
                System.out.println("用户创建失败,回滚: " + e.getMessage());
            }
            return null;
        });
    }
    
    public User getUserWithResult(Long id) {
        return transactionTemplate.execute(status -> {
            User user = userDao.findById(id);
            if (user == null) {
                status.setRollbackOnly();
            }
            return user;
        });
    }
    
    // 自定义事务配置
    public void customTransaction(User user) {
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        transactionTemplate.setTimeout(30);  // 30秒超时
        
        transactionTemplate.executeWithoutResult(status -> {
            userDao.save(user);
        });
    }
}

// 方式2:直接使用 PlatformTransactionManager
@Service
public class ManualTransactionService {
    
    private final PlatformTransactionManager transactionManager;
    private final UserDao userDao;
    
    public ManualTransactionService(
            PlatformTransactionManager transactionManager,
            UserDao userDao) {
        this.transactionManager = transactionManager;
        this.userDao = userDao;
    }
    
    public void createUserManual(User user) {
        // 定义事务
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setName("createUserTransaction");
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        
        // 获取事务状态
        TransactionStatus status = transactionManager.getTransaction(def);
        
        try {
            userDao.save(user);
            System.out.println("用户创建成功");
            
            // 提交事务
            transactionManager.commit(status);
        } catch (Exception e) {
            System.out.println("用户创建失败: " + e.getMessage());
            
            // 回滚事务
            transactionManager.rollback(status);
        }
    }
}

3.6 事务失效场景

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;

@Service
public class TransactionFailDemo {
    
    private final UserDao userDao;
    
    public TransactionFailDemo(UserDao userDao) {
        this.userDao = userDao;
    }
    
    // ❌ 失效场景1:方法非 public
    @Transactional
    private void privateMethod() {
        // 事务不生效
    }
    
    // ❌ 失效场景2:自调用问题
    public void outerMethod() {
        // 直接调用同类方法,代理不生效
        innerMethod();  // 事务不生效
    }
    
    @Transactional
    public void innerMethod() {
        userDao.save(new User(1L, "test", BigDecimal.ZERO));
    }
    
    // ✅ 解决方案:注入自身
    @Autowired
    private TransactionFailDemo self;
    
    public void outerMethodFixed() {
        self.innerMethod();  // 通过代理调用,事务生效
    }
    
    // ❌ 失效场景3:异常被捕获
    @Transactional
    public void caughtException() {
        try {
            userDao.save(new User(1L, "test", BigDecimal.ZERO));
            throw new RuntimeException("异常");
        } catch (Exception e) {
            // 异常被捕获,事务不回滚
            System.out.println("捕获异常: " + e.getMessage());
        }
    }
    
    // ✅ 解决方案:重新抛出或手动回滚
    @Transactional
    public void rethrowException() {
        try {
            userDao.save(new User(1L, "test", BigDecimal.ZERO));
            throw new RuntimeException("异常");
        } catch (Exception e) {
            System.out.println("捕获异常: " + e.getMessage());
            throw e;  // 重新抛出
        }
    }
    
    // ❌ 失效场景4:错误的异常类型
    @Transactional
    public void checkedException() throws Exception {
        userDao.save(new User(1L, "test", BigDecimal.ZERO));
        throw new Exception("checked 异常");  // 默认不回滚
    }
    
    // ✅ 解决方案:指定 rollbackFor
    @Transactional(rollbackFor = Exception.class)
    public void checkedExceptionFixed() throws Exception {
        userDao.save(new User(1L, "test", BigDecimal.ZERO));
        throw new Exception("checked 异常");  // 现在会回滚
    }
    
    // ❌ 失效场景5:finally 中清理数据
    @Transactional
    public void finallyBlock() {
        try {
            userDao.save(new User(1L, "test", BigDecimal.ZERO));
        } finally {
            // finally 在事务提交前执行
            userDao.delete(1L);  // 可能导致问题
        }
    }
}

3.7 多事务管理器

import org.springframework.context.annotation.*;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;

@Configuration
@EnableTransactionManagement
public class MultiTransactionConfig {
    
    @Bean
    @Primary
    public DataSource primaryDataSource() {
        return createDataSource("primary_db");
    }
    
    @Bean
    public DataSource secondaryDataSource() {
        return createDataSource("secondary_db");
    }
    
    @Bean
    @Primary
    public PlatformTransactionManager primaryTransactionManager(DataSource primaryDataSource) {
        return new DataSourceTransactionManager(primaryDataSource);
    }
    
    @Bean
    public PlatformTransactionManager secondaryTransactionManager(DataSource secondaryDataSource) {
        return new DataSourceTransactionManager(secondaryDataSource);
    }
    
    private DataSource createDataSource(String dbName) {
        // 创建数据源
        return null;  // 简化
    }
}

// 使用指定的事务管理器
@Service
public class MultiDataSourceService {
    
    private final PrimaryDao primaryDao;
    private final SecondaryDao secondaryDao;
    
    public MultiDataSourceService(PrimaryDao primaryDao, SecondaryDao secondaryDao) {
        this.primaryDao = primaryDao;
        this.secondaryDao = secondaryDao;
    }
    
    // 使用主数据源事务
    @Transactional("primaryTransactionManager")
    public void operatePrimary() {
        primaryDao.save(new User(1L, "primary", BigDecimal.ZERO));
    }
    
    // 使用从数据源事务
    @Transactional("secondaryTransactionManager")
    public void operateSecondary() {
        secondaryDao.save(new User(1L, "secondary", BigDecimal.ZERO));
    }
    
    // 分布式事务:需要特殊处理
    @Transactional("primaryTransactionManager")
    public void operateBoth() {
        primaryDao.save(new User(1L, "primary", BigDecimal.ZERO));
        // 这里的操作不在同一事务中!
        secondaryDao.save(new User(1L, "secondary", BigDecimal.ZERO));
    }
}

四、实战应用场景

4.1 银行转账

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import java.math.BigDecimal;

@Service
public class BankTransferService {
    
    private final AccountDao accountDao;
    private final TransferRecordDao recordDao;
    private final NotificationService notificationService;
    
    public BankTransferService(AccountDao accountDao, 
                               TransferRecordDao recordDao,
                               NotificationService notificationService) {
        this.accountDao = accountDao;
        this.recordDao = recordDao;
        this.notificationService = notificationService;
    }
    
    /**
     * 银行转账 - 完整事务示例
     */
    @Transactional(rollbackFor = Exception.class)
    public TransferResult transfer(Long fromAccountId, Long toAccountId, BigDecimal amount) {
        // 1. 查询账户
        Account fromAccount = accountDao.findById(fromAccountId);
        Account toAccount = accountDao.findById(toAccountId);
        
        if (fromAccount == null || toAccount == null) {
            throw new BusinessException("账户不存在");
        }
        
        // 2. 校验余额
        if (fromAccount.getBalance().compareTo(amount) < 0) {
            throw new BusinessException("余额不足");
        }
        
        // 3. 校验账户状态
        if (!fromAccount.isActive() || !toAccount.isActive()) {
            throw new BusinessException("账户状态异常");
        }
        
        // 4. 执行转账
        fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
        toAccount.setBalance(toAccount.getBalance().add(amount));
        
        accountDao.update(fromAccount);
        accountDao.update(toAccount);
        
        // 5. 记录转账流水
        TransferRecord record = new TransferRecord();
        record.setFromAccountId(fromAccountId);
        record.setToAccountId(toAccountId);
        record.setAmount(amount);
        record.setStatus(TransferStatus.SUCCESS);
        record.setCreateTime(new Date());
        
        recordDao.save(record);
        
        // 6. 发送通知(独立事务)
        notificationService.sendTransferNotification(fromAccountId, toAccountId, amount);
        
        return new TransferResult(true, "转账成功", record.getId());
    }
    
    /**
     * 批量转账
     */
    @Transactional(rollbackFor = Exception.class)
    public BatchTransferResult batchTransfer(List<TransferRequest> requests) {
        int successCount = 0;
        int failCount = 0;
        List<String> errors = new ArrayList<>();
        
        for (TransferRequest request : requests) {
            try {
                transfer(request.getFromAccountId(), 
                        request.getToAccountId(), 
                        request.getAmount());
                successCount++;
            } catch (Exception e) {
                failCount++;
                errors.add(String.format("转账失败: from=%d, to=%d, 原因=%s",
                    request.getFromAccountId(), 
                    request.getToAccountId(), 
                    e.getMessage()));
            }
        }
        
        return new BatchTransferResult(successCount, failCount, errors);
    }
}

// 账户实体
public class Account {
    private Long id;
    private String accountNo;
    private BigDecimal balance;
    private boolean active;
    
    // getter/setter
}

// 转账记录
public class TransferRecord {
    private Long id;
    private Long fromAccountId;
    private Long toAccountId;
    private BigDecimal amount;
    private TransferStatus status;
    private Date createTime;
    
    // getter/setter
}

public enum TransferStatus {
    PENDING, SUCCESS, FAILED
}

// 结果类
public class TransferResult {
    private boolean success;
    private String message;
    private Long recordId;
    
    public TransferResult(boolean success, String message, Long recordId) {
        this.success = success;
        this.message = message;
        this.recordId = recordId;
    }
    
    // getter
}

4.2 订单处理

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;

@Service
public class OrderProcessService {
    
    private final OrderDao orderDao;
    private final InventoryService inventoryService;
    private final PaymentService paymentService;
    private final CouponService couponService;
    private final PointsService pointsService;
    
    public OrderProcessService(OrderDao orderDao,
                               InventoryService inventoryService,
                               PaymentService paymentService,
                               CouponService couponService,
                               PointsService pointsService) {
        this.orderDao = orderDao;
        this.inventoryService = inventoryService;
        this.paymentService = paymentService;
        this.couponService = couponService;
        this.pointsService = pointsService;
    }
    
    /**
     * 创建订单 - 复杂事务处理
     */
    @Transactional(rollbackFor = Exception.class)
    public Order createOrder(OrderRequest request) {
        // 1. 创建订单
        Order order = new Order();
        order.setUserId(request.getUserId());
        order.setProductId(request.getProductId());
        order.setQuantity(request.getQuantity());
        order.setAmount(calculateAmount(request));
        order.setStatus(OrderStatus.CREATED);
        order.setCreateTime(new Date());
        
        orderDao.save(order);
        
        // 2. 扣减库存
        try {
            inventoryService.deductStock(request.getProductId(), request.getQuantity());
        } catch (InsufficientStockException e) {
            throw new BusinessException("库存不足");
        }
        
        // 3. 使用优惠券
        if (request.getCouponId() != null) {
            try {
                couponService.useCoupon(request.getUserId(), request.getCouponId(), order.getId());
            } catch (CouponException e) {
                throw new BusinessException("优惠券使用失败: " + e.getMessage());
            }
        }
        
        // 4. 处理支付
        try {
            paymentService.processPayment(order.getId(), order.getAmount());
            order.setStatus(OrderStatus.PAID);
        } catch (PaymentException e) {
            order.setStatus(OrderStatus.PAYMENT_FAILED);
            throw new BusinessException("支付失败: " + e.getMessage());
        }
        
        // 5. 增加积分(独立事务,失败不影响主流程)
        try {
            pointsService.addPoints(request.getUserId(), order.getAmount(), order.getId());
        } catch (Exception e) {
            // 积分失败不影响订单
            System.out.println("积分增加失败: " + e.getMessage());
        }
        
        orderDao.update(order);
        
        return order;
    }
    
    /**
     * 取消订单
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrder(Long orderId) {
        Order order = orderDao.findById(orderId);
        
        if (order == null) {
            throw new BusinessException("订单不存在");
        }
        
        if (order.getStatus() == OrderStatus.CANCELLED) {
            throw new BusinessException("订单已取消");
        }
        
        if (order.getStatus() == OrderStatus.COMPLETED) {
            throw new BusinessException("已完成的订单不能取消");
        }
        
        // 恢复库存
        inventoryService.restoreStock(order.getProductId(), order.getQuantity());
        
        // 退款
        if (order.getStatus() == OrderStatus.PAID) {
            paymentService.refund(orderId, order.getAmount());
        }
        
        // 恢复优惠券
        if (order.getCouponId() != null) {
            couponService.restoreCoupon(order.getCouponId());
        }
        
        // 扣除积分
        pointsService.deductPoints(order.getUserId(), order.getAmount());
        
        // 更新订单状态
        order.setStatus(OrderStatus.CANCELLED);
        order.setCancelTime(new Date());
        orderDao.update(order);
    }
    
    private BigDecimal calculateAmount(OrderRequest request) {
        // 计算金额逻辑
        return BigDecimal.ZERO;
    }
}

// 订单状态
public enum OrderStatus {
    CREATED,         // 已创建
    PAID,            // 已支付
    SHIPPED,         // 已发货
    COMPLETED,       // 已完成
    CANCELLED,       // 已取消
    PAYMENT_FAILED   // 支付失败
}

4.3 数据一致性保障

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.retry.annotation.Retryable;
import org.springframework.retry.annotation.Backoff;

@Service
public class DataConsistencyService {
    
    private final OrderDao orderDao;
    private final OutboxDao outboxDao;
    
    public DataConsistencyService(OrderDao orderDao, OutboxDao outboxDao) {
        this.orderDao = orderDao;
        this.outboxDao = outboxDao;
    }
    
    /**
     * Outbox 模式 - 保证数据一致性
     * 将业务操作和消息发送放在同一事务中
     */
    @Transactional(rollbackFor = Exception.class)
    public void createOrderWithOutbox(Order order) {
        // 1. 保存订单
        orderDao.save(order);
        
        // 2. 保存消息到 Outbox 表(同一事务)
        OutboxMessage message = new OutboxMessage();
        message.setAggregateType("Order");
        message.setAggregateId(order.getId().toString());
        message.setEventType("OrderCreated");
        message.setPayload(serializeOrder(order));
        message.setCreateTime(new Date());
        
        outboxDao.save(message);
        
        // 后台任务会扫描 Outbox 表并发送消息
    }
    
    /**
     * 幂等性处理
     */
    @Transactional(rollbackFor = Exception.class)
    public void processOrderWithIdempotency(String requestId, Order order) {
        // 检查是否已处理
        if (orderDao.existsByRequestId(requestId)) {
            System.out.println("请求已处理,跳过: " + requestId);
            return;
        }
        
        // 设置请求ID
        order.setRequestId(requestId);
        orderDao.save(order);
    }
    
    /**
     * 乐观锁 - 防止并发修改
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean updateWithOptimisticLock(Long orderId, OrderUpdate update, int version) {
        Order order = orderDao.findById(orderId);
        
        if (order == null) {
            throw new BusinessException("订单不存在");
        }
        
        // 版本检查
        if (order.getVersion() != version) {
            System.out.println("版本不匹配,当前版本: " + order.getVersion() + ", 请求版本: " + version);
            return false;
        }
        
        // 更新订单
        order.setStatus(update.getStatus());
        order.setVersion(version + 1);
        
        orderDao.update(order);
        
        return true;
    }
    
    /**
     * 重试机制
     */
    @Retryable(
        value = { OptimisticLockingFailureException.class },
        maxAttempts = 3,
        backoff = @Backoff(delay = 100, multiplier = 2)
    )
    @Transactional(rollbackFor = Exception.class)
    public void updateWithRetry(Long orderId, OrderUpdate update) {
        Order order = orderDao.findById(orderId);
        
        if (order == null) {
            throw new BusinessException("订单不存在");
        }
        
        order.setStatus(update.getStatus());
        orderDao.update(order);
    }
    
    private String serializeOrder(Order order) {
        // 序列化逻辑
        return "";
    }
}

// Outbox 消息
public class OutboxMessage {
    private Long id;
    private String aggregateType;
    private String aggregateId;
    private String eventType;
    private String payload;
    private Date createTime;
    
    // getter/setter
}

五、事务最佳实践

5.1 事务边界设计

@Service
public class TransactionBoundaryDemo {
    
    /**
     * ✅ 正确:事务边界清晰
     */
    @Transactional(rollbackFor = Exception.class)
    public void goodExample(Long userId, OrderRequest request) {
        // 所有数据库操作在一个事务中
        User user = userDao.findById(userId);
        Order order = createOrder(request);
        updateInventory(request);
        processPayment(order);
    }
    
    /**
     * ❌ 错误:事务范围过大
     */
    @Transactional(rollbackFor = Exception.class)
    public void badExample(Long userId, OrderRequest request) {
        // 包含了不必要的远程调用
        User user = userDao.findById(userId);
        
        // 远程调用可能很慢,不应该在事务中
        UserInfo remoteInfo = remoteService.getUserInfo(userId);
        
        Order order = createOrder(request);
        updateInventory(request);
        
        // 发送邮件也不应该在事务中
        emailService.sendEmail(user.getEmail(), "订单创建成功");
    }
    
    /**
     * ✅ 正确:分离事务和非事务操作
     */
    public void correctExample(Long userId, OrderRequest request) {
        // 远程调用放在事务外
        UserInfo remoteInfo = remoteService.getUserInfo(userId);
        
        // 只有必要的数据库操作在事务中
        processOrderInTransaction(userId, request);
        
        // 通知操作放在事务外
        emailService.sendEmail(remoteInfo.getEmail(), "订单创建成功");
    }
    
    @Transactional(rollbackFor = Exception.class)
    public void processOrderInTransaction(Long userId, OrderRequest request) {
        User user = userDao.findById(userId);
        Order order = createOrder(request);
        updateInventory(request);
        processPayment(order);
    }
}

5.2 只读事务优化

@Service
public class ReadOnlyTransactionDemo {
    
    /**
     * ✅ 读操作使用只读事务
     */
    @Transactional(readOnly = true)
    public User getUser(Long id) {
        return userDao.findById(id);
    }
    
    /**
     * ✅ 报表查询使用只读事务
     */
    @Transactional(readOnly = true)
    public List<OrderReport> getOrderReport(Date startDate, Date endDate) {
        return orderDao.findReportByDateRange(startDate, endDate);
    }
    
    /**
     * ✅ 分页查询
     */
    @Transactional(readOnly = true)
    public Page<User> getUserList(int page, int size) {
        return userDao.findAll(PageRequest.of(page, size));
    }
    
    /**
     * ❌ 错误:只读事务中进行写操作
     */
    @Transactional(readOnly = true)
    public void wrongExample(User user) {
        userDao.save(user);  // 会抛出异常
    }
}

5.3 事务超时设置

@Service
public class TransactionTimeoutDemo {
    
    /**
     * 设置事务超时
     */
    @Transactional(timeout = 10)  // 10秒超时
    public void longRunningOperation() {
        // 如果执行超过10秒,事务会自动回滚
    }
    
    /**
     * 批量操作设置较长超时
     */
    @Transactional(timeout = 300)  // 5分钟超时
    public void batchProcess(List<Long> ids) {
        for (Long id : ids) {
            processItem(id);
        }
    }
}

六、总结与最佳实践

最佳实践清单

  1. 事务边界

    • 事务范围尽可能小
    • 远程调用、文件操作放在事务外
    • 只包含必要的数据库操作
  2. 传播行为选择

    • 默认使用 REQUIRED
    • 独立事务使用 REQUIRES_NEW
    • 嵌套事务使用 NESTED
  3. 隔离级别选择

    • 大多数场景使用默认
    • 特殊场景按需设置
    • 性能与一致性权衡
  4. 回滚规则

    • 指定 rollbackFor = Exception.class
    • 明确异常处理策略
    • 避免异常被吞掉
  5. 性能优化

    • 读操作使用 readOnly = true
    • 合理设置超时时间
    • 避免长事务

常见问题

  1. 事务不生效

    • 检查方法是否为 public
    • 检查是否存在自调用
    • 检查异常是否被正确抛出
  2. 死锁问题

    • 按固定顺序访问资源
    • 减小事务范围
    • 设置合理的超时时间
  3. 性能问题

    • 避免长事务
    • 使用只读事务
    • 优化 SQL 语句

Spring 事务管理是企业级 Java 开发的核心技能。理解事务的传播行为、隔离级别、回滚规则,以及避免常见的陷阱,能够帮助我们构建出数据一致、性能优良的企业级应用。