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

问题描述 投票:5回答:5

我正在使用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?

spring hibernate postgresql jpa
5个回答
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();
}

1
投票

因为您正在使用PostgreSQL,所以您可以像在SELECT中那样调用任何类型函数的存储过程(Oracle,否则,只允许执行声明为只读选择的函数)。

你可以使用EntityManager.createNativeQuery(SQL)

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


1
投票

我认为这是导致问题的RETURN VOID。有an article on this topic可能也会帮助你:

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();

1
投票

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

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

更多在docs


0
投票

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.