You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL+Hibernate 6调用存储函数报错,寻求解决方案

问题解决:Hibernate 6调用PostgreSQL存储函数报错

问题背景

创建了如下PostgreSQL存储函数:

CREATE OR REPLACE FUNCTION test_function(id varchar, OUT res_id varchar, OUT res_code int)
language plpgsql as
$$
begin
    -- body
    res_id := id;
    res_code := 5;
    return;
end; $$

使用StoredProcedureQuery编写的调用代码在Hibernate 5中运行正常,但升级到Hibernate 6后抛出错误:

ERROR: test_function(character varying) is not a procedure
Hint: To call a function, use SELECT.
Position: 6

原调用代码:

public TestFunctionResult callTestFunction(String id) {
    StoredProcedureQuery query = entityManager
            .createStoredProcedureQuery("test_function")
            .registerStoredProcedureParameter("id", String.class, ParameterMode.IN)
            .registerStoredProcedureParameter("res_id", String.class, ParameterMode.OUT)
            .registerStoredProcedureParameter("res_code", Integer.class, ParameterMode.OUT)
            .setParameter("id", id);
    TestFunctionResult testFunctionResult = new TestFunctionResult();
    try {
        query.execute();
        testFunctionResult.setId((String)query.getOutputParameterValue("res_id"));
        testFunctionResult.setCode((Integer) query.getOutputParameterValue("res_code"));
    } finally {
        query.unwrap(ProcedureOutputs.class).release();
    }
    return testFunctionResult;
}

原因分析

Hibernate 6对PostgreSQL的存储过程(PROCEDURE)和函数(FUNCTION)语义区分更严格:

  • Hibernate 5会自动将StoredProcedureQuery的调用适配为PostgreSQL函数的SELECT语法
  • Hibernate 6默认使用CALL语句调用,而PostgreSQL不允许用CALL执行FUNCTION类型对象,因此触发报错

解决方案

方案1:使用原生SQL查询调用函数

直接遵循PostgreSQL函数调用语法,用SELECT执行,同时保持结果映射逻辑:

public TestFunctionResult callTestFunction(String id) {
    List<Object[]> results = entityManager.createNativeQuery(
            "SELECT res_id, res_code FROM test_function(:id)")
            .setParameter("id", id)
            .getResultList();
    
    TestFunctionResult result = new TestFunctionResult();
    if (!results.isEmpty()) {
        Object[] row = results.get(0);
        result.setId((String) row[0]);
        result.setCode((Integer) row[1]);
    }
    return result;
}

方案2:配置Hibernate兼容函数调用

通过全局配置或查询级配置,让StoredProcedureQuery自动用SELECT调用PostgreSQL函数:

全局配置(推荐)

在application.properties或persistence.xml中添加:

hibernate.dialect.postgresql.use_select_for_function_call=true

添加后无需修改原有调用代码,Hibernate会自动适配语法。

查询级配置

如果不想修改全局配置,可在单个查询中设置属性:

public TestFunctionResult callTestFunction(String id) {
    StoredProcedureQuery query = entityManager
            .createStoredProcedureQuery("test_function")
            .registerStoredProcedureParameter("id", String.class, ParameterMode.IN)
            .registerStoredProcedureParameter("res_id", String.class, ParameterMode.OUT)
            .registerStoredProcedureParameter("res_code", Integer.class, ParameterMode.OUT)
            .setParameter("id", id);
    
    // 强制当前查询使用SELECT语法调用函数
    query.unwrap(org.hibernate.query.spi.StoredProcedureQueryImplementor.class)
         .getSession()
         .getJdbcServices()
         .getDialect()
         .unwrap(org.hibernate.dialect.PostgreSQLDialect.class)
         .setUseSelectForFunctionCall(true);

    TestFunctionResult testFunctionResult = new TestFunctionResult();
    try {
        query.execute();
        testFunctionResult.setId((String)query.getOutputParameterValue("res_id"));
        testFunctionResult.setCode((Integer) query.getOutputParameterValue("res_code"));
    } finally {
        query.unwrap(ProcedureOutputs.class).release();
    }
    return testFunctionResult;
}

方案3:将数据库对象改为PROCEDURE

如果允许修改数据库对象,可将原FUNCTION改为PROCEDURE:

CREATE OR REPLACE PROCEDURE test_procedure(id varchar, OUT res_id varchar, OUT res_code int)
language plpgsql as
$$
begin
    res_id := id;
    res_code := 5;
end; $$

修改调用代码中的对象名为test_procedure即可正常使用StoredProcedureQuery。


内容的提问来源于stack exchange,提问作者Demid Demidov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 07:00:38