Skip to content

编程式事务

一、什么是编程式事务

编程式事务是通过代码手动控制事务的开启、提交和回滚,与声明式事务(@Transactional)不同,它不依赖 AOP 代理,而是直接操作 Spring 的事务 API。

声明式事务:加个注解,Spring 帮你管理事务(你只管写业务逻辑)
编程式事务:自己写代码,手动开启、提交、回滚事务(你说了算)

二、为什么需要编程式事务

声明式事务虽然方便,但有一个致命缺陷:事务边界是整个方法。当你需要更细粒度的控制时,声明式事务就无能为力了。

java
// 场景:批量处理 1000 条数据,希望每 100 条提交一次
// 声明式事务无法做到——要么全提交,要么全回滚

// 编程式事务可以:
for (int i = 0; i < 1000; i++) {
    transactionTemplate.execute(status -> {
        orderMapper.insert(orders.get(i));
        return null;
    });  // 每次循环独立提交
}

编程式事务的适用场景:

  • 需要在同一个方法内分段提交/回滚
  • 事务的提交/回滚取决于复杂的运行时条件
  • 需要精确控制事务边界(循环中每条记录独立事务)
  • 声明式事务因代理问题失效,又不想重构代码
  • 需要部分提交的能力(如批量导入跳过失败的行)

三、两种实现方式

3.1 TransactionTemplate(推荐)

TransactionTemplate 是 Spring 对 PlatformTransactionManager 的封装,简化了事务管理代码。

java
@Service
public class OrderService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    public void createOrder(Order order) {
        transactionTemplate.execute(status -> {
            orderMapper.insert(order);
            inventoryMapper.deduct(order.getProductId(), order.getQuantity());
            // 成功自动提交
            return null;
        });
    }
}

带条件回滚:

java
public void createOrder(Order order) {
    transactionTemplate.execute(status -> {
        orderMapper.insert(order);
        
        // 库存不足,主动回滚
        int stock = inventoryMapper.getStock(order.getProductId());
        if (stock < order.getQuantity()) {
            status.setRollbackOnly();  // 标记回滚
            return null;
        }
        
        inventoryMapper.deduct(order.getProductId(), order.getQuantity());
        return null;
    });
}

自定义传播行为:

java
@Service
public class OrderService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    public void createOrder(Order order) {
        // 设置 REQUIRES_NEW:每次都开新事务
        transactionTemplate.setPropagationBehavior(
            TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        
        transactionTemplate.execute(status -> {
            orderMapper.insert(order);
            return null;
        });
    }
    
    // 或创建新实例
    public void createOrderWithIsolation(Order order) {
        TransactionTemplate template = new TransactionTemplate(
            transactionManager,
            new DefaultTransactionDefinition(
                TransactionDefinition.PROPAGATION_REQUIRES_NEW
            )
        );
        template.execute(status -> {
            orderMapper.insert(order);
            return null;
        });
    }
}

完整配置:

java
@Configuration
public class TransactionConfig {

    @Bean
    public TransactionTemplate transactionTemplate(
            PlatformTransactionManager transactionManager) {
        TransactionTemplate template = new TransactionTemplate(transactionManager);
        
        // 设置传播行为
        template.setPropagationBehavior(
            TransactionDefinition.PROPAGATION_REQUIRED);
        
        // 设置隔离级别
        template.setIsolationLevel(
            TransactionDefinition.ISOLATION_READ_COMMITTED);
        
        // 设置超时时间(秒)
        template.setTimeout(30);
        
        // 设置只读
        template.setReadOnly(false);
        
        return template;
    }
}

3.2 PlatformTransactionManager(底层 API)

更底层,完全手动控制,适合需要精确控制事务生命周期的场景。

java
@Service
public class OrderService {

    @Autowired
    private PlatformTransactionManager transactionManager;

    public void createOrder(Order order) {
        // ① 定义事务属性
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        def.setTimeout(30);
        def.setReadOnly(false);

        // ② 开启事务
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            // ③ 执行业务逻辑
            orderMapper.insert(order);
            inventoryMapper.deduct(order.getProductId(), order.getQuantity());

            // ④ 提交事务
            transactionManager.commit(status);
        } catch (Exception e) {
            // ⑤ 回滚事务
            transactionManager.rollback(status);
            throw e;
        }
    }
}

TransactionStatus 常用方法:

java
// 判断是否是新事务
status.isNewTransaction();

// 判断是否有 savepoint
status.hasSavepoint();

// 标记回滚(事务不会立即回滚,会在 finally 回滚)
status.setRollbackOnly();

// 判断是否已被标记为回滚
status.isRollbackOnly();

// 判断事务是否已完成
status.isCompleted();

// 创建 savepoint
Object savepoint = status.createSavepoint();
// 回滚到 savepoint
status.rollbackToSavepoint(savepoint);
// 释放 savepoint
status.releaseSavepoint(savepoint);

四、两种方式对比

TransactionTemplatePlatformTransactionManager
复杂度
代码量
提交/回滚自动(返回值/异常)手动调用 commit/rollback
异常处理自动回滚(RuntimeException)需手动 try-catch + rollback
事务状态函数式,状态不暴露可获取 TransactionStatus
适用场景90% 的编程式事务场景需要 savepoint 或精确控制

五、实战场景

场景1:批量处理,每条独立事务

java
@Service
public class BatchService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    public void batchImport(List<Order> orders) {
        int successCount = 0;
        int failCount = 0;

        for (Order order : orders) {
            try {
                transactionTemplate.execute(status -> {
                    orderMapper.insert(order);
                    return null;
                });
                successCount++;
            } catch (Exception e) {
                log.error("订单 {} 导入失败", order.getId(), e);
                failCount++;  // 失败的不影响其他
            }
        }

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

场景2:每 100 条提交一次

java
@Service
public class BatchService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    public void batchInsert(List<Order> orders) {
        final int BATCH_SIZE = 100;
        List<List<Order>> batches = Lists.partition(orders, BATCH_SIZE);

        for (List<Order> batch : batches) {
            transactionTemplate.execute(status -> {
                for (Order order : batch) {
                    orderMapper.insert(order);
                }
                return null;
            });
            // 每 100 条提交一次,释放数据库资源
        }
    }
}

场景3:部分提交 + 错误收集

java
@Service
public class BatchService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    public BatchResult batchImport(List<Order> orders) {
        List<Order> failedOrders = new ArrayList<>();
        int successCount = 0;

        for (Order order : orders) {
            try {
                transactionTemplate.execute(status -> {
                    // 校验
                    if (order.getAmount() <= 0) {
                        status.setRollbackOnly();
                        return null;
                    }
                    orderMapper.insert(order);
                    return null;
                });
                successCount++;
            } catch (Exception e) {
                failedOrders.add(order);
            }
        }

        return new BatchResult(successCount, failedOrders);
    }
}

场景4:事务内分段操作(savepoint)

java
@Service
public class OrderService {

    @Autowired
    private PlatformTransactionManager transactionManager;

    public void createOrderWithSteps(Order order) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            // 步骤1:创建订单
            orderMapper.insert(order);

            // 步骤2:创建 savepoint(扣库存失败不影响订单)
            Object savepoint = status.createSavepoint();
            try {
                inventoryMapper.deduct(order.getProductId(), order.getQuantity());
            } catch (Exception e) {
                // 库存不足,回滚到 savepoint
                status.rollbackToSavepoint(savepoint);
                // 订单还是会创建成功
            }

            // 步骤3:创建支付记录
            paymentMapper.insert(order);
            
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw e;
        }
    }
}

六、声明式 vs 编程式 选择指南

┌─────────────────────────────────────────────────────┐
│              事务管理方式选择                          │
│                                                      │
│  你的事务场景是?                                      │
│                                                      │
│  ┌── 整个方法要么全成功要么全失败                        │
│  │   → 声明式事务(@Transactional)                    │
│  │                                                    │
│  ├── 方法内需要分段提交/回滚                            │
│  │   → 编程式事务(TransactionTemplate)               │
│  │                                                    │
│  ├── 需要 savepoint 精细控制                           │
│  │   → 编程式事务(PlatformTransactionManager)        │
│  │                                                    │
│  ├── 批量处理,部分失败不影响整体                          │
│  │   → 编程式事务(TransactionTemplate)               │
│  │                                                    │
│  └── 声明式事务因代理问题失效                            │
│      → 编程式事务(不依赖 AOP 代理)                      │
└─────────────────────────────────────────────────────┘

七、面试要点

Q1:声明式事务和编程式事务的区别?

声明式编程式
实现@Transactional 注解TransactionTemplate / PlatformTransactionManager
原理AOP 代理直接调用 API
事务边界整个方法代码块级别
侵入性
灵活性

Q2:为什么需要编程式事务?

声明式事务的粒度是整个方法,无法在方法内部实现分段提交/回滚。编程式事务可以精确控制事务边界,适用于批量处理、部分提交、savepoint 等场景。

Q3:TransactionTemplate 和 PlatformTransactionManager 怎么选?

  • TransactionTemplate:日常开发推荐,封装了提交/回滚逻辑,代码简洁
  • PlatformTransactionManager:需要 savepoint 或完全手动控制事务生命周期时使用