Spring事务详解

85 阅读13分钟

Spring事务详解:从原理到实战

前言

事务(Transaction)是数据库操作的核心概念,它保证了一组数据库操作要么全部成功,要么全部失败。Spring框架提供了强大的声明式事务管理功能,极大地简化了事务处理的复杂度。本文将深入讲解Spring事务的核心概念、实现原理和最佳实践。

一、事务基础

1.1 什么是事务

事务是一组原子性的数据库操作序列,这些操作要么全部执行成功,要么全部不执行。

事务示例:银行转账
┌─────────────────────────────────────────┐
│  无事务的问题:                          │
│  1. 从A账户扣款 -100元 ✓                │
│  2. 系统崩溃 ✗                          │
│  3. 向B账户增加 +100元 ✗(未执行)       │
│  结果:钱凭空消失!                      │
├─────────────────────────────────────────┤
│  有事务的保障:                          │
│  BEGIN TRANSACTION                      │
│  1. 从A账户扣款 -100元                  │
│  2. 向B账户增加 +100元                  │
│  3. 如果都成功 → COMMIT                 │
│     如果有失败 → ROLLBACK(全部撤销)    │
│  结果:数据一致性得到保障                │
└─────────────────────────────────────────┘

1.2 ACID特性

ACID事务特性
┌─────────────────────────────────────────┐
│  A - Atomicity(原子性)                 │
│  - 事务中的所有操作要么全部完成          │
│  - 要么全部不完成,不会停在中间状态      │
│  - 例如:转账操作不可分割                │
├─────────────────────────────────────────┤
│  C - Consistency(一致性)               │
│  - 事务前后数据的完整性必须保持一致      │
│  - 例如:转账前后总金额不变              │
├─────────────────────────────────────────┤
│  I - Isolation(隔离性)                 │
│  - 多个事务并发执行时互不干扰            │
│  - 通过隔离级别控制                      │
├─────────────────────────────────────────┤
│  D - Durability(持久性)                │
│  - 事务提交后,对数据的修改是永久的      │
│  - 即使系统故障也不会丢失                │
└─────────────────────────────────────────┘

1.3 事务隔离级别

/**
 * Spring支持的事务隔离级别
 */
public enum Isolation {

    /**
     * 使用数据库默认隔离级别
     */
    DEFAULT(-1),

    /**
     * 读未提交(最低级别)
     * 问题:脏读、不可重复读、幻读
     */
    READ_UNCOMMITTED(1),

    /**
     * 读已提交(Oracle默认)
     * 解决:脏读
     * 问题:不可重复读、幻读
     */
    READ_COMMITTED(2),

    /**
     * 可重复读(MySQL默认)
     * 解决:脏读、不可重复读
     * 问题:幻读
     */
    REPEATABLE_READ(4),

    /**
     * 串行化(最高级别)
     * 解决:脏读、不可重复读、幻读
     * 问题:性能最低
     */
    SERIALIZABLE(8);
}

并发问题说明:

┌──────────────┬────────────────────────────────┐
│  并发问题     │  说明                          │
├──────────────┼────────────────────────────────┤
│  脏读         │  读取到未提交的数据             │
│  (Dirty Read)│  事务A读取了事务B未提交的数据   │
├──────────────┼────────────────────────────────┤
│  不可重复读   │  同一事务中两次读取结果不同     │
│  (Non-       │  事务A先读取数据,事务B修改并   │
│   Repeatable │  提交,事务A再次读取时数据变化  │
│   Read)      │                                │
├──────────────┼────────────────────────────────┤
│  幻读         │  同一事务中两次查询记录数不同   │
│  (Phantom    │  事务A查询记录,事务B插入新记录 │
│   Read)      │  事务A再次查询时记录数增加      │
└──────────────┴────────────────────────────────┘

二、Spring事务管理

2.1 编程式事务 vs 声明式事务

/**
 * 编程式事务(不推荐)
 */
@Service
public class ProgrammaticTransactionService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private AccountDao accountDao;

    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        transactionTemplate.execute(status -> {
            try {
                // 扣款
                accountDao.deduct(fromId, amount);

                // 加款
                accountDao.add(toId, amount);

                return true;
            } catch (Exception e) {
                // 回滚
                status.setRollbackOnly();
                throw e;
            }
        });
    }
}

/**
 * 声明式事务(推荐)
 */
@Service
public class DeclarativeTransactionService {

    @Autowired
    private AccountDao accountDao;

    @Transactional
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        // 扣款
        accountDao.deduct(fromId, amount);

        // 加款
        accountDao.add(toId, amount);

        // Spring自动管理事务
    }
}

2.2 @Transactional注解详解

/**
 * @Transactional注解的各个属性
 */
@Service
public class TransactionalAttributesDemo {

    /**
     * 1. propagation:事务传播行为
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void requiredExample() {
        // 如果当前有事务,加入该事务;否则创建新事务(默认)
    }

    /**
     * 2. isolation:事务隔离级别
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void isolationExample() {
        // 使用读已提交隔离级别
    }

    /**
     * 3. timeout:事务超时时间(秒)
     */
    @Transactional(timeout = 30)
    public void timeoutExample() {
        // 30秒内必须完成,否则回滚
    }

    /**
     * 4. readOnly:只读事务
     */
    @Transactional(readOnly = true)
    public List<User> queryUsers() {
        // 只读事务,优化性能
        return null;
    }

    /**
     * 5. rollbackFor:指定回滚异常
     */
    @Transactional(rollbackFor = Exception.class)
    public void rollbackForExample() throws Exception {
        // 遇到Exception及其子类都回滚
    }

    /**
     * 6. noRollbackFor:指定不回滚异常
     */
    @Transactional(noRollbackFor = BusinessException.class)
    public void noRollbackForExample() {
        // 遇到BusinessException不回滚
    }

    /**
     * 7. 组合使用
     */
    @Transactional(
        propagation = Propagation.REQUIRED,
        isolation = Isolation.READ_COMMITTED,
        timeout = 30,
        rollbackFor = Exception.class
    )
    public void comprehensiveExample() throws Exception {
        // 完整的事务配置
    }
}

2.3 事务传播行为

/**
 * 事务传播行为详解
 */
@Service
@Slf4j
public class TransactionPropagationDemo {

    @Autowired
    private UserService userService;

    /**
     * REQUIRED(默认):如果有事务就加入,没有就创建
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void required() {
        log.info("REQUIRED事务");
        userService.saveUser(new User());  // 加入当前事务
    }

    /**
     * REQUIRES_NEW:总是创建新事务,挂起当前事务
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void requiresNew() {
        log.info("REQUIRES_NEW事务");
        userService.saveLog(new Log());  // 在新事务中执行
    }

    /**
     * SUPPORTS:如果有事务就加入,没有就以非事务方式执行
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    public void supports() {
        log.info("SUPPORTS事务");
    }

    /**
     * NOT_SUPPORTED:以非事务方式执行,如果有事务就挂起
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void notSupported() {
        log.info("NOT_SUPPORTED - 非事务执行");
    }

    /**
     * MANDATORY:必须在事务中执行,否则抛出异常
     */
    @Transactional(propagation = Propagation.MANDATORY)
    public void mandatory() {
        log.info("MANDATORY - 必须有事务");
    }

    /**
     * NEVER:必须在非事务中执行,否则抛出异常
     */
    @Transactional(propagation = Propagation.NEVER)
    public void never() {
        log.info("NEVER - 不能有事务");
    }

    /**
     * NESTED:嵌套事务,可以独立提交或回滚
     */
    @Transactional(propagation = Propagation.NESTED)
    public void nested() {
        log.info("NESTED - 嵌套事务");
    }
}

传播行为示意图:

REQUIRED传播行为
┌─────────────────────────────────────────┐
│  methodA() @Transactional(REQUIRED)     │
│  ┌───────────────────────────────────┐ │
│  │ Transaction                       │ │
│  │ ┌───────────────────────────────┐│ │
│  │ │ methodA操作                   ││ │
│  │ │ methodB() @Transactional      ││ │
│  │ │ ┌─────────────────────────┐   ││ │
│  │ │ │ 加入methodA的事务        │   ││ │
│  │ │ │ methodB操作             │   ││ │
│  │ │ └─────────────────────────┘   ││ │
│  │ └───────────────────────────────┘│ │
│  │ 一起提交或回滚                    │ │
│  └───────────────────────────────────┘ │
└─────────────────────────────────────────┘

REQUIRES_NEW传播行为
┌─────────────────────────────────────────┐
│  methodA() @Transactional               │
│  ┌───────────────────────────────────┐ │
│  │ Transaction A                     │ │
│  │ methodA操作                       │ │
│  │ ┌─────────────────────────────┐   │ │
│  │ │ 挂起Transaction A           │   │ │
│  │ └─────────────────────────────┘   │ │
│  └───────────────────────────────────┘ │
│  methodB() @Transactional(REQUIRES_NEW)│
│  ┌───────────────────────────────────┐ │
│  │ Transaction B (新事务)            │ │
│  │ methodB操作                       │ │
│  │ 独立提交或回滚                    │ │
│  └───────────────────────────────────┘ │
│  ┌───────────────────────────────────┐ │
│  │ Transaction A 恢复                │ │
│  └───────────────────────────────────┘ │
└─────────────────────────────────────────┘

三、实战案例

3.1 案例1:订单创建(多表操作)

/**
 * 订单实体
 */
@Data
public class Order {
    private Long id;
    private String orderNo;
    private Long userId;
    private BigDecimal totalAmount;
    private OrderStatus status;
    private LocalDateTime createTime;
}

/**
 * 订单项实体
 */
@Data
public class OrderItem {
    private Long id;
    private Long orderId;
    private Long productId;
    private Integer quantity;
    private BigDecimal price;
}

/**
 * 订单服务
 */
@Service
@Slf4j
public class OrderService {

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private OrderItemDao orderItemDao;

    @Autowired
    private ProductService productService;

    @Autowired
    private AccountService accountService;

    /**
     * 创建订单(事务保证原子性)
     */
    @Transactional(rollbackFor = Exception.class)
    public Order createOrder(Long userId, List<OrderItem> items) {
        log.info("开始创建订单,用户: {}", userId);

        // 1. 计算订单总金额
        BigDecimal totalAmount = items.stream()
            .map(item -> item.getPrice().multiply(new BigDecimal(item.getQuantity())))
            .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 2. 创建订单
        Order order = new Order();
        order.setOrderNo(generateOrderNo());
        order.setUserId(userId);
        order.setTotalAmount(totalAmount);
        order.setStatus(OrderStatus.PENDING);
        order.setCreateTime(LocalDateTime.now());

        orderDao.insert(order);
        log.info("订单创建成功: {}", order.getOrderNo());

        // 3. 创建订单项
        for (OrderItem item : items) {
            item.setOrderId(order.getId());
            orderItemDao.insert(item);

            // 4. 扣减库存(如果库存不足会抛出异常,导致整个事务回滚)
            productService.deductStock(item.getProductId(), item.getQuantity());
        }

        // 5. 扣减账户余额
        accountService.deduct(userId, totalAmount);

        log.info("订单创建完成: {}", order.getOrderNo());
        return order;
    }

    private String generateOrderNo() {
        return "ORD" + System.currentTimeMillis();
    }
}

/**
 * 商品服务
 */
@Service
@Slf4j
public class ProductService {

    @Autowired
    private ProductDao productDao;

    /**
     * 扣减库存(加入外部事务)
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void deductStock(Long productId, Integer quantity) {
        Product product = productDao.findById(productId);

        if (product.getStock() < quantity) {
            throw new InsufficientStockException("库存不足");
        }

        product.setStock(product.getStock() - quantity);
        productDao.update(product);

        log.info("扣减库存: 商品[{}], 数量[{}], 剩余[{}]",
                productId, quantity, product.getStock());
    }
}

/**
 * 账户服务
 */
@Service
@Slf4j
public class AccountService {

    @Autowired
    private AccountDao accountDao;

    /**
     * 扣减余额
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void deduct(Long userId, BigDecimal amount) {
        Account account = accountDao.findByUserId(userId);

        if (account.getBalance().compareTo(amount) < 0) {
            throw new InsufficientBalanceException("余额不足");
        }

        account.setBalance(account.getBalance().subtract(amount));
        accountDao.update(account);

        log.info("扣减余额: 用户[{}], 金额[{}], 剩余[{}]",
                userId, amount, account.getBalance());
    }
}

3.2 案例2:转账操作(REQUIRES_NEW)

/**
 * 转账服务
 */
@Service
@Slf4j
public class TransferService {

    @Autowired
    private AccountDao accountDao;

    @Autowired
    private TransferLogService transferLogService;

    /**
     * 转账(主事务)
     */
    @Transactional(rollbackFor = Exception.class)
    public void transfer(Long fromUserId, Long toUserId, BigDecimal amount) {
        log.info("开始转账: {} -> {}, 金额: {}", fromUserId, toUserId, amount);

        try {
            // 1. 扣款
            Account fromAccount = accountDao.findByUserId(fromUserId);
            if (fromAccount.getBalance().compareTo(amount) < 0) {
                throw new InsufficientBalanceException("余额不足");
            }
            fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
            accountDao.update(fromAccount);

            // 模拟异常
            // if (amount.compareTo(new BigDecimal("1000")) > 0) {
            //     throw new RuntimeException("转账金额过大");
            // }

            // 2. 加款
            Account toAccount = accountDao.findByUserId(toUserId);
            toAccount.setBalance(toAccount.getBalance().add(amount));
            accountDao.update(toAccount);

            // 3. 记录成功日志(新事务,即使转账失败也会记录)
            transferLogService.logSuccess(fromUserId, toUserId, amount);

            log.info("转账成功");

        } catch (Exception e) {
            // 4. 记录失败日志(新事务,即使转账回滚也会记录)
            transferLogService.logFailure(fromUserId, toUserId, amount, e.getMessage());
            throw e;
        }
    }
}

/**
 * 转账日志服务
 */
@Service
@Slf4j
public class TransferLogService {

    @Autowired
    private TransferLogDao transferLogDao;

    /**
     * 记录成功日志(新事务,独立提交)
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logSuccess(Long fromUserId, Long toUserId, BigDecimal amount) {
        TransferLog log = new TransferLog();
        log.setFromUserId(fromUserId);
        log.setToUserId(toUserId);
        log.setAmount(amount);
        log.setStatus("SUCCESS");
        log.setCreateTime(LocalDateTime.now());

        transferLogDao.insert(log);
        this.log.info("转账成功日志已记录");
    }

    /**
     * 记录失败日志(新事务,独立提交)
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logFailure(Long fromUserId, Long toUserId, BigDecimal amount, String errorMsg) {
        TransferLog log = new TransferLog();
        log.setFromUserId(fromUserId);
        log.setToUserId(toUserId);
        log.setAmount(amount);
        log.setStatus("FAILURE");
        log.setErrorMsg(errorMsg);
        log.setCreateTime(LocalDateTime.now());

        transferLogDao.insert(log);
        this.log.info("转账失败日志已记录");
    }
}

3.3 案例3:批量处理(嵌套事务)

/**
 * 批量数据导入服务
 */
@Service
@Slf4j
public class BatchImportService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private ErrorLogService errorLogService;

    /**
     * 批量导入用户(主事务)
     */
    @Transactional(rollbackFor = Exception.class)
    public void batchImport(List<User> users) {
        log.info("开始批量导入,总数: {}", users.size());

        int successCount = 0;
        int failureCount = 0;

        for (User user : users) {
            try {
                // 导入单个用户(嵌套事务)
                importUser(user);
                successCount++;

            } catch (Exception e) {
                // 单个用户导入失败不影响其他用户
                failureCount++;
                log.error("用户导入失败: {}", user.getUsername(), e);
            }
        }

        log.info("批量导入完成,成功: {}, 失败: {}", successCount, failureCount);
    }

    /**
     * 导入单个用户(嵌套事务,可以独立回滚)
     */
    @Transactional(propagation = Propagation.NESTED)
    public void importUser(User user) {
        // 数据校验
        if (user.getUsername() == null || user.getUsername().isEmpty()) {
            throw new ValidationException("用户名不能为空");
        }

        // 检查是否已存在
        if (userDao.existsByUsername(user.getUsername())) {
            throw new DuplicateException("用户名已存在");
        }

        // 保存用户
        userDao.insert(user);

        log.info("用户导入成功: {}", user.getUsername());
    }
}

3.4 案例4:分布式事务(Seata)

/**
 * 使用Seata实现分布式事务
 */
@Service
@Slf4j
public class DistributedOrderService {

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private AccountServiceClient accountServiceClient;  // Feign客户端

    @Autowired
    private InventoryServiceClient inventoryServiceClient;  // Feign客户端

    /**
     * 创建订单(分布式事务)
     * @GlobalTransactional 开启Seata分布式事务
     */
    @GlobalTransactional(
        name = "create-order",
        timeoutMills = 30000,
        rollbackFor = Exception.class
    )
    public Order createOrder(OrderDTO orderDTO) {
        log.info("开始创建订单(分布式事务)");

        // 1. 创建订单(本地事务)
        Order order = new Order();
        order.setOrderNo(generateOrderNo());
        order.setUserId(orderDTO.getUserId());
        order.setTotalAmount(orderDTO.getTotalAmount());
        order.setStatus(OrderStatus.PENDING);

        orderDao.insert(order);
        log.info("订单创建成功: {}", order.getOrderNo());

        // 2. 扣减库存(远程调用库存服务)
        boolean stockDeducted = inventoryServiceClient.deduct(
            orderDTO.getProductId(),
            orderDTO.getQuantity()
        );

        if (!stockDeducted) {
            throw new BusinessException("扣减库存失败");
        }
        log.info("库存扣减成功");

        // 3. 扣减账户余额(远程调用账户服务)
        boolean balanceDeducted = accountServiceClient.deduct(
            orderDTO.getUserId(),
            orderDTO.getTotalAmount()
        );

        if (!balanceDeducted) {
            throw new BusinessException("扣减余额失败");
        }
        log.info("余额扣减成功");

        // 4. 更新订单状态
        order.setStatus(OrderStatus.PAID);
        orderDao.update(order);

        log.info("订单创建完成(分布式事务): {}", order.getOrderNo());
        return order;
    }

    private String generateOrderNo() {
        return "ORD" + System.currentTimeMillis();
    }
}

/**
 * 库存服务(参与分布式事务)
 */
@Service
@Slf4j
public class InventoryService {

    @Autowired
    private InventoryDao inventoryDao;

    /**
     * 扣减库存
     * 被@GlobalTransactional标记的方法调用,自动加入分布式事务
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean deduct(Long productId, Integer quantity) {
        Inventory inventory = inventoryDao.findByProductId(productId);

        if (inventory.getStock() < quantity) {
            log.error("库存不足");
            return false;
        }

        inventory.setStock(inventory.getStock() - quantity);
        inventoryDao.update(inventory);

        log.info("库存扣减成功: 商品[{}], 数量[{}]", productId, quantity);
        return true;
    }
}

3.5 案例5:只读事务优化查询

/**
 * 查询服务(只读事务优化)
 */
@Service
@Slf4j
public class QueryService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private OrderDao orderDao;

    /**
     * 查询用户(只读事务)
     * 优势:
     * 1. 不需要脏检查
     * 2. 某些数据库可以优化只读事务
     * 3. 明确表达这是只读操作
     */
    @Transactional(readOnly = true)
    public User findUserById(Long id) {
        log.info("查询用户: {}", id);
        return userDao.findById(id);
    }

    /**
     * 复杂查询(只读事务)
     */
    @Transactional(readOnly = true)
    public UserOrderVO findUserWithOrders(Long userId) {
        log.info("查询用户及订单: {}", userId);

        User user = userDao.findById(userId);
        List<Order> orders = orderDao.findByUserId(userId);

        UserOrderVO vo = new UserOrderVO();
        vo.setUser(user);
        vo.setOrders(orders);

        return vo;
    }

    /**
     * 分页查询(只读事务)
     */
    @Transactional(readOnly = true)
    public Page<Order> findOrdersByPage(Long userId, int page, int size) {
        log.info("分页查询订单: 用户[{}], 页码[{}], 大小[{}]", userId, page, size);

        long total = orderDao.countByUserId(userId);
        List<Order> orders = orderDao.findByUserIdWithPage(userId, page, size);

        Page<Order> result = new Page<>();
        result.setTotal(total);
        result.setRecords(orders);
        result.setCurrent(page);
        result.setSize(size);

        return result;
    }
}

四、事务失效场景

4.1 常见失效场景

/**
 * 事务失效场景汇总
 */
@Service
@Slf4j
public class TransactionFailureDemo {

    @Autowired
    private UserDao userDao;

    /**
     * 场景1:方法不是public(事务失效)
     */
    @Transactional
    private void privateMethod() {  // ❌ 事务不生效
        userDao.save(new User());
    }

    @Transactional
    protected void protectedMethod() {  // ❌ 事务不生效
        userDao.save(new User());
    }

    /**
     * 场景2:同类内部调用(事务失效)
     */
    public void outerMethod() {
        // ❌ 直接调用,绕过代理,事务不生效
        this.innerMethod();
    }

    @Transactional
    public void innerMethod() {
        userDao.save(new User());
    }

    /**
     * 场景3:异常被捕获(事务不回滚)
     */
    @Transactional
    public void catchException() {
        try {
            userDao.save(new User());
            throw new RuntimeException("测试异常");
        } catch (Exception e) {
            // ❌ 异常被捕获,事务不会回滚
            log.error("异常被捕获", e);
        }
    }

    /**
     * 场景4:抛出检查异常(默认不回滚)
     */
    @Transactional  // ❌ 默认只回滚RuntimeException
    public void checkedExceptionMethod() throws Exception {
        userDao.save(new User());
        throw new Exception("检查异常");  // 不会回滚
    }

    /**
     * 场景5:错误的传播行为
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void notSupportedMethod() {  // ❌ 以非事务方式执行
        userDao.save(new User());
    }

    /**
     * 场景6:数据库不支持事务
     */
    @Transactional
    public void myisamTable() {  // ❌ MyISAM引擎不支持事务
        // 操作MyISAM表
    }

    /**
     * 场景7:未启用事务管理
     */
    // ❌ 配置类未添加@EnableTransactionManagement
}

4.2 解决方案

/**
 * 事务失效的解决方案
 */
@Service
@Slf4j
public class TransactionSolution {

    @Autowired
    private UserDao userDao;

    @Autowired
    private ApplicationContext context;

    /**
     * 解决方案1:方法改为public
     */
    @Transactional
    public void publicMethod() {  // ✓ 事务生效
        userDao.save(new User());
    }

    /**
     * 解决方案2:注入自己,通过代理调用
     */
    @Autowired
    private TransactionSolution self;

    public void outerMethodSolution1() {
        self.innerMethod();  // ✓ 通过代理调用,事务生效
    }

    /**
     * 解决方案3:从容器获取代理对象
     */
    public void outerMethodSolution2() {
        TransactionSolution proxy = context.getBean(TransactionSolution.class);
        proxy.innerMethod();  // ✓ 通过代理调用
    }

    /**
     * 解决方案4:使用AopContext
     */
    public void outerMethodSolution3() {
        ((TransactionSolution) AopContext.currentProxy()).innerMethod();  // ✓
    }

    @Transactional
    public void innerMethod() {
        userDao.save(new User());
    }

    /**
     * 解决方案5:异常后手动回滚
     */
    @Transactional
    public void catchExceptionSolution() {
        try {
            userDao.save(new User());
            throw new RuntimeException("测试异常");
        } catch (Exception e) {
            log.error("异常被捕获", e);
            // ✓ 手动标记回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

    /**
     * 解决方案6:指定回滚异常
     */
    @Transactional(rollbackFor = Exception.class)  // ✓ 指定回滚所有异常
    public void checkedExceptionSolution() throws Exception {
        userDao.save(new User());
        throw new Exception("检查异常");  // 会回滚
    }
}

五、最佳实践

5.1 事务粒度控制

/**
 * 事务粒度最佳实践
 */
@Service
@Slf4j
public class TransactionGranularityDemo {

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private EmailService emailService;

    /**
     * ❌ 不好:事务粒度太大
     */
    @Transactional
    public void badExample(Order order) {
        // 数据库操作
        orderDao.save(order);

        // 发送邮件(耗时操作,不需要在事务中)
        emailService.sendOrderConfirmation(order);

        // 调用第三方API(耗时操作,不需要在事务中)
        callThirdPartyApi(order);
    }

    /**
     * ✓ 好:事务粒度适中
     */
    public void goodExample(Order order) {
        // 数据库操作在事务中
        saveOrder(order);

        // 非事务操作
        emailService.sendOrderConfirmation(order);
        callThirdPartyApi(order);
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveOrder(Order order) {
        orderDao.save(order);
    }

    private void callThirdPartyApi(Order order) {
        // 调用第三方API
    }
}

5.2 异常处理策略

/**
 * 事务异常处理最佳实践
 */
@Service
@Slf4j
public class TransactionExceptionDemo {

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private NotificationService notificationService;

    /**
     * 策略1:让异常向上抛出
     */
    @Transactional(rollbackFor = Exception.class)
    public void strategy1(Order order) throws Exception {
        orderDao.save(order);

        if (order.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("订单金额必须大于0");
        }

        // 异常会导致事务回滚
    }

    /**
     * 策略2:捕获并重新抛出
     */
    @Transactional(rollbackFor = Exception.class)
    public void strategy2(Order order) {
        try {
            orderDao.save(order);
            processPayment(order);
        } catch (PaymentException e) {
            log.error("支付失败", e);
            // 转换为运行时异常,触发回滚
            throw new BusinessException("订单创建失败:" + e.getMessage(), e);
        }
    }

    /**
     * 策略3:部分回滚
     */
    @Transactional(rollbackFor = Exception.class)
    public void strategy3(Order order) {
        orderDao.save(order);

        try {
            // 发送通知失败不影响订单创建
            notificationService.sendNotification(order);
        } catch (Exception e) {
            log.error("发送通知失败,但不影响订单", e);
            // 不抛出异常,事务继续
        }
    }

    private void processPayment(Order order) throws PaymentException {
        // 支付处理
    }
}

5.3 性能优化

/**
 * 事务性能优化
 */
@Service
@Slf4j
public class TransactionPerformanceDemo {

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private CacheService cacheService;

    /**
     * 优化1:使用只读事务
     */
    @Transactional(readOnly = true)
    public List<Order> findOrders(Long userId) {
        // 只读事务可以提高性能
        return orderDao.findByUserId(userId);
    }

    /**
     * 优化2:合理设置超时时间
     */
    @Transactional(timeout = 10, rollbackFor = Exception.class)
    public void updateOrder(Order order) {
        // 10秒超时,防止长时间锁表
        orderDao.update(order);
    }

    /**
     * 优化3:减少事务范围
     */
    public void createOrder(Order order) {
        // 准备数据(不在事务中)
        validateOrder(order);
        enrichOrderData(order);

        // 数据库操作(在事务中)
        saveOrderInTransaction(order);

        // 后续处理(不在事务中)
        sendNotification(order);
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveOrderInTransaction(Order order) {
        orderDao.save(order);
    }

    /**
     * 优化4:批量操作
     */
    @Transactional(rollbackFor = Exception.class)
    public void batchInsert(List<Order> orders) {
        // 批量插入比逐条插入更高效
        orderDao.batchInsert(orders);
    }

    /**
     * 优化5:使用缓存减少数据库访问
     */
    @Transactional(readOnly = true)
    public Order findOrderWithCache(Long orderId) {
        // 先查缓存
        Order cached = cacheService.get("order:" + orderId, Order.class);
        if (cached != null) {
            return cached;
        }

        // 缓存未命中,查数据库
        Order order = orderDao.findById(orderId);

        // 更新缓存
        if (order != null) {
            cacheService.put("order:" + orderId, order);
        }

        return order;
    }

    private void validateOrder(Order order) { }
    private void enrichOrderData(Order order) { }
    private void sendNotification(Order order) { }
}

六、监控与调试

6.1 事务监控

/**
 * 事务监控切面
 */
@Aspect
@Component
@Slf4j
public class TransactionMonitorAspect {

    @Around("@annotation(transactional)")
    public Object monitorTransaction(ProceedingJoinPoint joinPoint,
                                    Transactional transactional) throws Throwable {

        String methodName = joinPoint.getSignature().toShortString();
        long startTime = System.currentTimeMillis();

        log.info("事务开始: {}, 传播行为: {}, 隔离级别: {}",
                methodName,
                transactional.propagation(),
                transactional.isolation());

        try {
            Object result = joinPoint.proceed();

            long duration = System.currentTimeMillis() - startTime;
            log.info("事务提交: {}, 耗时: {}ms", methodName, duration);

            // 记录慢事务
            if (duration > 1000) {
                log.warn("慢事务: {}, 耗时: {}ms", methodName, duration);
            }

            return result;

        } catch (Throwable e) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("事务回滚: {}, 耗时: {}ms, 异常: {}",
                    methodName, duration, e.getMessage());
            throw e;
        }
    }
}

6.2 事务调试

/**
 * 事务调试工具
 */
@Component
@Slf4j
public class TransactionDebugger {

    /**
     * 检查是否在事务中
     */
    public boolean isInTransaction() {
        return TransactionSynchronizationManager.isActualTransactionActive();
    }

    /**
     * 获取当前事务名称
     */
    public String getCurrentTransactionName() {
        return TransactionSynchronizationManager.getCurrentTransactionName();
    }

    /**
     * 获取当前事务隔离级别
     */
    public Integer getCurrentTransactionIsolationLevel() {
        return TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
    }

    /**
     * 事务是否只读
     */
    public boolean isCurrentTransactionReadOnly() {
        return TransactionSynchronizationManager.isCurrentTransactionReadOnly();
    }

    /**
     * 打印事务信息
     */
    public void printTransactionInfo() {
        if (isInTransaction()) {
            log.info("当前在事务中");
            log.info("事务名称: {}", getCurrentTransactionName());
            log.info("隔离级别: {}", getCurrentTransactionIsolationLevel());
            log.info("是否只读: {}", isCurrentTransactionReadOnly());
        } else {
            log.info("当前不在事务中");
        }
    }
}

/**
 * 使用调试工具
 */
@Service
@Slf4j
public class DebugService {

    @Autowired
    private TransactionDebugger debugger;

    @Transactional
    public void testTransaction() {
        debugger.printTransactionInfo();
        // 输出:
        // 当前在事务中
        // 事务名称: com.example.service.DebugService.testTransaction
        // 隔离级别: null (使用默认)
        // 是否只读: false
    }
}

七、总结

核心知识点回顾

Spring事务核心要点
│
├── 事务基础
│   ├── ACID特性
│   ├── 隔离级别
│   └── 并发问题(脏读、不可重复读、幻读)
│
├── 事务管理
│   ├── 编程式事务
│   └── 声明式事务(@Transactional)
│
├── 传播行为
│   ├── REQUIRED(默认)
│   ├── REQUIRES_NEW
│   ├── NESTED
│   └── 其他(SUPPORTS/NOT_SUPPORTED/MANDATORY/NEVER)
│
├── 实战应用
│   ├── 多表操作事务
│   ├── 分布式事务
│   ├── 批量处理
│   └── 只读优化
│
├── 失效场景
│   ├── 非public方法
│   ├── 同类调用
│   ├── 异常捕获
│   └── 检查异常
│
└── 最佳实践
    ├── 合理粒度
    ├── 异常处理
    ├── 性能优化
    └── 监控调试

Spring事务管理是企业级应用开发的核心技能。理解事务的原理和特性,掌握正确的使用方法,避免常见的陷阱,才能开发出健壮、高效的应用系统。