如何在Spring/Hibernate/JPA中正确调用PostgreSQL函数(存储过程)?

如何在Spring/Hibernate/JPA中正确调用PostgreSQL函数(存储过程)?,spring,hibernate,postgresql,jpa,Spring,Hibernate,Postgresql,Jpa,我正在使用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 postgr

我正在使用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;
@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();
}
RETURNS character varying AS
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;
如果我像这样在pgAdmin中运行此函数,它工作正常:

select spa.create_tenant('somename');
现在,我尝试从我的服务运行此函数,如下所示:

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;
@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();
}
RETURNS character varying AS
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;
如果我运行我的方法,我会得到以下错误:

javax.persistence.PersistenceException: org.hibernate.MappingException: No Dialect mapping for JDBC type: 1111
我猜这是因为函数中定义了返回类型void,所以我将返回类型更改为如下所示:

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;
@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();
}
RETURNS character varying AS
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;
如果我再次运行我的方法,则会出现以下异常:

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

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

因为您使用的是PostgreSQL,正如您已经编写的,您可以调用
SELECT中的函数类型的任何存储过程(否则,Oracle将只允许您在selects中执行声明为只读的函数)

您可以使用
EntityManager.createNativeQuery(SQL)


因为您使用的是Spring,所以可以使用
SimpleJdbcTemplate.query(SQL)
执行任何SQL语句。

在实体类中,定义一个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();
}
我认为这是问题的根源。因此,将
函数
定义更改如下:

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;
@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();
}
RETURNS character varying AS
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();

如果您也在使用spring数据,您可以在
@Repository
界面中定义这样一个过程

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

更多信息请参见。

如果您想保持简单,只需执行以下操作:

    em.createSQLQuery("SELECT * FROM spa.create_tenant(:t_name) ")
                          .setParameter("t_name", name)").list();
注意,我故意使用list()。出于某种原因。update()对我不起作用。

  • PostgreSQL
  • 休眠
  • Kotlin



您将如何调用JDBC CallableStatement?这就是JPA API的全部内容。如果Hibernate没有反映JDBC提供的内容,那么请尝试其他JPA提供程序,看看它如何处理该查询。该查询不会编译erry@AlexG,因为延迟响应,我已经编辑了实体代码建议,但我希望您已经找到了如何调用函数a的解决方案nd在控制器上获取结果?查看如何调用函数并在控制器上获取结果?查看