"x is a procedure, use "当我已经在使用呼叫时呼叫""

"x is a procedure, use "call"" when I am already using call

我正在使用 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;
    $$;

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

它会完美运行

然而,从我的 Java 程序调用它如下:

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

给我以下错误:

我试过的

如有任何想法,我们将不胜感激

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

CallableStatement stmt = conn.prepareCall("call reducestock(?, ?)");

过程 inocation ({call reduceStock(?, ?)}) 周围的大括号表示这不是原生 SQL,而是 JDBC 语法。您可以在这里阅读更多相关信息:Why do JDBC calls to stored procedures wrap the call in curly brackets?.

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

使其工作的一种方法是,如 @a_horse_with_no_name wrote in ,删除大括号。这使该语句成为本机调用,并且因为它是有效的 Postgres SQL 这将起作用。缺点是它不太跨平台,因为它不适用于不支持 call procname() 语法的数据库。例如,这不适用于 Oracle,因此如果您必须支持多个 JDBC 驱动程序,这是不太可取的方法。

更好的解决方法是提示 Postgres JDBC 驱动程序将此语法视为存储过程调用而不是函数调用,并将其相应地转换为 SQL。为此,Postgres 驱动程序也公开了一个 escapeSyntaxCallMode configuration property (check out the EscapeSyntaxCallMode enum):

Specifies how the driver transforms JDBC escape call syntax into underlying SQL, for invoking procedures or functions. (backend >= 11) In escapeSyntaxCallMode=select mode (the default), the driver always uses a SELECT statement (allowing function invocation only). In escapeSyntaxCallMode=callIfNoReturn mode, the driver uses a CALL statement (allowing procedure invocation) if there is no return parameter specified, otherwise the driver uses a SELECT statement. In escapeSyntaxCallMode=call mode, the driver always uses a CALL statement (allowing procedure invocation only).

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

您可以在 Postgres 文档 (Chapter 6. Calling Stored Functions and Procedures) 中找到使用此设置的示例:

// 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();>

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