Java 和 PostgreSQL 存储过程 - 返回注册为 out 参数,导致 in 参数出现问题

Posted

技术标签:

【中文标题】Java 和 PostgreSQL 存储过程 - 返回注册为 out 参数,导致 in 参数出现问题【英文标题】:Java and PostgreSQL stored procedure - return registered as out parameter, causing issues with in parameters 【发布时间】:2011-01-17 16:13:36 【问题描述】:

我正在尝试从 Java 应用程序调用 PostgreSQL 存储过程;该过程有一个 DATE 类型参数,所以我使用带有 CallableStatement.setDate() 的 java.sql.Date 类型。但是,执行语句总是会导致异常,并且 SQL 日志会显示:

LOG:  execute <unnamed>: select * from athlete.create_athlete($1,$2,$3,$4,$5,$6,$7) as result
DETAIL:  parameters: $1 = '', $2 = 'foo@bar.com', $3 = 'Joe', $4 = 'Blow', $5 = 'foobar', $6 = 'M', $7 = '1979-03-22 -04:00:00'
ERROR:  column "dob" is of type date but expression is of type text at character 122
HINT:  You will need to rewrite or cast the expression.
QUERY:  INSERT INTO athlete.athlete (email, first_name, last_name, password, gender, dob) VALUES ( $1 ,  $2 ,  $3 ,  $4 ,  $5 ,  $6 )
CONTEXT:  PL/pgSQL function "create_athlete" line 2 at SQL statement
STATEMENT:  select * from athlete.create_athlete($1,$2,$3,$4,$5,$6,$7) as result

存储过程实际上有 6 个参数(并且应该接收上面的 $2 到 $7 的值) - 第 7 个来自将返回值注册为 out 参数。这让我很困惑 - 当我为返回值注册一个 out 参数时,它显示为 7 个参数是否正确?

从我读过的所有文档中,我的印象是返回值必须注册为第一个参数:

registerQuery = "? = call athlete.create_athlete(?,?,?,?,?,?)";
...
CallableStatement cs = conn.prepareCall(registerQuery);
cs.registerOutParameter(1, Types.BOOLEAN);
cs.setString(2, email);
...

上面的错误提示我存储过程参数和提供给插入语句的参数不匹配。我一直在关注所有这些的文档,但显然做错了什么。如何为存储过程提供正确的参数并在调用后检索返回值?

【问题讨论】:

【参考方案1】:

取决于您的存储过程类型(函数/过程)的签名。

对于像下面这样的函数,out 参数将是第一个参数,并将 param1 和 param2 作为第二个和第三个参数。

数据库过程(用于函数):

CREATE FUNCTION my_func (
param1 INT,
param2 INT) 
    RETURNS INT
AS 
    :
    :

Java 代码(用于函数):

registerQuery = "? = call my_func(?,?)";
...
CallableStatement cs = conn.prepareCall(registerQuery);
cs.registerOutParameter(1, Types.INTEGER);
cs.setInteger(2, 10);
cs.setInteger(3, 10);
...

.

但是对于像下面这样的过程,out 参数将是第三个参数,并将 param1 和 param2 作为第一个和第二个参数。

数据库过程(用于过程):

CREATE PROCEDURE my_proc (
param1 INT,
param2 INT,
OUT param3 INT)
BEGIN
    :
    :
END;

Java 代码(用于过程):

registerQuery = "call my_func(?,?,?)";
...
CallableStatement cs = conn.prepareCall(registerQuery);
cs.registerOutParameter(3, Types.INTEGER);
cs.setInteger(1, 10);
cs.setInteger(2, 10);
...

.

请注意,您可以有多个输出参数,而只有一个返回值。

【讨论】:

我使用的是以前的存储过程签名,所以我想这不是问题。我觉得奇怪的是,如果我将 Date 传递给 Date 参数,我需要将它转换为插入(插入到 Date 列)。 您能否使用存储过程的签名更新您的问题【参考方案2】:

问题在于传递给存储过程的参数顺序与传递给插入语句的参数顺序不匹配。如果顺序很重要,我不明白为什么 PostgreSQL 会使用命名参数。

例如,存储过程的签名如下:

CREATE FUNCTION insert_Person (IN in_name TEXT, IN in_gender CHAR(1), IN in_bdate DATE) RETURNS BOOLEAN...

该存储过程中包含的 INSERT 语句如下:

INSERT INTO Person (name, bdate, gender) VALUES (in_name, in_bdate, in_gender);

更改参数的顺序,使其在存储过程签名或插入语句中匹配(我选择前者)解决了这个问题。

【讨论】:

【参考方案3】:

Postgresql 服务器支持命名参数,但 jdbc 驱动程序不支持它(据我所知还不支持),所以在此之前只支持位置参数。

【讨论】:

【参考方案4】:

由于 OCI 层的限制,JDBC 驱动程序不支持 将 BOOLEAN 参数传递给 PL/SQL 存储过程。如果一个 PL/SQL 过程包含 BOOLEAN 值,您可以通过以下方式解决限制 用第二个 PL/SQL 过程包装 PL/SQL 过程,该过程接受 将参数作为 INT 并将其传递给第一个存储过程。什么时候 调用第二个过程,服务器执行从 INT 转 BOOLEAN。

【讨论】:

以上是关于Java 和 PostgreSQL 存储过程 - 返回注册为 out 参数,导致 in 参数出现问题的主要内容,如果未能解决你的问题,请参考以下文章

Postgresql 11:存储过程调用错误 - 要调用过程,请使用 CALL、Java

为啥我在从 Java 批量执行 PostgreSQL 存储过程时收到错误消息,提示“未预期结果”?

PostgreSQL存储过程

PostgreSQL 存储过程/函数

PostgreSQL Oracle 兼容性之存储过程

JPA 和 PostgreSQL:如何调用具有 void 返回类型的存储过程?