Plan de la partie 2 (1/2) JPA 2.0 (Java PersistenceAPI) (cid:137)Récupérer des données de la base (cid:137)Langage d’interrogation JPQL Java SE (cid:137)API «Critère» (cid:137)Modifications en volume - Partie 2 (cid:137)Exceptions Université de Nice -Sophia Antipolis (cid:137)Transaction Version 2.17.3 –2/1/13 (cid:137)Concurrence Richard Grin (cid:137)Entité détachée (cid:137)Configuration d’une unité de persistance R. Grin JPA page 2 Plan de la partie 2 (2/2) (cid:137)Fichiers de configuration XML (cid:137)JPA et DAOs (cid:137)Cache des données Récupérer des données (cid:137)Optimisation de la base de données (cid:137)Méthodes callbacks et écouteurs (cid:137)Validation (cid:137)Adaptation à une base de données préexistante (cid:137)Précisions pour la génération du schéma de la base de données R. Grin JPA page 3 R. Grin JPA page 4 Rappel important (cid:137)Toutes les entités retrouvées par find, (cid:137)Cette section montre comment interroger la getReferenceou un querysont base de données pour récupérer des entités automatiquement gérées par le gestionnaire (des employés par exemple) ou des simples d’entités valeurs (des noms d’employés ou le total des (cid:137)Les modifications apportées à ces entités sont salaires des employés par exemple) donc enregistrées au prochain commit (mais les nouveaux objets associés à l’entité retrouvée ne sont pas automatiquement persistants même s'il y a une cascade sur persist; ça ne marche que pour un appel explicite de la méthode persist) R. Grin JPA page 5 R. Grin JPA page 6 1 Chercher par identité Exemple (cid:137)findet getReference(de EntityManager) Departement dept = permettent de retrouver une entité en donnant em.find(Departement.class, 10); son identificateur dans la BD (cid:137)<T> T find(Class<T> classeEntité, Object cléPrimaire) (cid:137)<T> T getReference(Class<T> classeEntité, Object cléPrimaire) (cid:137)Le résultat est nullsi aucune entité n’a cet identificateur dans la base de donnée R. Grin JPA page 7 R. Grin JPA page 8 getReference getReference (cid:137)getReferencerenvoie une référence vers une (cid:137)getReferencepeut être (rarement) utilisée entité, sans que cette entité ne soit pour améliorer les performances quand une nécessairement initialisée entité non initialisée peut être suffisante, sans (cid:137)Le plus souvent il vaut mieux utiliser find que l’entité entière soit retrouvée dans la base de données (cid:137)Par exemple, pour indiquer une association dont le but est unique (OneToOne ou ManyToOne) : Departement dept = em.getReference(Departement.class, 10); Employe emp.setDepartement(dept); R. Grin JPA page 9 R. Grin JPA page 10 Interroger la base de données Étapes pour récupérer des données (cid:137) Il est possible de rechercher des données sur 1. Décrire ce qui est recherché (langage JPQL des critères plus complexes que la simple ou criteriaAPI) identité 2. Créer une instance de type Query (cid:137) Remarque : dans toute la partie à venir sur 3. Initialiser la requête (paramètres, pagination) les requêtes (query, JPQL, API criteria), 4. Lancer l’exécution de la requête «entité» peut désigner une entité ou une classe (ou instance de) Embeddable; depuis JPA 2.0, les classes Embeddableont les «mêmes droits» que les classes entités R. Grin JPA page 11 R. Grin JPA page 12 2 Exemple Exemple plus complexe String q = "select e from Employe as e " String s = "select e from Employe as e"; + "where e.departement.numero = :numero"; Query query = em.createQuery(s); Query query = em.createQuery(q); List<Employe> listeEmployes = (List<Employe>)query.getResultList(); query.setParameter("numero", 10); query.setMaxResults(30); for (int i = 0; i < 5; i++) { query.setFirstResult(30 * i); List<Employe> listeEmployes = (List<Employe>)query.getResultList(); ... // Affichage page numéro i + 1 } R. Grin JPA page 13 R. Grin JPA page 14 Description de la requête Requêtes sur les entités « objet » (cid:137)Le langage JPQL(Java PersistenceQuery (cid:137)Les requêtes JPQL ou «criteria» travaillent avec Language) permet de décrire ce que le modèle objet et pas avec le modèle relationnel l’application recherche (voir prochaine section) (cid:137)Les identificateurs désignent les entités et leurs (cid:137)Il ressemble beaucoup à SQL : attributs et pas les tables et leurs colonnes select e from Employe e (cid:137)Les seules classes qui peuvent être explicitement where e.nom = 'Dupond' désignées (par leur nom) dans une requête sont (cid:137)L’API «Criteria»a été introduite par JPA 2.0 les entités et les classes «Embeddable» pour décrire la requête d’une façon plus sûre, (cid:137)Rappel : le nom est donné par l’attribut @namede vérifiée à la compilation (étudiée à la suite de @Entityou @Embeddable, ou, par défaut, par JPQL) le nom de la classe R. Grin JPA page 15 R. Grin JPA page 16 Type du résultat Query (cid:137)L’expression renvoyée peut être (cid:137)Pour faire exécuter la requête il faut créer un nune (ou plusieurs) expression «entité», Query, instance qui représente une requête par exemple un employé (epar exemple) (étudiée en détails un peu plus loin) nune (ou plusieurs) expression «valeur», (cid:137)Plusieurs méthodes de EntityManager par exemple le nom d’un employé (e.nom), permettent d’obtenir un Query, suivant la y compris une expression arithmétique façon dont la requête a été décrite (e.salaire * 1.25) (cid:137)L’expression ne peut pas être une collection (d.employespar exemple), bien que certains fournisseurs de persistance le permettent ! R. Grin JPA page 17 R. Grin JPA page 18 3 Méthodes pour obtenir un Query Exécuter une requête (1) (cid:137)Les méthodes suivantes de EntityManager (cid:137)Plusieurs méthodes de l’interface Query renvoient un Query; elles sont liées au permettent d’exécuter une requête et de langage JPQL, à l’API «criteria», aux requêtes récupérer son résultat natives SQL et aux requêtes nommées (tout cela sera étudié dans la suite du cours) (cid:137)createQuery(String jpql) (cid:137)createQuery(CriteriaQuery criteria) (cid:137)createNativeQuery(String sql) (cid:137)createNamedQuery(String nom) R. Grin JPA page 19 R. Grin JPA page 20 Exécuter une requête (2) Exécuter une requête (3) (cid:137)Pour le cas où une seule valeur ou entité est (cid:137)Si plusieurs valeurs ou entités peuvent être renvoyée, le plus simple est d’utiliser la renvoyées, il faut utiliser la méthode méthode getSingleResult(); elle renvoie getResultList()de l’interface Query un Object (cid:137)Elle renvoie une liste «raw» (pas générique) (cid:137)La méthode lance des exceptions s’il n’y a des résultats, instance de java.util.List, pas exactement une entité qui correspond à éventuellement vide si le résultat est vide la requête : (cid:137)Un message d’avertissement est affiché nNoResultException durant la compilation si le résultat est rangé dans une liste générique (List<Employe> nNonUniqueResultException par exemple) R. Grin JPA page 21 R. Grin JPA page 22 Exemple 1 : renvoie une entité Type d’un élément du résultat String s = "select e from Employe as e"; (cid:137)Le type d’un élément de la liste (ou de Query query = em.createQuery(s); l’unique valeur ou entité renvoyée) est List<Employe> listeEmployes = (List<Employe>)query.getResultList(); nObjectsi la clause select ne comporte qu’une seule expression nObject[]si elle comporte plusieurs On peut faire un cast expressions R. Grin JPA page 23 R. Grin JPA page 24 4 Exemple 2 : renvoie une propriété Exemple 3 : renvoie plusieurs valeurs String s = texte = "select e.nom, e.salaire " "select e.nom from Employe as e"; + " from Employe as e"; Query query = em.createQuery(s); query = em.createQuery(texte); List<String> listeEmployes = List<Object[]> liste = (List<String>)query.getResultList(); (List<Object[]>)query.getResultList(); for (Object[] info : liste) { System.out.println(info[0] + " gagne " On peut faire un cast + info[1]); } R. Grin JPA page 25 R. Grin JPA page 26 Méthodes de Query (1) Méthodes de Query (2) (cid:137)List getResultList() (cid:137)Query setParameter(String nom, (cid:137)Object getSingleResult() Object valeur) (cid:137)int executeUpdate() (cid:137)Query setParameter(String nom, Date valeur, TemporalType (cid:137)Query setMaxResults(int typeTemporel) nbResultats) (cid:137)Query setParameter(String nom, (cid:137)Query setFirstResult(int Calendar valeur, TemporalType positionDepart) typeTemporel) (cid:137)Query setFlushMode(FlushModeType modeFlush) R. Grin JPA page 27 R. Grin JPA page 28 Types temporels Exemple (cid:137)On a vu que les 2 types java temporels du (cid:137) @Temporal(TemporalType.DATE) paquetage java.util(Dateet Calendar) private Calendar dateEmb; nécessitent une annotation @Temporal (cid:137) em.createQuery("select e from employe e" + " where e.dateEmb between ?1 and ?2") (cid:137)Ils nécessitent aussi un paramètre .setParameter(1, debut, TemporalType.DATE) supplémentaire pour la méthode .setParameter(2, fin, TemporalType.DATE) setParameter .getResultList(); R. Grin JPA page 29 R. Grin JPA page 30 5 Types de requête Paramètres des requêtes (cid:137)Requête dynamiquedont le texte JPQL est donné en paramètre de createQuery (cid:137)Un paramètre peut être désigné par son numéro (?n) ou par son nom (:nom) (cid:137)Requête native(ou requête SQL) particulière à un SGBD ou trop complexe pour JPA ; requête (cid:137)Les valeurs des paramètres sont données par les méthodes setParameter SQL (pas JPQL) avec tables et colonnes (pas classes et attributs) ; createNativeQuery (cid:137)Les paramètres sont numérotés à partir de 1 (cid:137)Requête nomméedont le texte est donné (cid:137)Un paramètre peut être utilisé plus d’une fois statiquement dans une annotation d’une entité dans une requête et dont le nom est passé en paramètre de (cid:137)L’usage des paramètres nommés est createNamedQuery; une requête nommée recommandé (plus lisible) peut être écrite en JPQL ou en SQL (native) R. Grin JPA page 31 R. Grin JPA page 32 Requête nommée (1) Requête nommée (2) (cid:137)Seules les entités peuvent contenir des (cid:137)Les requêtes nommées peuvent être définitions de requêtes nommées analysée et précompilées par le fournisseur (cid:137)Une requête nommée peut être mise dans de persistance au démarrage de l’application, n’importe quelle entité, mais on choisira le ce qui peut améliorer les performances plus souvent l’entité qui correspond à ce qui (cid:137)L’annotation @NamedQuerydéfinit une est renvoyé par la requête requête nommée écrite en JPQL (cid:137)Le nom de la requête nommée doit être (cid:137)L’annotation @NamedNativeQuerydéfinit unique parmi toutes les entités de l’unité de une requête nommée écrite en SQL (native) persistance ; on pourra, par exemple, préfixer le nom par le nom de l’entité : Employe.findAll R. Grin JPA page 33 R. Grin JPA page 34 Exemple de requête nommée Exemples @Entity (cid:137) Query query = em.createQuery( @NamedQuery ( "select e from Employe as e " name="findNomsEmployes", + "where e.nom = ?1"); query="select e.nom from Employe as e query.setParameter(1, "Dupond"); where upper(e.departement.nom) = :nomDept" (cid:137) Query query = em.createQuery( ) "select e from Employe as e " public class Employe extends Personne { + "where e.nom = :nom"); query.setParameter("nom", "Dupond"); ... Query q = em.createNamedQuery("findNomsEmployes"); R. Grin JPA page 35 R. Grin JPA page 36 6 Plusieurs requêtes nommées Mappingpour requêtes natives (1) (cid:137)Si une classe a plusieurs requêtes nommées, (cid:137)Il est nécessaire de donner des informations il faut les regrouper dans une annotation sur les colonnes renvoyées par la requête @NamedQueries: SQL afin que le gestionnaire d’entités sache @NamedQueries({ si elle correspondent à des entités @NamedQuery(...), (cid:137)Dans le cas où toutes les colonnes @NamedQuery(...), correspondent aux attributs d’une entité ces ... }) informations de mappingsont indiquées en donnant la classe de l’entité en paramètre de createNativeQuery R. Grin JPA page 37 R. Grin JPA page 38 Exemple Mappingpour requêtes natives (2) String requete = (cid:137)Dans les autres cas, une annotation "select matr, nome,…, dept " @SqlResultSetMappingest nécessaire SQL Oracle + " from emp " (connectby) (cid:137)Elle doit être ajoutée à une entité quelconque + " start with matr = ?1 " (cid:137)Cette annotation a un nom qui doit être passé + " connect by prior matr = sup "; en paramètre à la méthode List<Employe> employes = (List<Employe>) createNativeQuery em.createNativeQuery(requete, (cid:137)Consultez une documentation sur JPA pour Employe.class) en savoir plus .setParameter(1, idChef); .getResultList(); R. Grin JPA page 39 R. Grin JPA page 40 Mode de flush Requêtes natives ou JDBC ? (cid:137)Normalement (mode FlushMode.AUTO) un (cid:137)Si une entité est renvoyée par la requête, il flush des entités concernées par une requête vaut mieux utiliser une requête native car le est effectué avant la requête pour que le gestionnaire d’entités gérera l’entité comme résultat tienne compte des modifications les autres entités et le code est plus simple effectuées en mémoire sur ces entités (cid:137)Si des valeurs scalaires sont renvoyées, le (cid:137)Pour une requête il est possible d'éviter ce choix est moins clair ; utiliser une requête flush avec la méthode setFlushMode: nommée permettra de rester dans JPA query.setFlushMode(FlushMode.COMMIT); (cid:137)Rarement, une requête trop complexe Dans ce mode, seul un commit provoquera un imposera JDBC flush R. Grin JPA page 41 R. Grin JPA page 42 7 Pagination du résultat Enchaînement des méthodes (cid:137)Query setMaxResults(int n): indique (cid:137)Les méthodes setParameter, le nombre maximum de résultats à retrouver setMaxResultsrenvoient le Querymodifié (cid:137)Query setFirstResult(int n): indique (cid:137)On peut donc les enchaîner la position du 1errésultat à retrouver (cid:137)Exemple : (numéroté à partir de 0) em.createQuery(texteQuery) .setParameter(nomParam, valeurParam) .setFirstResult(30 * i) .setMaxResults(30) .getResultList(); R. Grin JPA page 43 R. Grin JPA page 44 Exemples de requêtes JPQL (cid:137) select e from Employe as e (cid:137) select e.nom, e.salaire from Employe e (cid:137) select e from Employe e Langage JPQL – where e.departement.nom = 'Direction' Java Persistence Query Language (cid:137) select d.nom, avg(e.salaire) from Departement d join d.employes e group by d.nom having count(d.nom) > 5 R. Grin JPA page 45 R. Grin JPA page 46 Alias Clauses d’un select (cid:137)Le texte des requêtes utilise les alias de (cid:137)select: type des objets ou valeurs classe : «select e from Employe as e» renvoyées (cid:137)Les attributs des classes doivent être préfixés (cid:137)from: où les données sont récupérées par les alias : «e.nom» (cid:137)where: sélectionne les données (cid:137)Une erreur fréquente du débutant est (cid:137)groupby: regroupe des données d’oublier les alias en préfixe (cid:137)having: sélectionne les groupes (ne peut exister sans clause group by) (cid:137)orderby: ordonne les données R. Grin JPA page 47 R. Grin JPA page 48 8 Polymorphisme dans les requêtes (1) (cid:137)Les mots-clés select, from, distinct, (cid:137)Toutes les requêtes sont polymorphes : un join,… sont insensibles à la casse nom de classe dans la clause fromdésigne cette classe et toutes les sous-classes (cid:137)Exemple : select count(a) from Article as a compte le nombre d’instances de la classe Articleet de tous les sous-classes de Article (cid:137)Depuis JPA 2 on peut restreindre à un type donné : select a fromArticle wheretype(a) R. Grin JPA page 49 R. Grin JPA page 50 Polymorphisme dans les requêtes (2) Expression de chemin (cid:137)Depuis JPA 2 on peut restreindre à un type (cid:137)Les requêtes peuvent contenir des donné : expressions de chemin pour naviguer entre select a from Article a les entités en suivant les associations where type(a) in (Stylo, Lot) déclarées dans le modèle objet (les annotations @OneToOne, @OneToMany, …) (cid:137)La notation «pointée» est utilisée R. Grin JPA page 51 R. Grin JPA page 52 Règle pour les Exemples expressions de chemin (cid:137)Si e est un alias pour Employe, n«e.departement» désigne le département d’un employé (cid:137)Une navigation peut être chaînée à une navigation précédente à la condition que la n«e.projets» désigne la collection de navigation précédente ne donne qu’une seule projets auxquels participe un employé entité (OneToOneou ManyToOne) (cid:137) select e.nom from Employe as e (cid:137)Dans le cas où une navigation aboutit à where e.departement.nom = 'Qualité' plusieurs entités, il est possible d’utiliser la clause joinétudiée plus loin pour obtenir (cid:137)e.projets.nomn’est pas autorisé car ces entités e.projetsest une collection (voir clause join) R. Grin JPA page 53 R. Grin JPA page 54 9 Autre exemple distinct select e.nom, (cid:137)Dans une clause select, indique que les e.departement.nom, valeurs dupliquées sont éliminées (la requête e.superieur.departement.nom ne garde qu’une seule des valeurs égales) from Employe e (cid:137)Exemple : select distinct e.departement from Employe e R. Grin JPA page 55 R. Grin JPA page 56 new Group by (cid:137)On peut regrouper sur un alias ou sur attribut ; (cid:137)Il est possible de renvoyer des instances d’une on regroupe ici sur un alias : classe dont le constructeur prend en paramètre select d, count(e) des informations récupérées dans la base de from Departement d données join d.employes e (cid:137)La classe doit être désignée par son nom group by d complet (avec le nom du paquetage) (cid:137)Les expressions du select (ou du orderby s’il (cid:137)Exemple : y en a un) qui ne sont pas des regroupements select new p1.p2.Classe(e.nom, e.salaire) doivent se trouver aussi dans le group by, ce from Employe e qui peut amener à ajouter une expression «inutile» dans le group by R. Grin JPA page 57 R. Grin JPA page 58 Clauses where et having Exemple (cid:137)Ces clauses peuvent comporter les mots-clés select d.nom, avg(e.salaire) suivants : from Departement d join d.employes e n[NOT]LIKE, [NOT]BETWEEN, [NOT]IN group by d.nom nAND, OR, NOT having count(d.nom) > 3 n[NOT]EXISTS nALL, SOME/ANY nIS [NOT] NULL nIS[NOT]EMPTY, [NOT]MEMBEROF(pour les collections) R. Grin JPA page 59 R. Grin JPA page 60 10
Description: