如何确保以原子方式完成 JDBC 批量插入?
Posted
技术标签:
【中文标题】如何确保以原子方式完成 JDBC 批量插入?【英文标题】:How can I ensure that a JDBC batch insert is done atomically? 【发布时间】:2022-01-19 20:18:21 【问题描述】:我有以下(伪)代码将约 5000 行插入到 SQL Server 表中。我正在使用 Hikari(ds
,下面是我的 HikariDataSource)。
try (Connection connection = ds.getConnection();
PreparedStatement statement = connection.prepareStatement(
"insert into Dog (name, age, breed) values (?, ?, ?)");)
for (Dog d : dogs)
statement.setString(1, d.getName());
statement.setInt(2, d.getAge());
statement.setString(3, d.getBreed());
statement.addBatch();
statement.executeBatch();
// catch exceptions, etc.
这工作正常(插入按预期工作),但如果有人在批量插入中间查询表(这需要几秒钟),他们可能会得到一组不完整的行。我希望他们要么一无所获(假设表一开始是空的),要么我插入的每一行。
我认为我需要做一些特殊的事情来锁定表或以其他方式将所有插入作为单个事务执行(我认为这就是批量插入 但我错了)。
我该怎么做?
【问题讨论】:
SQL Server 可以轻松地一次性快速插入 1,000 行(甚至数百万行)。事实上,5,000 个单独的INSERT
语句将比 5,000 行的单个语句慢得多。如果您可以传递表格类型参数/变量,那么您可以一次性INSERT
这么多,不会让人得到公正的结果,而且会更快。
@Larnu Hmmm - 1)“传递表类型参数/变量”是什么意思?和 2)我正在做一个批量插入,我“一次”插入所有东西(或者我想) - 我不是通过批量插入“一次性插入全部”吗?
基本上听起来像什么。它是一个表类型的参数。不,包含 5,000 个 INSERT
语句的批处理不会同时插入所有这些行; SQL一个接一个地运行批处理中的每个语句,因此批处理中的每个语句将按顺序运行。
@DanGuzman 这是否就像在迭代 Dogs 之前添加 connection.setAutoCommit(false);
并在 statement.executeBatch()
之后添加 connection.commit()
一样简单?
见Using table-valued parameters。这个example 似乎很相关。
【参考方案1】:
默认情况下,连接处于自动提交模式。自动提交模式下批处理执行的事务行为取决于 JDBC 驱动程序的实现。如果要确保它们以原子方式完成,则需要禁用自动提交模式并在执行批处理后显式提交。
try (Connection connection = ds.getConnection();
PreparedStatement statement = connection.prepareStatement(
"insert into Dog (name, age, breed) values (?, ?, ?)");)
connection.setAutoCommit(false);
for (Dog d : dogs)
statement.setString(1, d.getName());
statement.setInt(2, d.getAge());
statement.setString(3, d.getBreed());
statement.addBatch();
statement.executeBatch();
connection.commit();
【讨论】:
以上是关于如何确保以原子方式完成 JDBC 批量插入?的主要内容,如果未能解决你的问题,请参考以下文章