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

Posted

技术标签:

【中文标题】JPA 和 PostgreSQL:如何调用具有 void 返回类型的存储过程?【英文标题】:JPA & PostgreSQL: How do I call a stored procedure with void return type? 【发布时间】:2011-11-29 22:32:36 【问题描述】:

我正在尝试调用执行更新的 PL/pgSQL 函数(语言 sql,返回 void)。我遇到了这个异常:

Internal Exception: org.postgresql.util.PSQLException: A result was returned when none was expected.

persistence.xml:

<named-native-query name="Clinic.deactivateByNotFoundInIncomingClinic">
    <query>
        <![CDATA[
            select apply_incoming_clinic_deletions(?)
        ]]>
    </query>
</named-native-query>

道:

public void deactivateByNotFoundInIncomingClinic(long clinicSystemId)

    em.createNamedQuery("Clinic.deactivateByNotFoundInIncomingClinic")
            .setParameter(1, clinicSystemId)
            .executeUpdate();

救命!

更新:

在 GlassFish 3.1.1 上使用 EclipeseLink 通过 PostgreSQL 9.0-801 JDBC 4 驱动程序与 PostgreSQL 9.0 数据库通信。

【问题讨论】:

如果您可以直接运行该 SQL 语句(psql 或 pgAdminIII),您可以将问题隔离到数据库或中间代码。其他要检查的事项:函数的返回值是否与声明的返回类型匹配?尽管出现错误消息,是否仍执行更新? JPA 和 PostgreSQL 是最新的吗?您是否尝试提高日志级别或调试级别以获取更多信息? @Catcall 请再次阅读问题。该函数没有返回类型。 SQL 语句 (select apply_incoming_clinic_deletions(1)) 本身运行良好。 函数有返回类型;它的类型是void。当函数返回(或试图返回)其声明类型以外的内容时,在 PostgreSQL 的早期版本中存在相同的错误消息。 如果 SQL 语句在 JPA 之外工作,在我看来问题必须是数据、您的代码、JPA 或数据库连接器。 (JPA 有自己的,还是使用 JDBC?)也许数据库连接器还没有准备好处理 Erwin Brandstetter 在下面指出的微妙之处。 我遇到了同样的问题(不过我使用的是不同的 JDBC 包装库),我可以通过使用 execute() 而不是 executeUpdate() 来解决它。 【参考方案1】:

不知道这是从哪里来的。问题中缺少您的函数定义。 但是您可以尝试使用plpgsql 函数而不是sql。当声明为RETURNS void 时,它们的返回类型略有不同。考虑这个演示:

CREATE OR REPLACE FUNCTION f_sql()
  RETURNS void AS
'UPDATE foo SET id = id+1 WHERE id = 34567'
  LANGUAGE sql;

CREATE OR REPLACE FUNCTION f_plpgsql()
  RETURNS void AS
$$
BEGIN
UPDATE foo SET id = id+1 WHERE id = 34567;
END;
$$  LANGUAGE plpgsql;

现在,VOID 是虚构的类型。虽然 plpgsql 函数实际上返回 VOID,但 SQL 函数似乎返回 NULL。我自己也不确定这是为什么。

db=# SELECT f_sql() IS NULL;
 ?column?
----------
 t

db=# SELECT f_plpgsql() IS NULL;
 ?column?
----------
 f

【讨论】:

不知道这是否是 OP 问题的根源,但 +1 表示注意到回报的差异。 @Catcall:我自己也很困惑。我实际上发布了question about that with more details。 是的,在 JPA 外部执行时存在差异,但我在 JPA 内部得到了相同的结果(GlassFish 3.1.1 上的 EclipeseLink 使用 PostgreSQL 9.0-801 JDBC 4 驱动程序与 PostgreSQL 9.0 数据库通信)。但是,当我将 executeUpdate() 更改为 getSingleResult() 时,它使用语言 plpgsql 工作。到那时,我已经更改了函数的主体,因此我无法恢复到 language sql 以查看它是否在其中发挥作用。我很快就会用另一个函数进行测试。 我测试了其他功能。将executeUpdate() 更改为getSingleResult() 是完成这项工作所需的全部内容。 sqlplpgsql 的选择没有区别。 @SteveTaylor:感谢您的反馈。这是我们应该期待的,很高兴得到确认。这是有道理的,你不能为函数调用.executeUpdate();,至于客户端,它不是UPDATE 语句,而是一个函数——即使UPDATE 是里面的全部。【参考方案2】:

将 DAO 代码更改为此有效:

public void deactivateByNotFoundInIncomingClinic(long clinicSystemId)

    em.createNamedQuery("Clinic.deactivateByNotFoundInIncomingClinic")
            .setParameter(1, clinicSystemId)
            .getSingleResult();

【讨论】:

【参考方案3】:

程序

DROP FUNCTION updatename(bigint);
CREATE OR REPLACE FUNCTION updatename(var_id bigint) 
RETURNS void AS $$
DECLARE


BEGIN
    update person set
        name ='lucas'
    where id=var_id;

RETURN;

END;$$
LANGUAGE 'plpgsql';

//调用过程

Long id=50;
Query  query = emProvider.get().createNativeQuery("SELECT updatename(?1)") ;
        query.setParameter(1,id);
        query.executeUpdate();

【讨论】:

以上是关于JPA 和 PostgreSQL:如何调用具有 void 返回类型的存储过程?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 JPA 和 Hibernate 映射 PostgreSQL 枚举

jOOQ 无法映射 PostgreSQL 列以使 JPA 满意

将数组传递给 PostgreSQL PL/pgSQL 函数

调用 getNextException 查看原因:如何让 Hibernate / JPA 显示异常的数据库服务器消息

如何使用 PostgreSQL 中的变量创建具有内部连接的视图?

如何使每个表中的主键值应从数字一(1)开始 - PostgreSQL,Spring data jpa