4 votes

Comment appeler les fonctions de base de données db2 avec des arguments littéraux en utilisant l'API de critères JPA?

Je possède une fonction de base de données DB2 déclarée comme suit :

CREATE FUNCTION MYDB.FN_ISNEWSCOVERAGE(NEWS_ID INTEGER, USERID VARCHAR(50))
RETURNS INTEGER
...

qui contient une logique SQL complexe et renvoie 1 ou 0 (pour indiquer vrai/faux).

J'essaie d'utiliser la fonction pour filtrer les résultats dans une requête construite en utilisant l'API de critères JPA (sur Spring 3/Hibernate), comme suit :

EntityManager em = ...
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(News.class);
Root news = cq.from(News.class);
Predicate criteria = cb.conjunction();
...
String userid = ...
criteria = cb.and( criteria, 
                   cb.equal( cb.function( "MYDB.FN_ISNEWSCOVERAGE", 
                                          Long.class, 
                                          news.get("id"), 
                                          cb.literal( userid ) ), 
                             1 )
                 );
...
TypedQuery tq = em.createQuery(cq);
List results = tq.getResultList();
...

Le problème est que lorsque la requête résultante est exécutée, db2 lance une erreur :

Caused by: com.ibm.db2.jcc.b.eo: DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610, SQLERRMC=null, DRIVER=3.53.95

qui est décrite ici. Il semble que le problème soit que le SQL généré contient un paramètre positionnel '?' pour l'un des arguments de la fonction MYDB.FN_ISNEWSCOVERAGE - voici la section pertinente de la chaîne de requête générée :

SELECT ... from MYDB.NEWS news0_ where 1=1 and news0_.ACTIVE=? and MYDB.FN_ISNEWSCOVERAGE(news0_.NEWS_ID, ?)=1

Si je copie la requête générée dans un client SQL et remplace les paramètres positionnels par des valeurs littérales, la requête s'exécute bien.

Y a-t-il un moyen d'éviter cette erreur lors de l'appel de la fonction de base de données à partir de l'API de critères JPA ?

Mise à jour

J'ai réalisé que je peux contourner le problème en utilisant une sous-requête pour remplacer l'argument de fonction littérale par une expression de chemin, comme suit :

Subquery sq = cq.subquery(News.class);
Root sqNews = sq.correlate(news);
Root sqUser = sq.from(User.class);
sq.select(news)
  .where(
     cb.equal(sqUser.get("id"), userid), 
     cb.equal(cb.function("MYDB.FN_ISNEWSCOVERAGE", 
                          Long.class, 
                          sqNews.get("id"), 
                          sqUser.get("id")),
              1)
  );
criteria = cb.and(cb.exists(sq));

Cependant, ce contournement pourrait ne pas être applicable dans d'autres cas - y a-t-il une meilleure solution ?

0voto

Ignasi Points 669

Même si c'est une vieille question, nous sommes tombés sur le même problème aujourd'hui.

Puisque le problème concerne les arguments '?', nous avons opté pour forcer Hibernate à intégrer littéralement.

properties.put(AvailableSettings.CRITERIA_LITERAL_HANDLING_MODE, LiteralHandlingMode.INLINE);

Pour plus d'explications sur la manière dont Hibernate gère les littéraux : https://vladmihalcea.com/how-does-hibernate-handle-jpa-criteria-api-literals/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X