多行 INSERT 与重复调用 PreparedStatement.executeUpdate()

Posted

技术标签:

【中文标题】多行 INSERT 与重复调用 PreparedStatement.executeUpdate()【英文标题】:Multi-row INSERT vs. repeatedly calling PreparedStatement.executeUpdate() 【发布时间】:2013-08-01 04:07:13 【问题描述】:

如果我有一个使用以下架构创建的表:

CREATE TABLE names (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR);

还有一个List<String> names 我想添加到表中的名称,我看到了两种解决方法。

简单的蛮力解决方案是手动构造 SQL,如下所示:

String sql = "INSERT INTO names (name) VALUES ("+
             Joiner.on("), (").join(names)+
             ");";
conn.createStatement().execute(sql);

这仅在 DB 中出现一次,并且看起来直观且直接。我知道存在 SQL 注入问题,但为了这个问题,我们假设这些问题已经得到解决。

替代方法是使用准备好的语句,然后 JDBC docs suggest 重复执行该语句,如下所示:

con.setAutoCommit(false);
PreparedStatement addNames = con.prepareStatement(
   "INSERT INTO names (name) VALUES (?);");

for (String name : names) 
    addNames.setString(1, name);
    addNames.executeUpdate();
    con.commit();

con.setAutoCommit(true);

这样重复执行PreparedStatement真的比简单地构造查询String并直接执行更可取吗?

【问题讨论】:

许多与性能相关的问题的问题在于它们过于理论化。一个人没有表,没有要插入的数据,没有要运行的单个查询,没有要处理的瓶颈,但由于某种原因,他突然遇到了一个问题——这更快。 为了更公平的比较,您应该 1) 将 con.commit() 移出循环并 2) 使用批量更新(尽管它取决于驱动程序和数据库是否具有不同的性能) JDBC 文档中的示例在循环中有con.commit();,所以这就是我这样做的原因。同样对在循环中进行提交持怀疑态度。 @YourCommonSense 我不同意 - 向数据库发出 O(n) 请求并不是“不必要的理论”,它对于可扩展的应用程序来说是一个至关重要的问题。在我看来,最佳安全实践(使用 PreparedStatement)与最佳可扩展性实践(最小化数据库调用)背道而驰,我试图了解适当的平衡在哪里。 【参考方案1】:

还有两个选择:

使用PreparedStatement.addBatch()。对于许多数据库,这将减少服务器往返次数。但并非适用于所有数据库(例如,不适用于 H2 数据库)。

使用PreparedStatement,但多行,批量:

INSERT INTO names (name) VALUES (?), (?), (?), (?), (?), (?), (?), (?);

第二个应该减少所有数据库的服务器往返,但它有点复杂。

【讨论】:

谢谢,由于某种原因,我错过或误解了addBatch()/executeBatch,做更多的挖掘是非常有意义的,这正是我想要的。【参考方案2】:

是的,它更快 因为每次java应用程序向数据库发送一条新的SQL语句时,服务器都会检查语法错误,准备执行该语句的计划并执行。

如果再次发送相同的语句,尽管数据库服务器会检查它是否已经收到与它完全相同的语句。如果是这样,服务器不会检查其语法并为其准备执行计划,因此服务器只会执行它。

为了利用这个数据库特性,java 提供了prepared statement 的使用。这个特性允许你发送发送语句到数据库服务器,通过接受发送给它的参数值来重复执行。这改进了数据库性能,因为数据库服务器只需检查语法并为每个语句准备一次执行计划。

【讨论】:

我想更大的瓶颈将是服务器和客户端之间的来回,而不是验证时间。根据您的描述,这两个语句都经过一次验证,因此 PreparedStatement 不需要多次验证这一事实似乎并没有帮助,因为天真的解决方案首先只有一个查询要运行。 ..

以上是关于多行 INSERT 与重复调用 PreparedStatement.executeUpdate()的主要内容,如果未能解决你的问题,请参考以下文章

将多行插入多个表而不重复“INSERT INTO”

如何在oracle 11g中使用一个INSERT语句在一列中插入多行[重复]

INSERT INTO .. ON DUPLICATE KEY更新多行记录

如何在不重复声明的“INSERT INTO dbo.Blah”部分的情况下插入多行?

INSERT INTO .. ON DUPLICATE KEY更新多行记录

插入多行时重复项会发生什么?