【问题标题】:How to properly call PostgreSQL functions (stored procedures) within Spring/Hibernate/JPA?如何在 Spring/Hibernate/JPA 中正确调用 PostgreSQL 函数(存储过程)?
【发布时间】:2014-11-25 04:44:27
【问题描述】:

我正在使用 Spring MVC 4、Hibernate 和 PostgreSQL 9.3,并在 Postgres 中定义了这样的函数(存储过程):

CREATE OR REPLACE FUNCTION spa.create_tenant(t_name character varying)
  RETURNS void AS
  $BODY$
    BEGIN
      EXECUTE format('CREATE SCHEMA IF NOT EXISTS %I AUTHORIZATION postgres', t_name);
    END
  $BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION spa.create_tenant(character varying)
OWNER TO postgres;

如果我像这样在 pgAdmin 中运行这个函数,它工作正常:

select spa.create_tenant('somename');

现在我正在尝试从我的服务中运行此功能,如下所示:

@Override
@Transactional
public void createSchema(String name) {
    StoredProcedureQuery sp = em.createStoredProcedureQuery("spa.create_tenant");
    sp.registerStoredProcedureParameter("t_name", String.class, ParameterMode.IN);
    sp.setParameter("t_name", name);
    sp.execute();
}

如果我运行我的方法,我会收到以下错误:

javax.persistence.PersistenceException: org.hibernate.MappingException: No Dialect mapping for JDBC type: 1111

我猜这是因为函数中定义的返回类型 void 所以我将返回类型更改为如下所示:

RETURNS character varying AS

如果我再次运行我的方法,我会得到这个异常:

javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Error calling CallableStatement.getMoreResults

有谁知道这里发生了什么以及如何在 PostgreSQL 中正确调用存储过程,即使返回类型为 void?

【问题讨论】:

  • 如何调用 JDBC CallableStatement?这就是 JPA API 的全部内容。如果 Hibernate 没有反映 JDBC 提供的内容,请尝试使用不同的 JPA 提供程序,看看它是如何处理它的

标签: spring hibernate postgresql jpa


【解决方案1】:

由于您使用的是 PostgreSQL,您可以像您已经编写的那样,在 @ 中调用 function 类型的 any 存储过程987654322@(否则,Oracle 将只允许您执行声明为在选择中只读的函数)。

您可以使用EntityManager.createNativeQuery(SQL)

由于您使用的是 Spring,因此您也可以使用 SimpleJdbcTemplate.query(SQL) 来执行任何 SQL 语句。

【讨论】:

【解决方案2】:

在您的实体类中,定义一个 NamedNativeQuery,就像您使用 select 调用 postgresql 函数一样。

import javax.persistence.NamedNativeQueries;
import javax.persistence.NamedNativeQuery;
import javax.persistence.Entity;
@NamedNativeQueries(
    value={
            // cast is used for Hibernate, to prevent No Dialect mapping for JDBC type: 1111
            @NamedNativeQuery(
                  name = "Tenant.createTenant",
                 query = "select cast(create_tenant(?) as text)"
            )
     }
)
@Entity
public class Tenant

hibernate 无法映射 void,因此解决方法是将结果转换为文本

public void createSchema(String name) {
    Query query = em.createNamedQuery("Tenant.createTenant")
            .setParameter(1, name);
    query.getSingleResult();
}

【讨论】:

  • 此查询无法编译
  • 抱歉@AlexG 回复晚了,我已经编辑了实体代码建议,但我希望你已经找到了解决方案
【解决方案3】:

我认为是RETURN VOID 导致了问题。所以,像这样更改FUNCTION 的定义:

CREATE OR REPLACE FUNCTION spa.create_tenant(t_name character varying)
  RETURNS bigint AS
  $BODY$
    BEGIN
      EXECUTE format('CREATE SCHEMA IF NOT EXISTS %I AUTHORIZATION postgres', t_name);
      RETURN 1;
    END
  $BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION spa.create_tenant(character varying)
OWNER TO postgres;

在您更改函数以返回一些虚拟值后,将存储过程查询更改为:

StoredProcedureQuery query = entityManager
    .createStoredProcedureQuery("spa.create_tenant")
    .registerStoredProcedureParameter(1, 
        Long.class, ParameterMode.OUT)
    .registerStoredProcedureParameter(2, 
        String.class, ParameterMode.IN)
    .setParameter(2, name);
 
query.getResultList();

【讨论】:

【解决方案4】:

如果您还使用弹簧数据,您可以像这样在@Repository 接口中定义一个过程,

@Procedure(value = "spa.create_tenant")
public void createTenantOrSomething(@Param("t_name") String tNameOrSomething);

更多内容请关注docs

【讨论】:

    【解决方案5】:

    如果你想保持简单,只需这样做:

        em.createSQLQuery("SELECT * FROM spa.create_tenant(:t_name) ")
                              .setParameter("t_name", name)").list();
    

    注意我故意使用 list().. 由于某种原因 .update() 对我不起作用。

    【讨论】:

      【解决方案6】:
      • PostgreSQL
      • 休眠
      • Kotlin

      CREATE OR REPLACE FUNCTION your_procedure() RETURNS text AS $$
      BEGIN
          RETURN 'Some text';
      END;
      $$ LANGUAGE plpgsql;
      

      val query = session.createNativeQuery("SELECT your_procedure()")
      query.list().map {
          println("NativeQuery: $it")
      }
      

      【讨论】:

        猜你喜欢
        • 2021-09-02
        • 2017-07-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-09
        • 1970-01-01
        相关资源
        最近更新 更多