怎样实现批量入库事务保障

wen 实用脚本 30

本文目录导读:

怎样实现批量入库事务保障

  1. 核心原则
  2. 方法一:原生 JDBC 实现(最底层、最通用)
  3. 方法二:Spring + JDBC / MyBatis(推荐,企业级常用)
  4. 方法三:MyBatis 批量插入 + 事务
  5. 关键注意事项

要实现批量入库的事务保障,核心目标是确保一批数据要么全部成功写入数据库,要么全部失败回滚,避免出现部分成功部分失败的数据不一致问题。

具体实现方法取决于你的技术栈(如 JDBC、Spring、MyBatis 等)和数据库类型,以下是几种常见且可靠的实现方式:

核心原则

  1. 关闭自动提交:显式开启事务。
  2. 批量执行:在单次事务中执行所有 INSERT/UPDATE 语句。
  3. 统一提交/回滚:全部成功则提交,任一失败则回滚。

原生 JDBC 实现(最底层、最通用)

这是理解事务机制的基础,关键操作是 connection.setAutoCommit(false)

public void batchInsertWithTransaction(List<Data> dataList) {
    Connection connection = null;
    PreparedStatement pstmt = null;
    try {
        // 1. 获取数据库连接
        connection = dataSource.getConnection();
        // 2. 关闭自动提交,开启事务
        connection.setAutoCommit(false);
        // 3. 准备 SQL 和批量执行
        String sql = "INSERT INTO my_table (col1, col2) VALUES (?, ?)";
        pstmt = connection.prepareStatement(sql);
        for (Data data : dataList) {
            pstmt.setString(1, data.getCol1());
            pstmt.setString(2, data.getCol2());
            pstmt.addBatch(); // 添加到批中
        }
        // 4. 执行批量操作
        pstmt.executeBatch();
        // 5. 全部成功,提交事务
        connection.commit();
        System.out.println("批量插入事务提交成功");
    } catch (SQLException e) {
        // 6. 发生任何异常,回滚事务
        if (connection != null) {
            try {
                connection.rollback();
                System.err.println("批量插入事务回滚");
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }
        e.printStackTrace();
    } finally {
        // 7. 恢复自动提交(可选)
        if (connection != null) {
            try {
                connection.setAutoCommit(true);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        // 关闭资源 (PreparedStatement, Connection)
        closeResources(pstmt, connection);
    }
}

优点:完全控制事务边界,性能高(网络往返少)。 缺点:代码较繁琐,需手动处理资源释放。


Spring + JDBC / MyBatis(推荐,企业级常用)

如果你使用 Spring 框架,可以利用声明式事务(@Transactional)或编程式事务,大大简化代码。

1 使用 @Transactional 注解(最简洁)

在 Service 层方法上添加 @Transactional,Spring 会自动管理事务的开启和回滚。

@Service
public class DataService {
    @Autowired
    private JdbcTemplate jdbcTemplate;  // 或 MyBatis 的 Mapper
    @Transactional(rollbackFor = Exception.class) // 关键注解
    public void batchInsert(List<Data> dataList) {
        String sql = "INSERT INTO my_table (col1, col2) VALUES (?, ?)";
        List<Object[]> batchArgs = new ArrayList<>();
        for (Data data : dataList) {
            batchArgs.add(new Object[]{data.getCol1(), data.getCol2()});
        }
        // 使用 JdbcTemplate 的 batchUpdate 方法
        jdbcTemplate.batchUpdate(sql, batchArgs);
        // 如果使用 MyBatis,可以调用 Mapper 的批量插入方法
    }
}

注意

  • 确保 Spring 事务管理器已配置(如 DataSourceTransactionManager)。
  • 默认情况下,运行时异常会回滚,受检异常不会,建议明确设置 rollbackFor = Exception.class
  • 事务边界:默认在 @Transactional 方法执行开始时创建连接,方法结束时提交。

2 使用 TransactionTemplate(编程式事务,更灵活)

如果你需要更精细的控制(中途根据条件决定回滚),可以使用 TransactionTemplate

@Service
public class DataService {
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    public void batchInsert(final List<Data> dataList) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    String sql = "...";
                    List<Object[]> batchArgs = ...;
                    jdbcTemplate.batchUpdate(sql, batchArgs);
                } catch (DataAccessException e) {
                    // 可以主动调用回滚
                    status.setRollbackOnly();
                    throw e; // 或者记录日志
                }
            }
        });
    }
}

MyBatis 批量插入 + 事务

MyBatis 本身不控制事务,需要与 Spring 事务结合,核心在于 Mapper 的 XML 中写批量插入的 SQL。

Mapper XML 示例

<insert id="batchInsert" parameterType="list">
    INSERT INTO my_table (col1, col2) VALUES
    <foreach collection="list" item="item" separator=",">
        (#{item.col1}, #{item.col2})
    </foreach>
</insert>

Service 层

@Service
public class MyBatisService {
    @Autowired
    private MyMapper myMapper;
    // 在方法上添加 @Transactional 即可保证批量插入的事务
    @Transactional
    public void batchInsert(List<Data> dataList) {
        myMapper.batchInsert(dataList);
    }
}

关键注意事项

  1. 数据库引擎:确保表使用的存储引擎支持事务。MySQL 必须使用 InnoDB,MyISAM 不支持事务。
  2. 批量大小:虽然在一个事务内执行,但不要一次性插入几十万条数据,建议分批次(如每 500-1000 条一批)提交,过大的事务会导致数据库锁占用时间长、内存溢出、binlog 日志暴增。
  3. 连接池配置:确保连接池(如 HikariCP)配置正确,不会因长时间占用连接导致连接池耗尽。
  4. 异常处理:捕获所有可能抛出的检查型异常(如 IOExceptionSQLException),并在 @TransactionalrollbackFor 中声明,否则事务会提交。
场景 推荐方案
简单/学习/底层控制 原生 JDBC
企业级 Spring 项目(99%情况) @Transactional + JdbcTemplate/MyBatis
需要动态控制事务边界 TransactionTemplate
极高性能要求(需自行管理) 原生 JDBC 批处理 + 合理批次大小

一句话最佳实践:在 Spring 项目中使用 @Transactional 注解控制事务边界,结合数据库的批量插入语法(如 INSERT ... VALUES (...), (...) 或 JDBC batchUpdate),并设置合理的批次大小(1000 条一批)。

抱歉,评论功能暂时关闭!