首页 > 解决方案 > 用于多个准备好的语句的 JDBC 批处理

问题描述

是否可以将来自多个 JDBC 准备语句的提交批处理在一起?

在我的应用程序中,用户将插入一条或多条记录以及相关表中的记录。例如,我们需要更新“contacts”表中的一条记录,删除“tags”表中的相关记录,然后插入一组新的标签。

UPDATE contacts SET name=? WHERE contact_id=?;
DELETE FROM tags WHERE contact_id=?;
INSERT INTO tags (contact_id,tag) values (?,?);
// insert more tags as needed here...

这些语句需要成为单个事务的一部分,我想在到服务器的一次往返中完成它们。

要在单次往返中发送它们,有两种选择:为每个命令创建一个 Statement 然后调用.addBatch(),或者为每个命令创建一个PreparedStatement,然后调用.setString().setInt()等等。对于参数值,然后调用.addBatch()

第一个选择的问题是在 . addBatch()调用效率低下,并且您无法从已清理的参数输入中受益。

第二种选择的问题是它可能无法保留 SQL 语句的顺序。例如,

Connection con = ...;
PreparedStatement updateState = con.prepareStatement("UPDATE contacts SET name=? WHERE contact_id=?;");
PreparedStatement deleteState = con.prepareStatement("DELETE FROM contacts WHERE contact_id=?;");
PreparedStatement insertState = con.prepareStatement("INSERT INTO tags (contact_id,tag) values (?,?);");

updateState.setString(1, "Bob");
updateState.setInt(1, 123);
updateState.addBatch();

deleteState.setInt(1, 123);
deleteState.addBatch();

... etc ...
... now add more parameters to updateState, and addBatch()...
... repeat ...

con.commit();

在上面的代码中,是否可以保证所有语句都将按照我们调用的顺序执行.addBatch(),甚至跨不同的预处理语句?订购显然很重要;我们需要在插入新标签之前删除标签。

我还没有看到任何文档说将为给定的连接保留语句的顺序。

如果这很重要,我正在使用 Postgres 和默认的 Postgres JDBC 驱动程序。

标签: postgresqljdbcprepared-statement

解决方案


批处理是每个语句对象,因此每次executeBatch()调用StatementPreparedStatement对象时都会执行批处理。换句话说,这只执行与该语句对象的批处理相关联的语句(或值集)。不可能跨多个语句对象“排序”执行。在单个批次中,订单被保留。

如果您需要以特定顺序执行语句,则需要以该顺序显式执行它们。这要么意味着单独调用execute()每个值集,要么使用单个Statement对象并动态生成语句。由于 SQL 注入的可能性,不推荐使用最后一种方法。


推荐阅读