无法使用Hibernate调用PostgreSQL的11存储过程

时间:2019-02-17 10:18:48

标签: java postgresql hibernate jpa spring-data

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

2 个答案:

答案 0 :(得分:3)

在 Postgresql 11 之后,PostgreSQL JDBC 驱动团队在 EscapeSyntaxCallMode 中引入了 ENUM 名称 Postgresql dirver version 42.2.16。所以这个枚举我们可以在创建数据库连接或创建数据源对象时使用。这个枚举有 3 种类型的值:

  1. "func" - 当我们总是想调用函数时设置它。
  2. "call" - 当我们总是想调用程序时设置它。
  3. "callIfNoReturn" - 它在调用函数/过程时检查返回类型,如果返回类型存在 postgres 将其视为函数并将其调用为函数方式。否则它称之为程序方式。所以在我的项目中,我使用了这个“callIfNoReturn”,因为我想让 postgres 自动检测我是在调用函数还是过程。

我已经详细给出了答案,并提供了正确的步骤:

https://github.com/benjaminp/six.git

答案 1 :(得分:0)

由于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