it-swarm.com.de

So rufen Sie die Oracle-Funktion oder -Prozedur mit Hibernate (EntityManager) oder JPA 2 auf

Ich habe eine Oracle-Funktion, die sys-refcursor zurückgibt. Wenn ich diese Funktion mit Hibernate 4 aufrufen, erhalte ich die folgende Ausnahme.

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)

Wie kann ich das beheben?

Oracle-Funktion

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;

Meine Entity-Klasse

@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")

und in DAO  

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

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

Oracle-Funktionen oder eine gespeicherte Prozedur können auf folgende Weise mit EntityManager aufgerufen werden.

Für Oracle-Funktion

Erstellen Sie eine Funktion mit sys_refcursor als Rückgabetyp

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;

Definieren Sie in der Entitätsklasse die Funktion als

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

Für gespeicherte Oracle-Prozeduren

Erstellen Sie eine Prozedur mit sys_refcursor als ersten OUT-Parameter

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-Klasse definieren Sie die Prozedur als

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

und schließlich in DAO-Klassenaufruffunktion oder -Prozedur als

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

Vielen Dank

19
Jåcob

In diesem Artikel werden alle Kombinationen zum Aufrufen von gespeicherten Prozeduren und Funktionen von Oracle ausführlich erläutert.

Für Ihre Funktion

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;

Sie können folgende NamedNativeQuery definieren:

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

Und Sie können die Abfrage wie folgt aufrufen:

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

Für eine gespeicherte Prozedur:

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;

können Sie die folgende JPA 2.1-Abfrage verwenden:

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();
3
Vlad Mihalcea

Für Verfahren:

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 Lösung: Prozedur mit sys_refcursor als OUT-Parameter aufrufen, ohne @NamedNativeQuery zu definieren.

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

In einem frühen Entwurf der JPA 2.1 heißt es, dass gespeicherte Prozeduren unterstützt werden, nach Arun Gupta von Oracle.

Unterstützung für gespeicherte Prozeduren: Unterstützung für den Aufruf vordefinierter Datenbankfunktionen und benutzerdefinierter Datenbankfunktionen für die Java Persistence-Abfragesprache wurde hinzugefügt.

Es gibt verschiedene Varianten von EntityManager.createXXXStoredProcedureQuery-Methoden, die eine StoredProcedureQuery zum Ausführen einer gespeicherten Prozedur zurückgeben. Gerade @NamedQuery gefällt, @NamedStoredProcedureQuery gibt eine gespeicherte Prozedur, ihre Parameter und ihren Ergebnistyp an und benennt sie. Diese Annotation kann für eine Entität oder eine zugeordnete Oberklasse angegeben werden. Der in der Annotation angegebene Name wird dann in EntityManager.createNamedStoredProcedureQuery verwendet. Die Parameter IN, OUT und INOUT können festgelegt und zum Abrufen von Werten verwendet werden, die von der Prozedur zurückgegeben werden. Zum Beispiel:

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

Wann die Spezifikation abgeschlossen werden wird oder Hibernate JPA 2.1 unterstützt, kann ich nicht sagen. Aber es lohnt sich, ein Auge zu haben.

1
prunge

Die bisherige Lösung scheint mit Eclipselink nicht zu funktionieren. Ich habe dies mit nativen Abfragen unter JPA mit 

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

Sie scheinen Oracle-Funktionen mit gespeicherten Prozeduren von Oracle zu verwechseln.

Funktionen können über eine select-Anweisung aufgerufen werden - benutzerdefinierte Funktionen wie Ihre verhalten sich genauso wie die integrierten Funktionen, wie min () und max (). Sie können nicht von einem externen "Aufruf" wie gespeicherte Prozeduren aufgerufen werden.

Siehe http://docs.Oracle.com/cd/B19306_01/server.102/b14200/functions231.htm#i1012049 für die Definition einer Funktion.

Sie müssen wahrscheinlich Ihre Funktion als gespeicherte Prozedur neu schreiben.

0
GreyBeardedGeek