createQuery()
de l'
entity manager
. Cette approche est parfaitement légale, mais elle pose un problème, et comporte un piège.
Le problème est que la traduction de la requête JPQL en SQL se fait au moment de la création de l'objet
Query
, et à chaque instanciation de cet objet. Si la requête est créée dans une servlet, ou dans un EJB, le coût de traitement devra être payé à chaque fois que l'utilisateur fait appel à cette servlet ou à cet EJB.
Le piège se trouve dans la façon dont on veut prendre en compte les paramètres de la requête. Supposons que l'on ait la méthode suivante.
Exemple 32. Une (mauvaise) requête dynamique paramétrée
public List<Marin> getMarinByName(String name) { Query query = em.createQuery("select marin from Marin marin " + "where marin.nom = '" + name + "'") ; List<Marin> marins = query.getResultList() ; return marins ; }
Surcouf' and prenom = 'RobertEt dans ce cas, elle effectue une tout autre requête. Cette attaque, connue sous le nom d'injection de code est très classique, et doit être absolument évitée. Pour cela, JPQL expose une fonctionnalité analogue aux
preparedStatement
de JDBC.
?1
,
?2
, etc... Dans le second cas, ils sont nommés :
:nom
,
:adresse
, etc...
Exemple 33. Paramètrage d'une requête, première méthode
Query query = em.createQuery("select marin from Marin marin " + "where marin.nom = ?1 and marin.prenom = ?2") ; query.setParameter(1, "Surcouf") ; query.setParameter(2, "Robert") ; List<Marin> marins = query.getResultList() ;
Exemple 34. Paramètrage d'une requête, deuxième méthode
Query query = em.createQuery("select marin from Marin marin " + "where marin.nom = :nom and marin.prenom = :prenom") ; query.setParameter("nom", "Surcouf") ; query.setParameter("prenom", "Robert") ; List<Marin> marins = query.getResultList() ;
setParameter()
.
java.util.Date
et
java.util.Calendar
posent problème en tant que paramètre. De sorte qu'ils soient correctement convertis, il faut ajouter un paramètre à l'appel à
setParameter()
, qui porte le type temporel dans lequel la conversion doit se faire.
Exemple 35. Paramètrage d'une requête, cas des dates
Query query = em.createQuery("select marin from Marin marin " + "where marin.dateDeNaissance = :ddn") ; query.setParameter("ddn", dateDeNaissance, TemporalType.DATE) ; List<Marin> marins = query.getResultList() ;
@NamedQuery
. Cette annotation prend deux attributs :
name
, qui porte le nom de la requête, et
query
, qui porte la requête JPQL. Le nom de la requête doit être unique pour une même unité de persistance. Sans que cela soit une obligation, il est donc pratique de le préfixer par le nom d'une entité.
Plusieurs annotations
@NamedQuery
peuvent être regroupées dans une annotation
@NamedQueries
, qui prend un tableau de
@NamedQuery
en attribut. Ces deux annotations doivent être posées sur la classe d'une entité JPA.
Exemple 36. Déclaration de requêtes nommées
@NamedQueries({ @NamedQuery( name="Marin.findAll", query="select marin from Marin marin"), @NamedQuery( name="Marin.findByName", query="select marin from Marin marin where marin.name = :name"), @NamedQuery( name="Marin.countAll", query="select count(marin) from Marin marin"), }) @Entity(name="Marin") public class Marin implements Serializable { // reste de la classe }
Marin
. Sans cela, il aurait fallu utiliser le nom complet de la classe
Marin
dans nos requêtes JPQL.
Les annotations
@NamedQueries
doivent être posées sur la classe d'une entité. Rien ne dit que ces requêtes doivent nécessairement retourner des instances ou collection d'instances de ces entités. Il est simplement pratique, lisible et habituel de mettre sur une classe les requêtes qui s'y rapportent. On notera d'ailleurs que la dernière de nos requête retournent un entier.
Enfin, et afin de garantir l'unicité du nommage de nos requêtes au niveau de l'unité de persistance, nous avons préfixé le nom de ces requêtes par le nom de l'entité sur laquelle elles sont définies. Il ne s'agit pas d'une obligation, mais d'une bonne façon de faire.
createNativeQuery()
de l'
entity manager
. Cette méthode prend une chaîne de caractères en paramètre : le code SQL natif que l'on veut exécuter.
@NamedNativeQueries
et
@NamedNativeQuery
.