简体   繁体   English

如何使用 Hibernate (EntityManager) 或 JPA 调用 Oracle 函数或过程

[英]How to call Oracle Function or Procedure using Hibernate (EntityManager) or JPA

I have an Oracle function which return sys-refcursor and when I call this function using Hibernate, I am getting the following exception.我有一个返回 sys-refcursor 的 Oracle 函数,当我使用 Hibernate 调用此函数时,出现以下异常。

Hibernate: { ? = call my_function(?) }
 org.hibernate.exception.GenericJDBCException: could not execute query
javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not execute query
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1360)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1288)
    at org.hibernate.ejb.QueryImpl.getSingleResult(QueryImpl.java:313)

How can I resolve this?我该如何解决这个问题?

Oracle function甲骨文功能

create or replace 
FUNCTION my_function(p_val IN varchar2)
    RETURN SYS_REFCURSOR
  AS
    my_cursor SYS_REFCURSOR;
  BEGIN
    OPEN my_cursor FOR SELECT emp_name FROM employees
    WHERE lower(emp_name) like lower(p_val||'%');
    RETURN my_cursor;    
  END;

My Entity class我的实体类

@Entity
@javax.persistence.NamedNativeQuery(name = "getFunc", query = 
"{ ? = call my_function(:empName) }", 
 resultClass = Employee.class, hints = 
 { @javax.persistence.QueryHint(name = "org.hibernate.callable", value = "true") })
 @Table(name = "EMPLOYEES")

and in DAO在 DAO 中

    @Override
        public void findEmployees(QueryData data,
                String empName) {

        List query = (List) entityManager.createNamedQuery("getFunc")
                         .setParameter("empName", empName)
                         .getSingleResult();
                data.setResult(query);
}

Oracle function or a stored procedure can be called using EntityManager in the following manner.可以通过以下方式使用 EntityManager 调用 Oracle 函数或存储过程。

For Oracle Function对于 Oracle 函数

Create a function with sys_refcursor as return type创建一个以 sys_refcursor 作为返回类型的函数

CREATE OR REPLACE FUNCTION my_function
(p_val IN varchar2)
    RETURN SYS_REFCURSOR
  AS
    my_cursor SYS_REFCURSOR;
  BEGIN
    OPEN my_cursor FOR SELECT emp_name FROM employees
    WHERE lower(emp_name) like lower(p_val||'%');
    RETURN my_cursor;    
  END;

In Entity class, define function as在实体类中,将函数定义为

@javax.persistence.NamedNativeQuery(name = "getFunc", query = "{? =  call
my_function(:empName) }", resultClass = Employee.class, hints = {
@javax.persistence.QueryHint(name = "org.hibernate.callable", value = "true") })

For Oracle Stored Procedure对于 Oracle 存储过程

Create procedure with sys_refcursor as first OUT parameter使用sys_refcursor作为第一个 OUT 参数创建过程

CREATE OR REPLACE PROCEDURE myProcedure(p_cursor out sys_refcursor,
     p_val  in varchar2
)
 AS
BEGIN
     OPEN o_cursor FOR
          SELECT     emp_name 
             FROM     employees 
            WHERE     LOWER (emp_name) LIKE lower(p_val||'%');

In Entity class define procedure as在实体类中将过程定义为

@javax.persistence.NamedNativeQuery(name = "getProc", query = "{ call
my_procedure(?,:empName) }", resultClass = Employee.class, hints = {
@javax.persistence.QueryHint(name = "org.hibernate.callable", value = "true") })

and finally in DAO class call function or procedure as最后在 DAO 类中调用函数或过程作为

Query query = entityManager.createNamedQuery("getFunc"); // if procedure then getProc 
query.setParameter("empName","smith"); 
query.getResultList(); 

Thanks谢谢

For your function,对于您的功能,

create or replace 
FUNCTION my_function(p_val IN varchar2)
    RETURN SYS_REFCURSOR
  AS
    my_cursor SYS_REFCURSOR;
  BEGIN
    OPEN my_cursor FOR SELECT emp_name FROM employees
    WHERE lower(emp_name) like lower(p_val||'%');
    RETURN my_cursor;    
  END;

You can define the following NamedNativeQuery :您可以定义以下NamedNativeQuery

@NamedNativeQuery(
    name = "my_function",
    query = "{ ? = call my_function( ? ) }",
    callable = true,
    resultClass = String.class
)

And, you can call the query like this:而且,您可以像这样调用查询:

List<String> employeeNames = entityManager
    .createNamedQuery("my_function")
    .setParameter(1, 1L)
    .getResultList();

For a stored procedure:对于存储过程:

CREATE OR REPLACE 
PROCEDURE my_procedure(p_val IN VARCHAR2, 
    my_cursor OUT SYS_REFCURSOR,
) 
AS
BEGIN
    OPEN my_cursor FOR
    SELECT emp_name FROM employees
    WHERE lower(emp_name) like lower(p_val||'%');
END;

, you can use the following JPA 2.1 query: ,您可以使用以下 JPA 2.1 查询:

StoredProcedureQuery query = entityManager
    .createStoredProcedureQuery("my_procedure")
    .registerStoredProcedureParameter(1, String.class, 
         ParameterMode.IN)
    .registerStoredProcedureParameter(2, Class.class, 
         ParameterMode.REF_CURSOR)
    .setParameter(1, 1L);
 
query.execute();
 
List<Object[]> result = query.getResultList();

For Procedure:对于程序:

CREATE OR REPLACE PROCEDURE my_procedure(p_val IN VARCHAR2, 
  my_cursor OUT SYS_REFCURSOR) 
AS
BEGIN
  OPEN my_cursor FOR SELECT emp_name FROM employees 
      WHERE lower(emp_name) like lower(p_val||'%');
END;

Alternative Solution: Call procedure with sys_refcursor as OUT parameter without defining @NamedNativeQuery替代解决方案:使用 sys_refcursor 作为 OUT 参数调用过程而不定义 @NamedNativeQuery

StoredProcedureQuery query = entityManager.createStoredProcedureQuery("myProcedure");
    query.registerStoredProcedureParameter(1, void.class, ParameterMode.REF_CURSOR);
    query.registerStoredProcedureParameter(2, String.class, ParameterMode.IN);
    query.setParameter(2, "Umesh");
    List result = query.getResultList();

You seem to be confusing Oracle functions with Oracle stored procedures.您似乎将 Oracle 函数与 Oracle 存储过程混淆了。

Functions can be invoked from a select statement - user defined functions like yours act the same way as the built-in functions, like min() and max().可以从 select 语句中调用函数 - 像您这样的用户定义函数的行为方式与内置函数相同,例如 min() 和 max()。 They cannot be invoked by an external "call" like stored procedures can.它们不能像存储过程那样被外部“调用”调用。

See http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions231.htm#i1012049 for the definition of a function.有关函数的定义,请参见http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions231.htm#i1012049

You probably will need to re-write your function as a stored procedure.您可能需要将函数重写为存储过程。

JPA 2.1 early draft states that there will be support for stored procedures, according to Arun Gupta from Oracle.根据Oracle 的Arun Gupta 的说法, JPA 2.1 早期草案声明将支持存储过程。

Support for Stored Procedures: Added support for the invocation of predefined database functions and user-defined database functions to the Java Persistence query language.支持存储过程:添加了对 Java 持久性查询语言调用预定义数据库函数和用户定义数据库函数的支持。

There are different variants of EntityManager.createXXXStoredProcedureQuery methods that return a StoredProcedureQuery for executing a stored procedure. EntityManager.createXXXStoredProcedureQuery方法有不同的变体,它们返回一个 StoredProcedureQuery 以执行存储过程。 Just liked @NamedQuery , there is @NamedStoredProcedureQuery that specifies and names a stored procedure, its parameters, and its result type.就像@NamedQuery@NamedStoredProcedureQuery指定和命名存储过程、它的参数和它的结果类型。 This annotation can be specified on an entity or mapped superclass.可以在实体或映射的超类上指定此注释。 The name specified in the annotation is then used in EntityManager.createNamedStoredProcedureQuery .然后在EntityManager.createNamedStoredProcedureQuery使用注释中指定的名称。 The IN, OUT, and INOUT parameters can be set and used to retrieve values passed back from the procedure. IN、OUT 和 INOUT 参数可以设置并用于检索从过程传回的值。 For example:例如:

@Entity
@NamedStoredProcedureQuery(name="topGiftsStoredProcedure", procedureName="Top10Gifts")
public class Product {
 . . .
}

// In your client

StoredProcedreQuery query = EntityManager.createNamedStoredProcedureQuery("topGiftsStoredProcedure");
query.registerStoredProcedureParameter(1, String.class, ParameterMode.INOUT);
query.setParameter(1, "top10");
query.registerStoredProcedureParameter(2, Integer.class, ParameterMode.IN);
query.setParameter(2, 100);
// there are other setParameter methods for defining the temporal type of a parameter
. . .
query.execute();
String response = query.getOutputParameterValue(1);

As for when the spec is going to be finalized, or when Hibernate will support JPA 2.1, I can't say.至于规范何时最终确定,或者 Hibernate 何时将支持 JPA 2.1,我不能说。 But it might be worth keeping an eye out.但它可能值得关注。

The previous solution doesn´t seem to work with eclipselink.以前的解决方案似乎不适用于 eclipselink。 I got this running with native queries under JPA with我在 JPA 下使用本机查询运行了它

            List result = em.createNativeQuery("select YOURFUNCTION(?) from dual ")
                      .setParameter(1, ONEPARAMETER)
                      .getResultList();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM