本文目录导读:

要实现批量入库的事务保障,核心目标是确保一批数据要么全部成功写入数据库,要么全部失败回滚,避免出现部分成功部分失败的数据不一致问题。
具体实现方法取决于你的技术栈(如 JDBC、Spring、MyBatis 等)和数据库类型,以下是几种常见且可靠的实现方式:
核心原则
- 关闭自动提交:显式开启事务。
- 批量执行:在单次事务中执行所有 INSERT/UPDATE 语句。
- 统一提交/回滚:全部成功则提交,任一失败则回滚。
原生 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);
}
}
关键注意事项
- 数据库引擎:确保表使用的存储引擎支持事务。MySQL 必须使用 InnoDB,MyISAM 不支持事务。
- 批量大小:虽然在一个事务内执行,但不要一次性插入几十万条数据,建议分批次(如每 500-1000 条一批)提交,过大的事务会导致数据库锁占用时间长、内存溢出、binlog 日志暴增。
- 连接池配置:确保连接池(如 HikariCP)配置正确,不会因长时间占用连接导致连接池耗尽。
- 异常处理:捕获所有可能抛出的检查型异常(如
IOException、SQLException),并在@Transactional的rollbackFor中声明,否则事务会提交。
| 场景 | 推荐方案 |
|---|---|
| 简单/学习/底层控制 | 原生 JDBC |
| 企业级 Spring 项目(99%情况) | @Transactional + JdbcTemplate/MyBatis |
| 需要动态控制事务边界 | TransactionTemplate |
| 极高性能要求(需自行管理) | 原生 JDBC 批处理 + 合理批次大小 |
一句话最佳实践:在 Spring 项目中使用 @Transactional 注解控制事务边界,结合数据库的批量插入语法(如 INSERT ... VALUES (...), (...) 或 JDBC batchUpdate),并设置合理的批次大小(1000 条一批)。