J'ai une fonction Oracle qui renvoie sys-refcursor et lorsque j'appelle cette fonction avec Hibernate 4, j'obtiens l'exception suivante.
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)
Comment puis-je résoudre ça?
Fonction Oracle
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;
Mon cours d'entité
@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")
et dans DAO
@Override
public void findEmployees(QueryData data,
String empName) {
List query = (List) entityManager.createNamedQuery("getFunc")
.setParameter("empName", empName)
.getSingleResult();
data.setResult(query);
}
La fonction Oracle ou une procédure stockée peut être appelée à l'aide de EntityManager de la manière suivante.
Pour la fonction Oracle
Créer une fonction avec sys_refcursor comme type de retour
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;
Dans la classe Entity, définissez la fonction comme
@javax.persistence.NamedNativeQuery(name = "getFunc", query = "{? = call
my_function(:empName) }", resultClass = Employee.class, hints = {
@javax.persistence.QueryHint(name = "org.hibernate.callable", value = "true") })
Pour la procédure stockée Oracle
Créer une procédure avec sys_refcursor
comme premier paramètre 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||'%');
Dans la classe Entity, définissez la procédure comme suit:
@javax.persistence.NamedNativeQuery(name = "getProc", query = "{ call
my_procedure(?,:empName) }", resultClass = Employee.class, hints = {
@javax.persistence.QueryHint(name = "org.hibernate.callable", value = "true") })
et enfin dans la fonction ou la procédure d'appel de classe DAO
Query query = entityManager.createNamedQuery("getFunc"); // if procedure then getProc
query.setParameter("empName","smith");
query.getResultList();
Merci
Cet article explique en détail toutes les combinaisons d'appels de procédures et de fonctions stockées Oracle.
Pour votre fonction,
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;
Vous pouvez définir la NamedNativeQuery
suivante:
@NamedNativeQuery(
name = "my_function",
query = "{ ? = call my_function( ? ) }",
callable = true,
resultClass = String.class
)
Et, vous pouvez appeler la requête comme ceci:
List<String> employeeNames = entityManager
.createNamedQuery("my_function")
.setParameter(1, 1L)
.getResultList();
Pour une procédure stockée:
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;
, vous pouvez utiliser la requête JPA 2.1 suivante:
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();
Pour la procédure:
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;
Solution alternative: Appelez la procédure avec sys_refcursor comme paramètre OUT sans définir @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();
Les premières versions de JPA 2.1 indiquent que les procédures stockées seront prises en charge, selon Arun Gupta d’Oracle.
Prise en charge des procédures stockées: prise en charge du langage de requête Java Persistence pour la création de fonctions de base de données prédéfinies et de fonctions de base de données définies par l'utilisateur.
Il existe différentes variantes des méthodes
EntityManager.createXXXStoredProcedureQuery
qui renvoient un StoredProcedureQuery pour l'exécution d'une procédure stockée. Juste aimé@NamedQuery
, il existe@NamedStoredProcedureQuery
qui spécifie et nomme une procédure stockée, ses paramètres et son type de résultat. Cette annotation peut être spécifiée sur une entité ou une super-classe mappée. Le nom spécifié dans l'annotation est ensuite utilisé dansEntityManager.createNamedStoredProcedureQuery
. Les paramètres IN, OUT et INOUT peuvent être définis et utilisés pour extraire les valeurs renvoyées de la procédure. Par exemple:
@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);
Je ne saurais dire quand les spécifications vont être finalisées ou quand Hibernate supportera JPA 2.1. Mais cela pourrait valoir la peine de garder un œil sur.
La solution précédente ne semble pas fonctionner avec eclipselink. Je l’ai exécuté avec des requêtes natives sous JPA avec
List result = em.createNativeQuery("select YOURFUNCTION(?) from dual ")
.setParameter(1, ONEPARAMETER)
.getResultList();
Vous semblez confondre les fonctions Oracle avec les procédures stockées Oracle.
Les fonctions peuvent être appelées à partir d'une instruction select. Les fonctions définies par l'utilisateur, comme le vôtre, agissent de la même manière que les fonctions intégrées, comme min () et max (). Ils ne peuvent pas être appelés par un "appel" externe, contrairement aux procédures stockées.
Voir http://docs.Oracle.com/cd/B19306_01/server.102/b14200/functions231.htm#i1012049 pour la définition d'une fonction.
Vous devrez probablement réécrire votre fonction en tant que procédure stockée.