不能用Hibernate调用PostgreSQL的11个存储过程
Posted
技术标签:
【中文标题】不能用Hibernate调用PostgreSQL的11个存储过程【英文标题】:Can't call PostgreSQL's 11 Stored Procedure with Hibernate 【发布时间】:2019-07-10 22:43:27 【问题描述】:PostgreSQL 11 现在支持存储过程,我正在尝试使用 Hibernate 5.3.7.Final 和 Postgresql 42.2.5 JDBC 驱动程序 调用一个。在 PostgreSQL 11 之前,我们有可以使用 JPA 的 @NamedStoredProcedure
调用的函数。但是,函数是用SELECT my_func();
执行的,新的存储过程必须用CALL my_procedure();
执行
我正在尝试执行以下简单的存储过程:
CREATE OR REPLACE PROCEDURE p_raise_wage_employee_older_than(operating_years
int, raise int)
AS $$
BEGIN
UPDATE employees
SET wage = wage + raise
WHERE EXTRACT(year FROM age(entrance_date)) >= operating_years;
END $$
LANGUAGE plpgsql;
JPA 注释如下所示:
@NamedStoredProcedureQuery(name = "raiseWage",
procedureName = "p_raise_wage_employee_older_than",
parameters =
@StoredProcedureParameter(name = "operating_years", type = Integer.class,
mode = ParameterMode.IN),
@StoredProcedureParameter(name = "raise", type = Integer.class,
mode = ParameterMode.IN)
)
我正在调用存储过程:
StoredProcedureQuery storedProcedureQuery = this.em.createNamedStoredProcedureQuery("raiseWage");
storedProcedureQuery.setParameter("operating_years", 20);
storedProcedureQuery.setParameter("raise", 1000);
storedProcedureQuery.execute();
我的日志如下所示:
Hibernate: call p_raise_wage_employee_older_than(?,?)
2019-02-17 11:07:41.290 WARN 11168 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 42809
2019-02-17 11:07:41.291 ERROR 11168 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: p_raise_wage_employee_older_than(integer, integer) is a procedure
Hinweis: To call a procedure, use CALL.
Position: 15
第一个 Hibernate 日志表明 Hibernate 使用 call
来执行存储过程,但我收到了一个 SQL 异常,指出未使用 CALL
。这是 Postgresql 11 之前的 Postgresql 方言中的错误吗?您可以将 FUNCTIONS
建模为 JPA 中的存储过程,因此使用了 SELECT
而不是 CALL
?
【问题讨论】:
你能使用一个基本的 JDBC CallableStatement 吗?建议您尝试一下,因为这可能是所有 JPA 提供程序都在使用的,所以它可以将问题隔离到不同之处 由于 pgJDBC 42.2.5 于 2018 年 8 月在 PostgreSQL 11(2018 年 10 月)之前发布,我怀疑其中是否支持CALL
语法。您在日志中看到的call ...
语法是特定于 JDBC 的语法,它应该由 JDBC 驱动程序转换为适当的语法。您可能想发布此here,因为我在那里没有看到任何相关问题。 -- 目前,你可以使用“旧”方式(即用RETURNS VOID
定义FUNCTION
s)。
【参考方案1】:
由于 pgJDBC 42.2.5 是在 PostgreSQL 11 版本(2018 年 10 月)之前(2018 年 8 月)发布的,我认为这目前是 PostgreSQL 本身的 JDBC 驱动程序中的一个问题。我在 GitHub 存储库中创建了一个issue。
对于解决方法,您可以将 STORED PROCEDURE
重写为 FUNCTION
并使用 @NamedStoredProcedureQuery
或直接与 JDBC CallableStatement
交互,例如:
Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost:5432/", "postgres", "postgres");
CallableStatement callableStatement = conn.prepareCall("call f_raise_wage_employee_older_than(?,?)");
callableStatement.setInt(1, 20);
callableStatement.setInt(2, 500);
callableStatement.executeUpdate();
或使用EntityManager
执行本机查询:
this.em.createNativeQuery("CALL p_raise_wage_employee_older_than(1, 20)");
我会在收到 pgJDBC 维护者的答复后立即更新此答案。
更新:
这个话题已经在 Postgres 邮件列表 (https://www.postgresql.org/message-id/4285.1537201440%40sss.pgh.pa.us) 中讨论过,目前还没有解决方案。唯一的方法是将原生 SQL 查询传递给数据库或将STORED PROCEDURE
重写为FUNCTION
【讨论】:
42.2.6 还是有这个问题,你得到答案了吗? 我还没有查看此版本的发行说明【参考方案2】:在 PostgreSQL 11 之后,PostgreSQL JDBC 驱动团队在 PostgreSQL 驱动版本 42.2.16 中引入了一个 ENUM 名称 EscapeSyntaxCallMode
。所以我们可以在创建数据库连接或创建 DataSource 对象时使用这个枚举。这个枚举有 3 种类型的值:
-
"
func
" - 当我们总是想调用函数时设置它。
"call
" - 当我们总是想调用过程时设置它。
"callIfNoReturn
" - 它检查调用函数/过程中的返回类型,如果返回类型存在,PostgreSQL 将其视为函数并将其作为函数方式调用。否则将其称为过程方式。所以在我的项目中,我使用了这个“callIfNoReturn
”,因为我希望 PostgreSQL 自动检测我是在调用函数还是过程。
我已经给出了详细的答案以及要遵循的正确步骤:
Postgresql 11: Stored Procedure call error - To call a procedure, use CALL, Java
【讨论】:
非常感谢您抽出宝贵时间来添加此问题的答案以上是关于不能用Hibernate调用PostgreSQL的11个存储过程的主要内容,如果未能解决你的问题,请参考以下文章
hibernate映射postgreSQL数据库中的表时,表名是大写的时候为啥hibernate不能映射实体
Hibernate 不使用 Spring boot 在 PostgreSql 中创建数据库
JOOQ快速上手(基于springboot 和 postgresql)