是否可以创建一个准备好的语句,并在以后在postgres下与Java一起使用?

问题描述

我正在尝试使用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,也许我不应该玩弄代码风格而导致无法轻易测试,难以发现并且会出错的错误仅在忙碌时才引起问题。

因此,请遵循(严格)协议以避免此类情况:

  1. 任何创建的资源(通过new SomeResource()或通过明确表明它是创建者的方法(例如Files.newInputStream)必须用相同的代码关闭或,该代码又必须是可关闭的资源。
  2. 提高#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不会强制执行此操作,但是如果您不遵循这些规则,肯定会遇到麻烦。