“x 是一个过程,当我已经使用 call 时使用“call””

Swo*_*get 5 java postgresql procedure jdbc psql

我正在使用 Postgres 12 并编写了以下过程:

CREATE OR REPLACE PROCEDURE reduceStock(id INTEGER, soldQuantity INTEGER)
    LANGUAGE plpgsql AS
    $$
    BEGIN
    UPDATE inventory SET ProductStockAmount = ProductStockAmount - soldQuantity WHERE ProductID = id;     
    END;
    $$;

Run Code Online (Sandbox Code Playgroud)

如果我在命令行上打开 psql 并运行,它会完美运行call reduceStock(1,1);

但是,从我的 Java 程序中调用它如下:

CallableStatement stmt = conn.prepareCall("{call reduceStock(?, ?)}");
stmt.setInt(1, productID);
stmt.setInt(2, quantity);
stmt.execute();
Run Code Online (Sandbox Code Playgroud)

给我以下错误: 在此输入图像描述

我尝试过的

  • call reduceStock(1,1);从 psql 客户端运行- 完美运行
  • 删除数据库并重新开始查看是否缓存了某些旧定义 - 不起作用
  • 不同大小写、不同间距call

任何想法,将不胜感激

小智 9

您需要删除花括号,它是调用过程的 JDBC 转义。但因为 Postgres 有它自己的call命令,所以不需要它们(并且与 JDBC 转义冲突)。

CallableStatement stmt = conn.prepareCall("call reducestock(?, ?)");
Run Code Online (Sandbox Code Playgroud)


jan*_*nis 6

过程 inocation ( {call reduceStock(?, ?)}) 周围的大括号意味着这不是本机 SQL,而是 JDBC 语法。您可以在此处阅读更多相关信息:Why do JDBC Calls to Stored procedure 将调用括在大括号中?

因此,像这样的调用仍然必须由 JDBC 驱动程序转换为本机 SQL。默认情况下,Postgres 驱动程序将此类语句视为函数调用并将它们转换为SELECT reduceStock(?, ?)SQL 查询。这不是在 Postgres 中调用存储过程的方式。在Postgres中,存储过程调用SQL是call reduceStock(?, ?)

使其发挥作用的一种方法是,就像@a_horse_with_no_name在他的回答中写的那样,删除花括号。这使得该语句成为本机调用,并且因为它是有效的 Postgres SQL,所以它会起作用。缺点是它的跨平台性较差,因为它不适用于不支持语法的数据库call procname()。例如,这不适用于 Oracle,因此如果您必须支持多个 JDBC 驱动程序,那么这是不太优选的方法。

更好的解决方法是提示 Postgres JDBC 驱动程序将此语法视为存储过程调用而不是函数调用,并将其相应地转换为 SQL。为此,Postgres 驱动程序公开了escapeSyntaxCallMode配置属性(另请查看EscapeSyntaxCallMode 枚举):

指定驱动程序如何将 JDBC 转义调用语法转换为基础 SQL,以调用过程或函数。(backend >= 11) 在 escapeSyntaxCallMode=select 模式(默认)下,驱动程序始终使用 SELECT 语句(仅允许函数调用)。在 escapeSyntaxCallMode=callIfNoReturn 模式下,如果没有指定返回参数,驱动程序将使用 CALL 语句(允许过程调用),否则驱动程序将使用 SELECT 语句。在 escapeSyntaxCallMode=call 模式下,驱动程序始终使用 CALL 语句(仅允许过程调用)。

正如您所看到的,{call something()}默认情况下所有语句都被视为函数调用,并且始终转换为 SELECT。设置escapeSyntaxCallModecall将使驱动程序将它们转换为callSQL 语句。该callIfNoReturn选项对于大多数用例来说似乎是最合理的,因为如果未指定返回参数,它将把 JDBC 调用转换为存储过程调用,否则转换为函数调用。

您可以在 Postgres 文档中找到使用此设置的示例(第 6 章。调用存储函数和过程):

// set up a connection
String url = "jdbc:postgresql://localhost/test"; 
Properties props = new Properties(); 
// ... other properties ... 
// Ensure EscapeSyntaxCallmode property set to support procedures if no return value
props.setProperty("escapeSyntaxCallMode", "callIfNoReturn");
Connection con = DriverManager.getConnection(url, props);

// Setup procedure to call.
Statement stmt = con.createStatement();
stmt.execute("CREATE TEMP TABLE temp_val ( some_val bigint )");
stmt.execute("CREATE OR REPLACE PROCEDURE commitproc(a INOUT bigint) AS '"
    + " BEGIN "
    + " INSERT INTO temp_val values(a); "
    + " COMMIT; "
    + " END;' LANGUAGE plpgsql");
stmt.close();

// As of v11, we must be outside a transaction for procedures with transactions to work.
con.setAutoCommit(true);

// Procedure call with transaction
CallableStatement proc = con.prepareCall("{call commitproc( ? )}");
proc.setInt(1, 100);
proc.execute(); proc.close();>
Run Code Online (Sandbox Code Playgroud)

-- https://jdbc.postgresql.org/documentation/head/callproc.html#call-procedure-example