是否可以创建一个准备好的语句,然后在postgres下用Java重用它?

是否可以创建一个准备好的语句,然后在postgres下用Java重用它?,java,sql,postgresql,prepared-statement,Java,Sql,Postgresql,Prepared Statement,我正试着和博士后一起测试一些东西。我想知道是否可以创建并定义一个PreparedStatement,例如 String statement = "Insert into table_one values (?)"; PreparedStatement insert = con.prepareStatement(statement); insert.execute() // tell postgres to create this Prepared statement with

我正试着和博士后一起测试一些东西。我想知道是否可以创建并定义一个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没有为参数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是否会“记住”/已将上述内容注册为准备好的语句(我不是说缓存实际语句)?

正如您的第二个代码片段所示,是的,您可以这样做

您可以创建一个preparedstatement,然后等待一段又一段时间,然后为传递的SQL字符串中的每个问号设置每个“参数”(例如调用
.setInt(1,…)
),并调用其中一个执行方法,然后。。。再等一段时间,然后再做一次,想做多少次就做多少次

preparedstatement的有效期与连接的有效期相同,或者直到您关闭它为止,您应该这样做。真正的麻烦在于:关闭它们您必须明确关闭它们。它们就像文件和网络套接字那样

如果你搞砸了,你的应用程序不会立即崩溃,测试也不会失败。然而,连接是永久性的“损坏”,因为它只能容纳有限数量的准备好的声明。当您不断地进行更多操作并且从不关闭它们时,最终会耗尽资源,并且会出现奇怪的SQL异常。你的应用程序是一颗滴答作响的定时炸弹。这是最糟糕的一类bug:很难测试,当事情变得繁忙时,不可避免地会爆炸你的应用程序。当bossman开始询问法律团队是否可以起诉你的重大过失时,你就会说:噢,也许我不应该玩弄那些导致错误的代码样式,这些错误不容易测试,很难发现,并且只会在事情繁忙时才引起问题

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

  • 创建的任何资源(通过
    new SomeResource()
    ,或者通过一种非常清楚它是创建者的方法,例如
    文件.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 (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没有强制执行这一点,但如果不遵守这些规则,您肯定会遇到麻烦。

    当您在PostgreSQL JDBC驱动程序中使用
    Java.sql.PreparedStatement
    时,它首先不会在数据库服务器上创建真正的prepared语句,而只是构造一个简单的sql语句发送到数据库只有在第六次执行时,它才会认为值得在服务器上创建一个命名的prepared语句,以供将来执行时重用

    您可以使用<代码>准备好的阈值>代码>连接属性来影响行为,请参见第二个示例使用服务器准备好的语句,您必须将阈值降低到0。只有当您知道您将重用所有已准备好的语句时,这才是有用的;比如避免SQL注入问题

    在数据库服务器上有一个类似的功能:执行准备好的语句的前五次,PostgreSQL计算它的自定义计划。只有在第六次执行时,它才会考虑切换到一个通用的计划,这样才能避免从那时起计划的开销。这可以受到PostgreSQL语句的影响。从v12开始


    因此,在默认设置下,需要执行十次
    java.sql.PreparedStatement
    ,才能看到由于避免计划成本而提高的性能。

    是否可以使用参数化查询而不为所有参数提供值?否。是否可以重用准备好的查询(使用相同或其他值,如果它也是参数化的)?是的。我的问题更多的是postgres的问题。。postgres是保持preparestatement活动,还是它一直在发送消息,postgres只是按原样处理它。