0

0

JDBC 批量插入中的异常处理与事务回滚最佳实践

碧海醫心

碧海醫心

发布时间:2026-01-26 16:42:02

|

284人浏览过

|

来源于php中文网

原创

JDBC 批量插入中的异常处理与事务回滚最佳实践

本文详解如何在 jdbc 批量操作中正确启用事务控制,通过禁用自动提交、显式调用 commit/rollback 实现 acid 一致性,并规避因 sqlexception 导致部分提交的风险。

在使用 JDBC 进行批量数据操作(如 executeBatch())时,若未妥善管理事务,极易破坏数据一致性——例如:某一批次插入失败,但此前已成功执行的批次已被自动提交,导致“半成功”状态,严重违背 ACID 原则。核心问题在于:默认的 auto-commit = true 模式下,executeBatch() 的错误行为是驱动依赖且不可靠的,JDBC 规范明确指出(§14.1.1):当 auto-commit 启用时,发生异常后是否回滚、回滚范围及是否继续执行,均由具体 JDBC 驱动实现决定,不应被信任或依赖

✅ 正确做法是:始终在批量操作前显式关闭自动提交

connection.setAutoCommit(false);

此后,所有 DML 操作(包括 executeUpdate() 和 executeBatch())均纳入同一事务上下文,直至显式调用 connection.commit() 或 connection.rollback()。

SuperDesign
SuperDesign

开源的UI设计AI智能体

下载

✅ 推荐实现结构(含完整异常回滚)

以下为生产就绪的批量插入模板,兼顾可读性、事务完整性与日志可观测性:

public void insertingRowsByBatches(RequestData requestData) {
    try (Connection connection = myDataSource.getConnection()) {
        connection.setAutoCommit(false); // 关键:禁用自动提交

        try (PreparedStatement deleteStmt = connection.prepareStatement("DELETE FROM my_table");
             PreparedStatement insertStmt = connection.prepareStatement("INSERT INTO my_table(SOME_DATA) VALUES (?)")) {

            // 1. 清空 staging 表(作为事务一部分)
            int deleted = deleteStmt.executeUpdate();
            log.info("[{}] existing records deleted from staging table", deleted);

            // 2. 分批插入(每批独立 prepare,复用同一 Statement)
            List data = Optional.ofNullable(requestData.getData()).orElse(List.of());
            List> partitions = Lists.partition(data, MAX_ROWS_PER_INSERT);

            long totalStart = System.currentTimeMillis();
            for (int i = 0; i < partitions.size(); i++) {
                List batch = partitions.get(i);
                log.debug("Processing batch [{}/{}], size: {}", i + 1, partitions.size(), batch.size());

                insertStmt.clearBatch(); // 确保批次干净
                for (String recordId : batch) {
                    insertStmt.setString(1, recordId);
                    insertStmt.addBatch();
                }

                int[] results = insertStmt.executeBatch();
                log.debug("Batch [{}/{}] executed: {} updates", i + 1, partitions.size(), results.length);
            }

            // 3. 全局提交 —— 仅当全部批次成功才生效
            connection.commit();
            long totalTime = System.currentTimeMillis() - totalStart;
            log.info("Successfully inserted [{}] rows in {}ms", data.size(), totalTime);

        } catch (SQLException e) {
            connection.rollback(); // ⚠️ 关键:异常时立即回滚整个事务
            log.error("Transaction rolled back due to SQL error: {}", e.getMessage(), e);
            throw new GenericRuntimeException("Bulk insert failed and was rolled back", e);
        }
    } catch (SQLException e) {
        throw new GenericRuntimeException("Failed to acquire database connection", e);
    }
}

? 关键要点说明

  • setAutoCommit(false) 必须在获取 Connection 后、任何 DML 操作前调用,且需确保其作用于整个逻辑事务范围;
  • rollback() 应在 catch (SQLException) 中紧随异常捕获之后立即执行,避免因后续代码异常导致回滚遗漏;
  • 避免在 forEach() 中嵌套 try-catch:原代码中对每个 partition 单独 try-catch 并吞掉异常,会导致事务无法整体回滚(如第3批失败,前2批已“事实提交”),应改用传统 for 循环并让异常穿透至外层事务处理块;
  • clearBatch() 是良好实践:防止上一批残留指令干扰当前批次;
  • 日志建议包含批次序号与大小:便于故障定位与性能分析;
  • 连接资源由 try-with-resources 自动关闭:即使 rollback 后,Connection 仍需安全释放。

❗ 注意事项

  • 若业务要求“单批次失败不影响其他批次”,则需将事务粒度降至每批次一级(即每个 partition 内 setAutoCommit(false) → 操作 → commit()/rollback()),但此时已不满足“全部成功或全部失败”的强一致性语义;
  • connection.rollback() 仅对当前事务有效;若连接已关闭或处于已提交状态,调用会抛出 SQLException,因此务必确保其只在活跃事务中执行;
  • 生产环境建议配合数据库连接池(如 HikariCP)的事务隔离级别配置(如 TRANSACTION_READ_COMMITTED),并在必要时显式设置 connection.setTransactionIsolation(...)。

遵循以上模式,即可在 JDBC 批量场景中真正实现原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)与持久性(Durability),让数据变更经得起故障考验。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
php中foreach用法
php中foreach用法

本专题整合了php中foreach用法的相关介绍,阅读专题下面的文章了解更多详细教程。

71

2025.12.04

数据库三范式
数据库三范式

数据库三范式是一种设计规范,用于规范化关系型数据库中的数据结构,它通过消除冗余数据、提高数据库性能和数据一致性,提供了一种有效的数据库设计方法。本专题提供数据库三范式相关的文章、下载和课程。

356

2023.06.29

如何删除数据库
如何删除数据库

删除数据库是指在MySQL中完全移除一个数据库及其所包含的所有数据和结构,作用包括:1、释放存储空间;2、确保数据的安全性;3、提高数据库的整体性能,加速查询和操作的执行速度。尽管删除数据库具有一些好处,但在执行任何删除操作之前,务必谨慎操作,并备份重要的数据。删除数据库将永久性地删除所有相关数据和结构,无法回滚。

2078

2023.08.14

vb怎么连接数据库
vb怎么连接数据库

在VB中,连接数据库通常使用ADO(ActiveX 数据对象)或 DAO(Data Access Objects)这两个技术来实现:1、引入ADO库;2、创建ADO连接对象;3、配置连接字符串;4、打开连接;5、执行SQL语句;6、处理查询结果;7、关闭连接即可。

348

2023.08.31

MySQL恢复数据库
MySQL恢复数据库

MySQL恢复数据库的方法有使用物理备份恢复、使用逻辑备份恢复、使用二进制日志恢复和使用数据库复制进行恢复等。本专题为大家提供MySQL数据库相关的文章、下载、课程内容,供大家免费下载体验。

255

2023.09.05

vb中怎么连接access数据库
vb中怎么连接access数据库

vb中连接access数据库的步骤包括引用必要的命名空间、创建连接字符串、创建连接对象、打开连接、执行SQL语句和关闭连接。本专题为大家提供连接access数据库相关的文章、下载、课程内容,供大家免费下载体验。

325

2023.10.09

数据库对象名无效怎么解决
数据库对象名无效怎么解决

数据库对象名无效解决办法:1、检查使用的对象名是否正确,确保没有拼写错误;2、检查数据库中是否已存在具有相同名称的对象,如果是,请更改对象名为一个不同的名称,然后重新创建;3、确保在连接数据库时使用了正确的用户名、密码和数据库名称;4、尝试重启数据库服务,然后再次尝试创建或使用对象;5、尝试更新驱动程序,然后再次尝试创建或使用对象。

412

2023.10.16

vb连接access数据库的方法
vb连接access数据库的方法

vb连接access数据库方法:1、使用ADO连接,首先导入System.Data.OleDb模块,然后定义一个连接字符串,接着创建一个OleDbConnection对象并使用Open() 方法打开连接;2、使用DAO连接,首先导入 Microsoft.Jet.OLEDB模块,然后定义一个连接字符串,接着创建一个JetConnection对象并使用Open()方法打开连接即可。

409

2023.10.16

拼多多赚钱的5种方法 拼多多赚钱的5种方法
拼多多赚钱的5种方法 拼多多赚钱的5种方法

在拼多多上赚钱主要可以通过无货源模式一件代发、精细化运营特色店铺、参与官方高流量活动、利用拼团机制社交裂变,以及成为多多进宝推广员这5种方法实现。核心策略在于通过低成本、高效率的供应链管理与营销,利用平台社交电商红利实现盈利。

31

2026.01.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号