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 ?