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
相关产品推荐
相关产品推荐

