问题描述
我正在尝试使用postgres进行一些测试。我想知道是否有可能创建定义PreparedStatement之类的
String statement = "Insert into table_one values (?)";
PreparedStatement insert = con.prepareStatement(statement);
insert.execute()
// tell postgres to create this Prepared statement without
尝试时,我收到一条错误消息:sqlState: 22023 No value specified for parameter 1
此外,如果我要执行以下操作
PreparedStatement insert = con.prepareStatement(statement);
insert.setInt(1,10); //insert into table_one values (10);
insert.execute()
.... //commit other transactions to postgres
insert.setInt(1,20);
insert.execute();
postgres是否会“记住” /已将以上内容注册为准备好的语句(我不是在谈论缓存实际的语句)?
解决方法
当将java.sql.PreparedStatement
与PostgreSQL JDBC驱动程序一起使用时,它首先不会在数据库服务器上创建真正的预准备语句,而只是构造一个简单的SQL语句以发送到数据库服务器。仅在第六次执行时,它才会认为值得在服务器上创建一个名为“ prepared语句”的代码,以供将来执行时重用。
您可以使用prepareThreshold
连接属性来影响行为,请参见the documentation。因此,要使第二个示例使用服务器准备好的语句,就必须将阈值降低到0。仅当您知道将重用所有准备好的语句时,这才有用。考虑到准备好的语句通常用于其他目的,例如避免SQL注入问题。
在数据库服务器上有一个类似的功能:前五次执行一条准备好的语句,PostgreSQL为此计算一个自定义计划。仅在第六次执行时,它才会考虑改用通用计划,这样您就可以避免以后的计划开销。从v12开始,PostgreSQL参数plan_cache_mode
会影响这一点。
因此,使用默认设置,将需要执行java.sql.PreparedStatement
十次,然后才能避免避免计划成本而提高性能。
是的,如第二个片段所示,可以做到这一点。
您可以创建一个Prepared语句,然后等待年龄和年龄,然后设置每个“参数”(对于您传递的SQL字符串中的每个问号,调用.setInt(1,...)
,然后调用其中一个execute方法,然后...再等待一些年龄,然后根据需要将其重复多次。
准备好的语句的寿命与连接的寿命一样长,或者直到您将其关闭为止。真正的问题在于:关闭它们。 您必须明确将其关闭。它们就像文件和网络套接字一样。
我把它弄乱了,您的应用不会立即崩溃,并且任何测试都不会失败。但是,该连接是永久性的“损坏”,因为它只具有有限数量的准备好的语句的空间。随着您不断制造更多产品而从未关闭它们,最终您将用光,并且将发生奇怪的SQL异常。您的应用是滴答作响的定时炸弹。这是最糟糕的错误类别:难以测试,会在忙碌的时候不可避免地爆炸您的应用程序。当bossman开始询问法律团队是否可以起诉您重大过失时,就是这样:Oof,也许我不应该玩弄代码风格而导致无法轻易测试,难以发现并且会出错的错误仅在忙碌时才引起问题。
因此,请遵循(严格)协议以避免此类情况:
- 任何创建的资源(通过
new SomeResource()
或通过明确表明它是创建者的方法(例如Files.newInputStream
)必须用相同的代码关闭或,该代码又必须是可关闭的资源。 - 提高#1的质量,除非您自己是一个可关闭的实体,否则只能在try-with块中打开资源。
换句话说,您要么这样做:
try (PreparedStatement ps = ....) {
// use ps here,as often as you want....
} // ps is closed here
或者您这样做:
class WhateverTool implements AutoClosable {
private PreparedStatement ps; // long-lived
@Override public void close() throws SQLException {
if (ps != null) ps.close();
}
}
和您与此类的仅互动本身就是:
try (WhateverTool t = new WhateverTool()) {
// do whatever you want here...
} // but this is where it ends
代码库中没有其他选项可以与资源进行交互。
Java不会强制执行此操作,但是如果您不遵循这些规则,肯定会遇到麻烦。