Plan de la partie 2 (1) JPA 2 -...
Transcript of Plan de la partie 2 (1) JPA 2 -...
1
JPA 2.0(Java Persistence API)
- Partie 2- Partie 2Université de Nice - Sophia Antipolis
Version 2.6 – 28/1/10Richard Grin
Plan de la partie 2 (1)
Récupérer des données de la baseLangage d’interrogation JPQLAPI « Critère »Modifications en volume
R. Grin JPA page 2
Modifications en volumeExceptionsTransactionConcurrenceEntités détachées
Plan de la partie 2 (2)
Configuration d’une unité de persistanceFichiers de configuration XMLJPA et DAOsCache des données
R. Grin JPA page 3
Cache des donnéesOptimisationMéthodes callbacks et écouteursGénération du schéma de la base de données
Récupérer des données de la base de données
R. Grin JPA page 4
de la base de données
Cette section montre comment interroger la base de données pour récupérer des entités (des employés par exemple) ou des simples valeurs (des noms d’employés ou le total des
R. Grin JPA page 5
salaires des employés par exemple)
Rappel importantToutes les entités retrouvées par find, getReference ou un query sont automatiquement gérées par le gestionnaire d’entitésLes modifications apportées à ces entités sont
R. Grin JPA page 6
Les modifications apportées à ces entités sont 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)
2
Chercher par identité
find et getReference (de EntityManager) permettent de retrouver une entité en donnant son identificateur dans la BD<T> T find(Class<T> classeEntité, Obj t léP i i )
R. Grin JPA page 7
Object cléPrimaire)<T> T getReference(Class<T> classeEntité, Object cléPrimaire)Le résultat est null si aucune entité n’a cet identificateur dans la base de donnée
Exemple
Departement dept =em.find(Departement.class, 10);
R. Grin JPA page 8
getReference
getReference renvoie une référence vers une entité, sans que cette entité ne soit nécessairement initialiséeLe plus souvent il vaut mieux utiliser find
R. Grin JPA page 9
getReference
getReference peut être (rarement) utilisée pour améliorer les performances quand une entité non initialisée peut être suffisante, sans que l’entité entière soit retrouvée dans la base d d é
R. Grin JPA page 10
de donnéesPar exemple, pour indiquer une association dont le but est unique (OneToOne ou ManyToOne) :Departement dept =em.getReference(Departement.class, 10);
Employe emp.setDepartement(dept);
Interroger la base de donnéesIl est possible de rechercher des données sur des critères plus complexes que la simple identitéRemarque : dans toute la partie à venir sur les requêtes (query JPQL API criteria)
R. Grin JPA page 11
les requêtes (query, JPQL, API criteria), « entité » peut désigner une entité ou une classe (ou instance de) Embeddable ; depuis JPA 2.0, les classes Embeddable ont les « mêmes droits » que les classes entités
Étapes pour récupérer des données1. Décrire ce qui est recherché (langage JPQL
ou criteria API)2. Créer une instance de type Query3. Initialiser la requête (paramètres, pagination)
R. Grin JPA page 12
4. Lancer l’exécution de la requête
3
Description de la requête
Le langage JPQL (Java Persistence QueryLanguage) permet de décrire ce que l’application recherche (voir prochaine section)Il ressemble beaucoup à SQL :
R. Grin JPA page 13
select e from Employe e where e.nom = 'Dupond'
L’API « Criteria » a été introduite par JPA 2.0 pour décrire la requête d’une façon plus sûre, vérifiée à la compilation (étudiée à la suite de JPQL)
Requêtes sur les entités « objet »
Les requêtes JPQL ou « criteria » travaillent avec le modèle objet et pas avec le modèle relationnelLes identificateurs désignent les entités et leurs attributs et pas les tables et leurs colonnes
R. Grin JPA page 14
Les seules classes qui peuvent être explicitement désignées (par leur nom) dans une requête sont les entités et les classes « Embeddable »Rappel : le nom est donné par l’attribut @name de @Entity ou @Embeddable, ou, par défaut, par le nom de la classe
Type du résultat
L’expression renvoyée peut être n une (ou plusieurs) expression « entité »,
par exemple un employé (e par exemple)n une (ou plusieurs) expression « valeur »,
R. Grin JPA page 15
par exemple le nom d’un employé (e.nom), y compris une expression arithmétique (e.salaire * 1.25)
L’expression ne peut pas être une collection (d.employes par exemple), bien que certains fournisseurs de persistance le permettent !
Query
Pour faire exécuter la requête il faut créer un Query, instance qui représente une requête (étudiée en détails un peu plus loin)Plusieurs méthodes de EntityManager
’permettent d’obtenir un Query, suivant la façon dont la requête a été décrite
R. Grin JPA page 16
Méthodes pour obtenir un Query
Les méthodes suivantes de EntityManagerrenvoient un Query ; elles sont liées au langage JPQL, à l’API « criteria », aux requêtes natives SQL et aux requêtes nommées (tout
l ét dié d l it d )cela sera étudié dans la suite du cours)createQuery(String jpql)createQuery(CriteriaQuery criteria)createNativeQuery(String sql)createNamedQuery(String nom)
R. Grin JPA page 17
Exécuter une requête (1)
Plusieurs méthodes de l’interface Querypermettent d’exécuter une requête et de récupérer son résultat
R. Grin JPA page 18
4
Exécuter une requête (2)
Pour le cas où une seule valeur ou entité est renvoyée, le plus simple est d’utiliser la méthode getSingleResult() ; elle renvoie un ObjectLa méthode lance des exceptions s’il n’y a
R. Grin JPA page 19
La méthode lance des exceptions s il n y a pas exactement une entité qui correspond à la requête :n EntityNotFoundExceptionn NonUniqueResultException
Exécuter une requête (3)
Si plusieurs valeurs ou entités peuvent être renvoyées, il faut utiliser la méthode getResultList() de l’interface QueryElle renvoie une liste « raw » (pas générique) des résultats instance de java util List
R. Grin JPA page 20
des résultats, instance de java.util.List, éventuellement vide si le résultat est videUn message d’avertissement est affiché durant la compilation si le résultat est rangé dans une liste générique (List<Employe>par exemple)
Type d’un élément du résultat
Le type d’un élément de la liste (ou de l’unique valeur ou entité renvoyée) estn Object si la clause select ne comporte
R. Grin JPA page 21
j pqu’une seule expression
n Object[] si elle comporte plusieurs expressions
Exemple 1 : renvoie une entité
String s = "select e from Employe as e"; Query query = em.createQuery(s); List<Employe> listeEmployes = (List<Employe>)query.getResultList();
R. Grin JPA page 22
On peut faire un cast
Exemple 2 : renvoie une propriété
String s = "select e.nom from Employe as e";
Query query = em.createQuery(s); List<String> listeEmployes = (List<String>)query getResultList();
R. Grin JPA page 23
(List<String>)query.getResultList();
On peut faire un cast
Exemple 3 : renvoie plusieurs valeurs
texte = "select e.nom, e.salaire " + " from Employe as e";
query = em.createQuery(texte);List<Object[]> liste = (List<Object[]>)query getResultList();
R. Grin JPA page 24
(List<Object[]>)query.getResultList();for (Object[] info : liste) {System.out.println(info[0] + " gagne "
+ info[1]);}
5
Méthodes de Query (1)
List getResultList()Object getSingleResult()int executeUpdate()Query setMaxResults(int
R. Grin JPA page 25
Q y (nbResultats)Query setFirstResult(intpositionDepart)Query setFlushMode(FlushModeTypemodeFlush)
Méthodes de Query (2)
Query setParameter(String nom, Object valeur)Query setParameter(String nom, Date valeur, TemporalType
R. Grin JPA page 26
typeTemporel)Query setParameter(String nom, Calendar valeur, TemporalType typeTemporel)
Types temporels
On a vu que les 2 types java temporels du paquetage java.util (Date et Calendar) nécessitent une annotation @TemporalIls nécessitent aussi un paramètre
R. Grin JPA page 27
supplémentaire pour la méthode setParameter
Exemple
@Temporal(TemporalType.DATE)private Calendar dateEmb;em.createQuery("select e from employe e"+ " where e.dateEmb between ?1 and ?2").setParameter(1, debut, TemporalType.DATE)
R. Grin JPA page 28
p yp.setParameter(2, fin, TemporalType.DATE).getResultList();
Types de requêteRequête dynamique dont le texte JPQL est donné en paramètre de createQueryRequête native (ou requête SQL) particulière à un SGBD ou trop complexe pour JPA ; requête SQL (pas JPQL) avec tables et colonnes (pas
R. Grin JPA page 29
SQL (pas JPQL) avec tables et colonnes (pas classes et attributs) ; createNativeQueryRequête nommée dont le texte est donné statiquement dans une annotation d’une entité et dont le nom est passé en paramètre de createNamedQuery ; une requête nommée peut être écrite en JPQL ou en SQL (native)
Paramètres des requêtes
Un paramètre peut être désigné par son numéro (?n) ou par son nom (:nom)Les valeurs des paramètres sont données par les méthodes setParameter
R. Grin JPA page 30
Les paramètres sont numérotés à partir de 1Un paramètre peut être utilisé plus d’une fois dans une requêteL’usage des paramètres nommés est recommandé (plus lisible)
6
Requête nommée (1)
Seules les entités peuvent contenir des définitions de requêtes nomméesUne requête nommée peut être mise dans n’importe quelle entité, mais on choisira le
R. Grin JPA page 31
plus souvent l’entité qui correspond à ce qui est renvoyé par la requêteLe nom de la requête nommée doit être unique parmi toutes les entités de l’unité de persistance ; on pourra, par exemple, préfixer le nom par le nom de l’entité : Employe.findAll
Requête nommée (2)
Les requêtes nommées peuvent être analysée et précompilées par le fournisseur de persistance au démarrage de l’application, ce qui peut améliorer les performances
’ éf
R. Grin JPA page 32
L’annotation @NamedQuery définit une requête nommée écrite en JPQLL’annotation @NamedNativeQuery définit une requête nommée écrite en SQL (native)
Exemple de requête nommée@Entity@NamedQuery (name="findNomsEmployes",query="select e.nom from Employe as e where upper(e.departement.nom) = :nomDept"
R. Grin JPA page 33
)public class Employe extends Personne {...
Query q = em.createNamedQuery("findNomsEmployes");
Exemples
Query query = em.createQuery("select e from Employe as e "+ "where e.nom = ?1");
query.setParameter(1, "Dupond");Query query = em.createQuery(
R. Grin JPA page 34
y q y y"select e from Employe as e "+ "where e.nom = :nom");
query.setParameter("nom", "Dupond");
Plusieurs requêtes nommées
Si une classe a plusieurs requêtes nommées, il faut les regrouper dans une annotation @NamedQueries :@NamedQueries({@NamedQuery( )
R. Grin JPA page 35
@NamedQuery(...),@NamedQuery(...),...
})
Mapping pour requêtes natives (1)
Il est nécessaire de donner des informations sur les colonnes renvoyées par la requête SQL afin que le gestionnaire d’entités sache si elle correspondent à des entitésDans le cas où toutes les colonnes correspondent aux attributs d’une entité ces informations de mapping sont indiquées en donnant la classe de l’entité en paramètre de createNativeQuery
R. Grin JPA page 36
7
Mapping pour requêtes natives (2)
Dans les autres cas, une annotation @SqlResultSetMapping est nécessaireElle doit être ajoutée à une entité quelconqueCette annotation a un nom qui doit être passé q pen paramètre à la méthode createNativeQuery
Consultez une documentation sur JPA pour en savoir plus
R. Grin JPA page 37
Requêtes natives ou JDBC ?
Si une entité est renvoyée par la requête, il vaut mieux utiliser une requête native car le gestionnaire d’entités gérera l’entité comme les autres entitésSi des valeurs scalaires sont renvoyées, le choix est moins clair ; utiliser une requête nommée permettra de rester dans JPAUne requête trop complexe nécessitera JDBC ; de même si le texte de la requête est généré lors de l’exécution
R. Grin JPA page 38
Mode de flushNormalement (mode FlushMode.AUTO) un flush des entités concernées par une requête est effectué avant la requête pour que le résultat tienne compte des modifications effectuées en mémoire sur ces entités
R. Grin JPA page 39
Pour une requête il est possible d'éviter ce flush avec la méthode setFlushMode :query.setFlushMode(FlushMode.COMMIT);Dans ce mode, seul un commit provoquera un flush
Pagination du résultat
Query setMaxResults(int n) : indique le nombre maximum de résultats à retrouverQuery setFirstResult(int n) : indique la position du 1er résultat à retrouver
R. Grin JPA page 40
(numéroté à partir de 0)
Enchaînement des méthodes
Les méthodes setParameter, setMaxResults renvoient le Query modifiéOn peut donc les enchaînerExemple :
R. Grin JPA page 41
pem.createQuery(texteQuery).setParameter(nomParam, valeurParam).setFirstResult(30 * i + 1).setMaxResults(30).getResultList();
Langage JPQL –J P i t Q L
R. Grin JPA page 42
Java Persistence Query Language
8
Exemples de requêtes JPQL
select e from Employe as eselect e.nom, e.salaire from Employe eselect e from Employe e where e.departement.nom = 'Direction'select d nom avg(e salaire)
R. Grin JPA page 43
select d.nom, avg(e.salaire) from Departement d join d.employes e group by d.nomhaving count(d.nom) > 5
Alias
Le texte des requêtes utilise les alias de classe : « select e from Employe as e »Les attributs des classes doivent être préfixés par les alias : « e.nom »
R. Grin JPA page 44
Une erreur fréquente du débutant est d’oublier les alias en préfixe
Clauses d’un select
select : type des objets ou valeurs renvoyéesfrom : où les données sont récupéréeswhere : sélectionne les données
R. Grin JPA page 45
group by : regroupe des donnéeshaving : sélectionne les groupes (ne peut exister sans clause group by)order by : ordonne les données
Les mots-clés select, from, distinct, join,… sont insensibles à la casse
R. Grin JPA page 46
Polymorphisme dans les requêtes (1)
Toutes les requêtes sont polymorphes : un nom de classe dans la clause from désigne cette classe et toutes les sous-classesExemple :
R. Grin JPA page 47
select count(a) from Article as acompte le nombre d’instances de la classe Article et de tous les sous-classes de Article
Depuis JPA 2 on peut restreindre à un type donné : select a from Article where type(a)
Polymorphisme dans les requêtes (2)
Depuis JPA 2 on peut restreindre à un type donné :select a from Article a where type(a) in (Stylo, Lot)
R. Grin JPA page 48
9
Expression de chemin
Les requêtes peuvent contenir des expressions de chemin pour naviguer entre les entités en suivant les associations déclarées dans le modèle objet (les annotations @O T O @O T M )
R. Grin JPA page 49
annotations @OneToOne, @OneToMany, …)La notation « pointée » est utilisée
Règle pour les expressions de chemin
Une navigation peut être chaînée à une navigation précédente à la condition que la navigation précédente ne donne qu’une seule
R. Grin JPA page 50
navigation précédente ne donne qu une seule entité (OneToOne ou ManyToOne)Dans le cas où une navigation aboutit à plusieurs entités, il est possible d’utiliser la clause join étudiée plus loin pour obtenir ces entités
ExemplesSi e est un alias pour Employe, n « e.departement » désigne le
département d’un employén « e.projets » désigne la collection de
projets auxquels participe un employé
R. Grin JPA page 51
p j q p p p yselect e.nomfrom Employe as ewhere e.departement.nom = 'Qualité'
e.projets.nom n’est pas autorisé car e.projets est une collection (voir clause join)
Autre exemple
select e.nom,e.departement.nom,e.superieur.departement.nom
from Employe e
R. Grin JPA page 52
distinct
Dans une clause select, indique que les valeurs dupliquées sont éliminées (la requête ne garde qu’une seule des valeurs égales)Exemple :
R. Grin JPA page 53
select distinct e.departementfrom Employe e
new
Il est possible de renvoyer des instances d’une classe dont le constructeur prend en paramètre des informations récupérées dans la base de données
R. Grin JPA page 54
La classe doit être désignée par son nom complet (avec le nom du paquetage)Exemple :select new p1.p2.Classe(e.nom, e.salaire)from Employe e
10
Clauses where et having
Ces clauses peuvent comporter les mots-clés suivants :n [NOT] LIKE, [NOT] BETWEEN, [NOT] INn AND, OR, NOT
R. Grin JPA page 55
n [NOT] EXISTSn ALL, SOME/ANYn IS [NOT] NULLn IS [NOT] EMPTY, [NOT] MEMBER OF (pour
les collections)
Exemple
select d.nom, avg(e.salaire)from Departement d join d.employes egroup by d.nomhaving count(d.nom) > 3
R. Grin JPA page 56
having
Restriction : la condition doit porter sur l’expression de regroupement ou sur une fonction de regroupement portant sur l’expression de regroupement
R. Grin JPA page 57
Par exemple, la requête suivante provoque une exception :select d.nom, avg(e.salaire)from Departement d join d.employes egroup by d.nomhaving avg(e.salaire) > 1000
Sous-requête (1)
Les clauses where et having peuvent contenir des sous-requêtesExemple :select e from Employe eh l i > (
R. Grin JPA page 58
where e.salaire >= (select e2.salaire from Employe e2where e2.departement = 10)
Sous-requête (2)
{ALL | ANY | SOME} (sous-requête) fonctionne comme dans SQLExemple :select e from Employe eh l i > ALL (
R. Grin JPA page 59
where e.salaire >= ALL (select e2.salaire from Employe e2where e2.departement =
e.departement)
Sous-requête synchronisée
Une sous-requête peut être synchronisée avec une requête englobanteExemple :select e from Employe eh l i > ALL (
R. Grin JPA page 60
where e.salaire >= ALL (select e2.salaire from Employe e2where e2.departement =
e.departement)
11
Sous-requête - exists
[not]exists fonctionne comme avec SQLExemple :select emp from Employe ewhere exists (select ee from Employe ee
R. Grin JPA page 61
select ee from Employe eewhere ee = e.epouse)
Utilité de la jointure
« d.employes.nom » est interdit car d.employes est une collectionPour avoir les noms des employés d’un département, il faut utiliser une jointure
R. Grin JPA page 62
Jointure
Une jointure permet de combiner plusieurs entités dans un selectRappel : il est aussi possible d’utiliser plusieurs entités dans une requête grâce à la
i ti
R. Grin JPA page 63
navigationUne jointure est le plus souvent utilisée pour résoudre les cas (interdit par JPA) oùn l’expression du select serait une collectionn la navigation partirait d’une collection
Types de jointures
Il existe plusieurs types de jointures :n jointure interne (jointure standard join)n jointure externe (outer join)n jointure avec récupération de données en
R. Grin JPA page 64
j pmémoire (join fetch)
n jointure « à la SQL » dans un where pour joindre suivant des champs qui ne correspondent pas à une association (where e1.f1 = e2.f2)
Exemple
Si on veut tous les employés d’un département, la requête suivante n’est pas permise par la spécification JPAUne jointure est nécessaire :
R. Grin JPA page 65
select efrom Departement d join d.employes ewhere d.nom = 'Direction'
Autres exemplesselect e.nomfrom Departement d
join d.employes ewhere d.nom = 'Direction'select e.nom, parts.projet.nomfrom Employe e
join e participations parts
R. Grin JPA page 66
join e.participations parts select e.nom, d.nomfrom Employe e, Departement dwhere d = e.departementselect e, pfrom Employe e join e.participations partsjoin parts.projet p
12
Jointure externe
select e, dfrom Employe e left join e.departement dramènera aussi les employés qui ne sont pas associés à un département
R. Grin JPA page 67
join fetch
Permet d’éviter le problème des « N + 1 selects »L’entité placé à droite de join fetch sera créée en mémoire en même temps que
R. Grin JPA page 68
l’entité de la clause selectLe select SQL généré sera une jointure externe qui récupérera les données de toutes les entités associées en même temps que les données des entités principales de la requête
Exemple
select efrom Employe e join fetch e.departement
Cette requête récupérera tous les employés mais, en plus, l’appel de la méthode getDepartement() ne provoquera aucune
R. Grin JPA page 69
getDepartement() ne provoquera aucune interrogation de la base de données puisque le « join fetch » aura déjà chargé tous les départements des employés
Exemple
String texteQuery ="select e "+ " from Employe as e " + " join fetch e.participations";
Q t Q (t t Q )
R. Grin JPA page 70
Query query = em.createQuery(texteQuery);listeEmployes = (List<Employe>)query.getResultList();
Cet exemple précharge les participations aux projets des employés
Doublons possibles avec join fetchLa requête SQL lancée par un join fetch fait une jointure pour récupérer les entités préchargéesEnsuite, les entités préchargées sont enlevées des lignes du résultat pour qu’elles n’apparaissent pas dans le résultat du queryC t it t i é l é ifi ti d
R. Grin JPA page 71
Ce traitement, imposé par la spécification de JPA, peut occasionner des doublons dans le résultat si le select renvoie des valeurs (pas des entités)Pour les éviter, il faut ajouter l’opérateur DISTINCT dans le texte de la requête ou placer le résultat dans une collection de type Set
Il existe aussi des variantes « outer join » de join fetch pour récupérer dans le select des entités non jointes à une autre entité
R. Grin JPA page 72
13
Produit cartésien (1)
Le préchargement par join fetch de plusieurscollections d’une même entité peut occasionner un phénomène nuisible aux performancesEn effet, le select SQL généré par le
R. Grin JPA page 73
fournisseur de persistance peut récupérer un produit cartésien des éléments des collectionsLe fournisseur s’arrange pour ne garder que les informations nécessaires mais le select peut renvoyer un très grand nombre de lignes qui vont transiter par le réseau
Produit cartésien (2)Dans le cas où les collections contiennent de nombreux éléments il faut donc vérifier avec les logs du fournisseur si le select généré renvoie effectivement un trop grand nombre de lignes et changer de stratégie de récupération si c’est le cas (récupérer séparément les informations
R. Grin JPA page 74
le cas (récupérer séparément les informations sur les collections)En effet, si 2 collections contiennent 20 éléments, et si 1000 entités principales sont renvoyées le select renverra 400.000 lignes ! (au lieu de 40.000 si on ramène les informations avec 2 join fetch séparés)
ExempleUne entité principale ep avec 2 associations de l’entité principale avec d’autres entités e2 et e3Pour récupérer en mémoire les entités principales et associées, le select généré risque d’être du type suivant (consulter les logs
R. Grin JPA page 75
q yp ( gdu fournisseur de persistance) :select ep.*, e2.*, e3.*from EP epleft outer join E2 e2 on ep.id_e2 =
e2.idleft outer join E3 e3 on ep.id_e3 =
e3.id
FonctionsPour les chaînes de caractères : concat, substring, trim, lower, upper, length, locate (localiser une sous-chaîne dans une autre)Arithmétiques : abs, sqrt, mod, size (d’une
ll ti )
R. Grin JPA page 76
collection)Pour les date : current_date, current_time, current_timestampDe regroupement : count, max, min, avg« Conditionnelles » : case, coalesce, nullifqui sont les pendants des fonctions SQL
caseJPA 2.0 a introduit le case de SQL (peut être utilisé dans une clause where ou having ou même dans un update (voir section suivante)Variante « simple » :CASE valeurWHEN valeur1 THEN résultat1WHEN valeur1 THEN résultat1WHEN valeur2 THEN résultat2…ELSE résultatN
ENDvaleur peut être un attribut ou une expression de type
R. Grin JPA page 77
case
Variante « généraleCASEWHEN condition1 THEN résultat1WHEN condition2 THEN résultat2…ELSE résultatN
END
R. Grin JPA page 78
14
Exemples de caseselect a.description,case type(a)when Stylo then 'stylo'when Ramette then 'ramette'else 'autre type'
endendfrom Article aupdate Employe eset e.salaire = case e.indicewhen 1 then e.salaire * 1.1else e.salaire * 1.2
endR. Grin JPA page 79
coalesce
coalesce(expression1, expression2,...)renvoie la valeur de la 1ère expression non égale à nullExemple : coalesce(commission, salaire, 0)renvoie la valeur de la commission si elle n’est pas null, sinon renvoie la valeur s’il n’est pas null, et renvoie 0 sinon
R. Grin JPA page 80
nullif
nullif(expression1, expression2) renvoie null si les 2 expressions sont égales, sinon renvoie la valeur de expression1Peut servir pour une ancienne base de données dans laquelle les valeurs « null » sont en fait représentées par une certaine valeurExemple : nullif(salaire, -1)
R. Grin JPA page 81
Fonction index
JPA 2.0 permet de conserver l’ordre d’une liste grâce à une colonne de la BDLa fonction index retourne la valeur de cette colonne (0 pour le 1er élément) Exemple :select e.nom, t.numerofrom Employe e join e.telephones twhere index(t) = 0affiche les noms des employés avec leur numéro de téléphone classé 1er dans la liste de leurs numéro de téléphone
R. Grin JPA page 82
Travail avec les collectionsUne expression chemin d’un select peut désigner une collectionExemples : departement.employesfacture.lignes
R. Grin JPA page 83
facture.lignesLa fonction size donne la taille de la collectionLa condition « is [not] empty » est vraie si la collection est [n’est pas] videLa condition « [not] member of » indique si une entité appartient à une collection
Exemples
select dfrom Departementwhere e.employes is emptyselect efrom Employe e
R. Grin JPA page 84
p ywhere :projet member of e.participations.projet
15
Parcours d’une collection
La requête suivante ne donnera pas tous les produits liés à une facture ; elle provoquera une exception à l’exécutionselect distinct f.lignes.produitfrom Facture as f
R. Grin JPA page 85
from Facture as f
En effet, il est interdit de composer une expression de chemin en partant d’une expression qui désigne une collection (f.lignes)
Parcours d’une collection
Le plus simple est d’utiliser un join dans le from avec un alias (« l » dans l’exemple ci-dessous) pour désigner un élément qui parcourt la collection :select distinct l produit
R. Grin JPA page 86
select distinct l.produitfrom Facture as f join f.lignes as l
L’autre solution, « in », une ancienne syntaxe héritée d’EJB 2, n’est pas recommandée
API « critères »
R. Grin JPA page 87
IntroductionAPI introduite par JPA 2.0Tout ce qui peut être fait avec JPQL peut l’être avec l’API « critères »Les requêtes JPQL sont des String qui
t t i d ( l lpeuvent contenir des erreurs (par exemple le nom d’une classe qui n’existe pas)L’avantage de l’API « critères » est que les requêtes peuvent être vérifiées à la compilationL’inconvénient est que le code est un peu plus complexe à écrire, et sans doute moins lisible
R. Grin JPA page 88
API complexe
L’API contient de nombreuses interfaces et ne peut être détaillée dans ce coursNous n’en donnerons qu’un aperçu avec un exemple simple qui est l’équivalent de l’ordre JPQL suivant :select efrom Employe ewhere e.nom = 'Dupond'
R. Grin JPA page 89
Sans le meta-modèle
CriteriaBuilder cb = em.getCriteriaBuilder();CriteriaQuery<Employe> criteria =
cb.createQuery(Employe.class);Root<Employe> emp = criteria.from(Employe.class);c.select(emp).
h ( b l( t(" ") "D d"))where(cb.equal(emp.get("nom"), "Dupond"));
R. Grin JPA page 90
16
Meta-modèle
On voit que des String (get("nom")) sont utilisées pour désigner les attributsDes erreurs ne pourront être détectées qu’à l’exécutionLe meta-modèle va permettre de sécuriser les requêtes en les faisant vérifier par le compilateurUne classe du meta-modèle décrit une classe persistante ; le compilateur peut l’utiliser pour vérifier une requête
R. Grin JPA page 91
Exemple de classe du meta-modèle
@StaticModel(Employe.class)public class Employe_ {
public static volatile SingularAttributes<Employe, Integer> id;
public static volatile SingularAttributes<Employe, String> nom;
public static volatile SingularAttributes<Employe, Departement> dept;
public static volatile CollectionAttributes<Employe, Projet> projets;
// D’autres « public static volatile ... »...
}R. Grin JPA page 92
Avec le meta-modèle
CriteriaBuilder cb = em.getCriteriaBuilder();CriteriaQuery<Employe> criteria =
cb.createQuery(Employe.class);Root<Employe> emp = criteria.from(Employe.class);c.select(emp).
h ( b l( t(E l ) "D d"))where(cb.equal(emp.get(Employe_.nom), "Dupond"));
R. Grin JPA page 93
Exemple plus complexeQueryBuilder qb = em.getQueryBuilder();Criteria q = qb.create();Root<Client> client = q.from(Client.class);Join<Facture,LigneFacture> ligne =
client.join(Client_.factures).join(Facture_.lignesFacture);
q.where(qb.equals(q (q q (ligne.get(ligne_.produit).get(Produit_.typeProduit),"imprimante")).select(client.get(Client_.nom));
R. Grin JPA page 94
select c.nomfrom Client c join c.factures f joinf.lignes lwhere l.produit.typeProduit = 'imprimante'
En JPQL :
Classes de métadonnées
Les noms de classe suffixés par « _ » sont des classes qui représentent les métadonnées sur les classes entitésCes classes peuvent être générées automatiquement au moment de la compilation, ou écrites par le développeur
R. Grin JPA page 95
Opérations de modification en volume
R. Grin JPA page 96
en volume
17
Utilité
Pour les performances il est parfois mauvais de charger toutes les données à modifier dans des instances d’entitésEn ce cas, JPQL permet de modifier les
R. Grin JPA page 97
données de la base directement, sans créer les entités correspondantes
Cas d’utilisationSi on veut augmenter de 5% les 1000 employés de l’entreprise il serait mauvais de récupérer dans 1000 instances les données de chaque employés, de modifier le salaire de chacun, puis de sauvegarder les données
R. Grin JPA page 98
de chacun, puis de sauvegarder les donnéesUn simple ordre SQLupdate employeset salaire = salaire * 1.05sera énormément plus performantJPQL permet de lancer cet ordre sans passer par JDBC
Exempleem.getTransaction().begin();String ordre =
"update Employe e " +" set e.salaire = e.salaire * 1.05";
Query q = em.createQuery(ordre);
R. Grin JPA page 99
int nbEntitesModif = q.executeUpdate(); em.getTransaction().commit();
SyntaxeLes ordres de modification en volume référencent les classes et les propriétés des classes mais ils ne créent aucune entité en mémoire et ils ne mettent pas à jour les entités déjà présentes en mémoire
R. Grin JPA page 100
j pupdate Entite as aliasset alias.prop1 = val1, alias.prop2 = val2,…where conditionLa condition peut être aussi complexe que la condition d’un select
RemarquesLes modifications doivent être faites dans une transactionLe plus souvent il faut isoler le lancement de ces opérations dans une transaction à part, ou au moins exécuter ces opérations au début
R. Grin JPA page 101
d’une transaction avant la récupération dans la base d’entités touchées par l’opérationEn effet, les entités en mémoire ne sont pas modifiées par l’opération et elles ne correspondront alors donc plus aux nouvelles valeurs modifiées dans la base de données
Version des entités
Les versions des entités modifiées par les modifications en volume ne sont pas nécessairement mises à jour (dépend des fournisseurs de persistance, donc pas
t bl )portable)Il peut donc être nécessaire d’incrémenter explicitement le numéro de version dans le update
R. Grin JPA page 102
18
Exceptions
R. Grin JPA page 103
Exceptions utilisées par JPA
JPA n’utilise que des exceptions non contrôlées (descendantes de RuntimeException)JPA utilise les 2 exceptions IllegalArgumentException et
R. Grin JPA page 104
IllegalStateException du paquetage java.lang
Sinon, toutes les autres exceptions sont dans le paquetage javax.persistence et héritent de PersistenceException
Types d’exception
NonUniqueResultExceptionNoResultExceptionEntityNotFoundExceptionEntityExistsException
R. Grin JPA page 105
y pTransactionRequiredExceptionRollbackExceptionOptimisticLockExceptionPessimisticLockExceptionLockTimeoutException
PersistenceException et rollbackToute les exceptions de type PersistenceException marquent la transaction en cours pour un rollback (voir section suivante sur les transactions), sauf NoResultException,
R. Grin JPA page 106
pNonUniqueResultException et LockTimeoutException
Exceptions dans les méthodes d’accès aux propriétés
Les RuntimeException marquent la transaction en cours pour un rollbackLes exception contrôlées sont enveloppées dans une PersistenceException (nondans une PersistenceException (non contrôlée) et la transaction en cours est marquée pour un rollback
R. Grin JPA page 107
Transaction
R. Grin JPA page 108
19
2 types de transactions
Les transactions locales à une ressource, fournies par JDBC sont attachées à une seule base de donnéesLes transactions JTA, ont plus de
R. Grin JPA page 109
fonctionnalités que les transactions JDBC ; en particulier elles peuvent travailler avec plusieurs bases de données
Transactions dans Java EE
Elles ne sont pas étudiées dans ce cours
R. Grin JPA page 110
Transactions dans Java SE(sans serveur d’applications)
D’après la spécification JPA, dans Java SE, les fournisseurs de persistance doivent supporter les transactions locales à une ressource mais ne sont pas obligés de
R. Grin JPA page 111
ressource, mais ne sont pas obligés de supporter les transactions JTALa démarcation des transactions est choisie par le développeurLes contextes de persistance peuvent couvrir plusieurs transactions
EntityTransaction
En dehors d’un serveur d’applications, une application doit utiliser l’interface javax.persitence.EntityTransactionpour travailler avec des transactions locales à
R. Grin JPA page 112
une ressourceUne instance de EntityTransaction peut s’obtenir par la méthode getTransaction()de EntityManager
EntityTransaction
public interface EntityTransaction {public void begin();public void commit();public void rollback();bli id tR llb kO l ()
R. Grin JPA page 113
public void setRollbackOnly();public boolean getRollbackOnly();public boolean isActive();
}
Exemple
EntityManager em;...try {em.getTransaction().begin()
R. Grin JPA page 114
...em.getTransaction().commit();
}finally {em.close();
}
20
Rollback (1)
En cas de rollback,n rollback dans la base de donnéesn le contexte de persistance est vidé ; toutes
les entités deviennent détachées
R. Grin JPA page 115
Rollback (2)
Les instances d’entités Java gardent les valeurs qu’elles avaient au moment du rollbackMais ces valeurs sont le plus souvent faussesIl est donc rare d’utiliser ces entités en les
R. Grin JPA page 116
rattachant par un merge à un GELe plus souvent il faut relancer des requêtes pour récupérer des entités avec des valeurs correctes
Transaction et contexte de persistance
Quand un GE n’est pas géré par un container (c’est le cas en dehors d’un serveur d’applications) le contexte de persistance n’est pas fermé à la fin d’une transaction
R. Grin JPA page 117
n est pas fermé à la fin d une transactionQuand un GE est géré par un container (c’est le cas avec un serveur d’applications) et que le contexte de persistance n’est pas de type « étendu » (pas étudié dans ce cours), le contexte est fermé à la fin d’une transaction
Synchronisation d’un GE avec une transaction
Synchronisation d’un GE avec une transaction : le GE est enregistré auprès de la transaction ; un commit de la transaction provoquera alors automatiquement un flush du GE (le GE est
R. Grin JPA page 118
automatiquement un flush du GE (le GE est averti lors du commit)En dehors d’un serveur d’applications (avec Java SE), un GE est obligatoirement synchronisé avec les transactions (qu’il a lancées par la méthode begin() de EntityTransaction)
Modifications et commit
Les modifications effectuées sur les entités gérées sont enregistrées dans la base de données au moment d’un flush du contexte de persistanceSi le GE est synchronisé à la transaction en
R. Grin JPA page 119
Si le GE est synchronisé à la transaction en cours, le commit de la transaction enregistre donc les modifications dans la baseLes modifications sont enregistrées dans la base, même si elles ont été effectuées avant le début de la transaction (avant tx.begin()dans Java SE)
Erreurs et problèmes divers
Démarrer une nouvelle transaction alors qu’il y en a déjà une en cours lancera une IllegalStateExceptioncommit() et rollback() lancent la même exception si aucune transaction n’est activeSi une erreur survient lors de rollback(), une PersistanceException sera lancéeSi une erreur survient lors de commit(), une RollbackException est lancée
R. Grin JPA page 120
21
Marquage pour un rollback (1)
Dans les applications d’entreprise, les transactions sont souvent gérées par un serveur d’applications qui effectue lui-même les commit et rollbackLe code de l’application ne peut lancer de commit ou de rollback ; elle peut cependant marquer la transaction pour un rollback par la méthode setRollbackOnly()Le serveur d’applications sera alors obligé de terminer la transaction par un rollback
R. Grin JPA page 121
Marquage pour un rollback (2)
Le plus souvent la méthode setRollbackOnly est appelée dans un bloc catch lorsqu’une exception s’est produitePlus généralement elle peut être appelée lorsqu’une condition quelconque ne permet pas que la transaction soit validéeIl est impossible d’enlever une marque pour un rollback
R. Grin JPA page 122
Marquage pour un rollback (3)
Cette méthode (ainsi que la méthode getRollbackOnly) ne peut être appelée dans le cas où l’application gère elle-même les transactionsC’est le cas en particulier des applications qui ne sont pas gérées par un serveur d’applications ; ces 2 méthodes sortent donc du cadre fixé pour ce cours
R. Grin JPA page 123
Exceptions et transactions
Il faut prendre garde de ne pas laisser des transactions ouvertes dans le cas où des flushs ont transféré des commandes vers la base de donnéesEn effet, les blocages effectuées dans la base de données ne seront alors pas libérésUne cause d’erreur peut être la levée d’une exception qui fait sortir d’une méthode, et dont le traitement ne ferme pas la transaction en cours (par commit ou rollback)
R. Grin JPA page 124
Concurrence
R. Grin JPA page 125
GE et threads
Une fabrique de GE peut être utilisée sans problème par plusieurs threadsMais un GE ne doit être utilisé concurremment que par un seul thread
R. Grin JPA page 126
22
Entités et threads
Les entités ne sont pas prévues pour être utilisées par plusieurs threads en même tempsSi ça doit être le cas, l’application doit prendre toutes ses précautions pour éviter les problèmes
R. Grin JPA page 127
problèmesC’est aussi le rôle de l’application d’empêcher qu’une entité ne soit gérée par plusieurs GE en même tempsLe rôle du fournisseur de persistance n’intervient pas pour ces cas
Concurrence « BD »
Le fournisseur de persistance peut apporter automatiquement une aide pour éviter les problèmes d’accès concurrents aux données de la BD, pour les entités gérées par un GE
R. Grin JPA page 128
Exemple de problème de concurrence « BD »
Une entité est récupérée depuis la BD (par un find par exemple)L’entité est ensuite modifiée par l’application puis la transaction souhaite valider cette
R. Grin JPA page 129
pmodificationSi une autre transaction a modifié entre-temps les données de la BD correspondant à l’entité, la transaction ne doit pas être validée pour éviter les problèmes de mises à jour perdues
Gestion de la concurrence
Par défaut, le fournisseur de persistance gère les problèmes d’accès concurrents aux entités gérées par un GE avec une stratégie optimisteJPA suppose aussi que la BD a le mode
R. Grin JPA page 130
d’isolation « READ COMMITED » (voir cours sur les BD) et que les écritures dans la base n’ont lieu qu’au moment d’un flush
@Version
Annote un attribut dont la valeur représente un numéro de version (incrémenté à chaque modification), utilisé pour savoir si l’état d’une entité a été modifié entre 2 moments différentsL’attribut doit être de type int Integer
R. Grin JPA page 131
L attribut doit être de type int, Integer, short, Short, long, Long ou java.sql.TimeStamp (si possible, éviter ce dernier type) et ne doit pas être null dans la base de données
Modification de la versionL’application ne doit jamais modifier un tel attribut (seul JPA le modifie), sauf, éventuellement lors des opérations de modifications en volumeLa version est mise à jour automatiquement si le champ modifié est un champ « ordinaire » ou un
R. Grin JPA page 132
champ lié à une association dont l’entité possède la clé étrangère Si on veut mettre à jour la version dans le cas d’une association dont l’entité ne possède pas la clé étrangère, il faut utiliser lock avec l’option d’incrément de la version (étudié ci-après)
23
Exemple
@Versionprivate int version;
R. Grin JPA page 133
Entité « versionnée »
C’est une entité qui possède un attribut qui possède l’annotation @Version (ou le tag correspondant dans les fichiers XML)
R. Grin JPA page 134
Protection des entités « versionnées »
Les données de la BD correspondant à une entité versionnée ne peuvent être modifiées concurremment par 2 transactions gérées par JPA : si une transaction t1 récupère les
R. Grin JPA page 135
pdonnées, elle ne pourra valider des modifications sur ces données que si une autre transaction ne les a pas modifiées entre le moment où t1 les a lues et le moment où elle lance un commit
Protection des entités « non versionnées »
Le fournisseur de persistance peut aussi effectuer automatiquement ce contrôle sur une entité non versionnée (par exemple en comparant les données lues avec les
R. Grin JPA page 136
pdonnées de la base au moment du commit), mais JPA ne l’impose pasPour la portabilité il vaut donc mieux versionner les entités sujettes à des accès concurrents
lock(entite, mode)
Sert à protéger une entité contre les accès concurrents pour les cas où la protection offerte par défaut ne suffit pasCette entité doit être versionnée (sinon, le
R. Grin JPA page 137
traitement n’est pas portable) et déjà gérée
Situations à éviter
Les blocages de JPA ont pour rôle, au moins, d’éviter 2 situations bien connues de la théorie des accès concurrents par des transactionsn les lectures « sales » (dirty) : une valeur
modifiée par transaction non validée peut être lue par les autres transactions
n les lectures non répétables : une transaction lit 2 fois la même entité et l’état de cette entité a été modifiée par une autre transaction entre les 2 lectures
R. Grin JPA page 138
24
Modes de blocage
L’énumération LockModeType (paquetage javax.persistence) définit 5 modes n OPTIMISTIC (READ en JPA 1)n OPTIMISTIC_FORCE_INCREMENT (WRITE
R. Grin JPA page 139
_ _en JPA 1)
n PESSIMISTIC_READn PESSIMISTIC_WRITEn PESSIMISTIC_FORCE_INCREMENT
Modes de blocage optimistesOPTIMISTIC : permet une lecture répétable ; si une autre transaction modifie l’entité bloquée, la transaction qui a bloqué l’entité s’apercevra que le numéro de version a changé et sera invalidée
R. Grin JPA page 140
changé et sera invalidée OPTIMISTIC_FORCE_INCREMENT : comme OPTIMISTIC, mais en plus l’attribut de version est incrémenté, même si l’entité n’a pas été modifiée ) par la transaction dans laquelle le blocage a été effectué (utilité expliquée dans les transparents suivants
Utilité du blocage OPTIMISTICCe blocage est utile pour faire des bilans pour lesquels les données ne doivent pas être modifiées du début à la fin du traitementIl peut aussi être utile si 2 valeurs sont étroitement liées de telle sorte que la
R. Grin JPA page 141
étroitement liées de telle sorte que la modification d’une des valeurs par une transaction T empêche la modification de l’autre par une autre transaction ; pour modifier une des 2 valeurs, T doit alors bloquer aussi l’autre valeur, même si celle-ci ne sera pas modifiée par la transaction T
Blocage OPTIMISTICLa différence avec le mode de protection par défaut est que les 2 transactions ne seront pas validées, même si l’entité n’est pas modifiée par la transaction qui a bloqué l’entité
R. Grin JPA page 142
Blocage OPTIMISTIC
Le « blocage » réel peut n’être effectué qu’au moment du flush ou du commitS’il y a un problème, une exception OptimisticLockException peut être
R. Grin JPA page 143
lancée
Un exemple (1)Un bilan qui calcule le total des salaires des employés pour chaque département doit interdire qu’un employé ne change de département pendant le traitement, en passant du département 10 au département 20
R. Grin JPA page 144
p pSinon, cet employé sera compté 2 fois : après avoir fait le calcul du total du département 10, on va faire une requête pour récupérer les employés du département 20 et retrouver l’employé déjà décompté dans le département 10
25
Un exemple (2)
Pour éviter ce problème, les employés peuvent être bloqués en mode OPTIMISTIC, au fur et à mesure de leur prise en compte pour le calcul du total des salaires de leur dé t t
R. Grin JPA page 145
département
OPTIMISTIC_FORCE_INCREMENT
Force une incrémentation du numéro de versionPour certains traitements il est intéressant de considérer que l’entité a été modifiée même si aucun de ses attributs n’a été modifié, par
R. Grin JPA page 146
exemple lorsqu’une association avec une autre entité a été modifiéeDans l’exemple qui suit, le blocage OPTIMISTIC_FORCE_INCREMENT évite en quelque sorte les problèmes de lignes fantômes avec les associations qui concernent une entité
OPTIMISTIC_FORCE_INCREMENT
En effet, sans le blocage OPTIMISTIC_FORCE_INCREMENT (« OFI » pour la suite), le fournisseur de persistance incrémente automatiquement le numéro de
i i tité t difié i i
R. Grin JPA page 147
version si une entité est modifiée mais pas si les liens des associations qui partent de l’entité sont modifiées lorsque l’entité n’est pas propriétaire de l’association (voir cependant la remarque qui suit l’exemple donné dans les transparents suivants)
Utilité des blocages OFI (1)
Une en-tête de facture contient un champ dans lequel est stocké le total de la factureSupposons que le calcul de ce total est effectué par un processus à part du processus de l’ajout des lignes de la facture
R. Grin JPA page 148
de l ajout des lignes de la factureLorsqu’une ligne de facture est ajoutée à une facture, l’en-tête de la facture n’est pas modifiée donc le fournisseur de persistance n’incrémente pas le numéro de version de l’en-tête
Utilité des blocages OFI (2)
Une ligne peut être ajoutée à une facture sans que le traitement qui calcule le total ne s’en rende compte ; il y aura inconsistance entre ce total et les lignes de la factureSi l’en tête est bloquée en écriture pendant
R. Grin JPA page 149
Si l en-tête est bloquée en écriture pendant l’ajout d’une ligne de facture, le traitement qui effectue le calcul du total s’apercevra que la facture a changé pendant le calcul puisque le numéro de version aura été incrémenté et il n’y aura pas inconsistance
Remarque
Certains fournisseurs de persistance comme Hibernate incrémentent le numéro de version quand les collections d’une entité sont modifiées
R. Grin JPA page 150
Dans ce cas le blocage OFI sera tout de même utile si, par exemple, un des éléments de la collection est modifié, par exemple le prix d’un produit compris dans la facture
26
Blocages pessimistes (1/3)
Lorsque la probabilité de conflits est trop importante, la stratégie optimiste conduit à de trop nombreux rollbackEn ce cas il vaut mieux bloquer dès le début l’accès des autres transactions aux données que l’on manipuleJPA 2.0 a ajouté des blocages pessimistesComme les blocages optimistes, ils permettent d’éviter les lectures non répétables
R. Grin JPA page 151
Blocages pessimistes (2/3)
Le blocage dure jusqu’à la fin de la transactionAucune autre transaction du SGBD sous-jacent ne pourra modifier ou supprimer les données bloquéesSi le blocage est FORCE_INCREMENT ou WRITE, la transaction qui a bloqué pourra modifier ou supprimer les données bloquées (un blocage READ peut être partagé avec une autre transaction et donc empêcher la transaction de modifier les données)
R. Grin JPA page 152
Blocages pessimistes (3/3)
Les versions des entités bloquées en mode pessimiste et modifiées sont mises à jour
R. Grin JPA page 153
Portée du blocageL’énumération PessimisticLockScopecontient les 2 valeurs NORMAL et EXTENDEDEn mode EXTENDED, les données correspondant à une association ou à une collection d’éléments qui sont dans une tablecollection d éléments, qui sont dans une table jointe, sont aussi bloquéesLa portée peut être indiquée avec la propriété javax.persistence.lock.scope dans différentes méthodes des classes EntityManager et Query comme la méthode lock(entité, mode, propriétés)
R. Grin JPA page 154
Modes de blocage pessimistes
PESSIMISTIC_WRITE : le mode le plus courant ; les données de la base correspondant aux entités bloquées ne pourront être modifiées par d’autres t titransactionsPESSIMISTIC_READ : assure des lectures répétablesPESSIMISTIC_FORCE_INCREMENT : comme WRITE avec, en plus, la mise à jour de la version, même si l’entité n’a pas été modifiée
R. Grin JPA page 155
PESSIMISTIC_WRITE
L’entité est bloquée (pour les autres transaction) dès sa modification par la transaction qui a lancé le lock ; correspond à un select for update dans la base de donnéesEmpêche les autres transactions de bloquer en mode PESSIMISTIC_READ ou _WRITE(jusqu’à la fin de la transaction)Les autres transactions devront attendre la fin de la transaction pour pouvoir à leur tour modifier l’entité
R. Grin JPA page 156
27
PESSIMISTIC_READ
Mode utilisé par certaines bases de données pour empêcher les lectures non répétables, sans bloquer par un select for updateLes autres transactions ne peuvent modifier
é à ’ éles données qui correspondent à l’entité mais peuvent les lireEmpêche les autres transactions de bloquer en mode PESSIMISTIC_WRITE
R. Grin JPA page 157
Changement automatique de mode
Lorsqu’une transaction a bloqué une entité en mode PESSIMISTIC_READ et qu’elle modifie l’entité, le mode passe automatiquement en PESSIMISTIC_WRITE
R. Grin JPA page 158
Exception
Lorsque le fournisseur de persistance ne peut obtenir un blocage pessimiste et que le SGBD sous-jacent ne fait qu’un rollback de la requête (et pas de la transaction toute entière) une L kTi tE ti doitentière), une LockTimeoutException doit être lancée et la transaction ne doit être marquée pour un rollback
R. Grin JPA page 159
Autres méthodes pour le blocage
Il est possible d’indiquer un mode de blocage pour les entités ramenées par un find ou un queryrefresh permet aussi d’indiquer un blocage pour l’entité dont on rafraîchit l’état
R. Grin JPA page 160
Entité détachée
R. Grin JPA page 161
Les entités détachées sont surtout utiles dans les applications multi-tiers (avec Java EE et un serveur d’applications)Cette section survole les possibilités offertes
R. Grin JPA page 162
par les entités détachées
28
Cas d’utilisation des entités détachées
Une application multi-tiersLe serveur d’application récupère des données dans la BDCes données sont passées à la couche client
R. Grin JPA page 163
Ces données sont passées à la couche client, montrées à l’utilisateur qui peut les modifierLes modifications sont repassées au serveur et enregistrées dans la BDLes entités détachées facilitent l’implémentation d’un tel cas d’utilisation
Utilisation des entités détachées
Une fois détachées les entités peuvent être passées à la couche clienteLa couche cliente peut modifier les entités détachées
R. Grin JPA page 164
Ces entités peuvent ensuite être rattachées à un GE et les modifications effectuées dans la couche cliente peuvent être alors enregistrées dans la base de données lors d'un flush
Rattachement
La méthode merge de EntityManagerpermet d’obtenir une entité gérée à partir d’une entité détachéeSa signature :
R. Grin JPA page 165
<T> T merge(T entité)
Attention, l’entité passée en paramètre n’est pas rattachée ; c’est l’entité renvoyée par la méthode merge qui est rattachée ; cette entité a le même état et la même clé primaire que l’entité passée en paramètre
Si l’entité passée en paramètre de merge est déjà gérée par le GE, elle est renvoyée par la méthode merge
R. Grin JPA page 166
État d’une entité détachée
L’état d’une entité détachée peut ne pas être entièrement disponible
R. Grin JPA page 167
État d’une entité
Pour des raisons de performances, l'état d’une entité gérée par un GE peut ne avoir été complètement récupéré dans la BD (récupération retardé ; lazy)Le reste de l’état ne sera récupéré avec l’aide
R. Grin JPA page 168
Le reste de l état ne sera récupéré, avec l aide du GE, que lorsque l’entité en aura vraiment besoinSi l’entité est détachée alors qu’une partie de son état n’a pas encore été récupérée, la partie manquante de l’entité détachée ne sera pas disponible
29
Attribut disponible
Un attribut persistant d’une entité est immédiatement disponible dans les 2 cas suivants :n l’attribut a déjà été utilisé
R. Grin JPA page 169
n l’attribut n’a pas été marqué par fetch=LAZY(par défaut, les valeurs des attributs sont chargées en mémoire)
Association d’une entité détachée
Si une association d’un objet détaché a le mode de récupération LAZY, il est possible que l’association ne puisse être récupérée (dépend des circonstances et des fournisseurs d JPA)
R. Grin JPA page 170
de JPA)
Association « lazy » disponible
Si une association d’un objet détaché a le mode de récupération LAZY, il est possible de naviguer à travers cette association si
l’application a déjà effectué cette
R. Grin JPA page 171
n l’application a déjà effectué cette navigation
n ou si l’association a été chargée par un join fetch lors d’une requête
Rendre accessible l’état d’une entité détachée
2 solutions pour accéder à tout l’état (valeurs dans la base de données) d’une entité détachée
R. Grin JPA page 172
L’application récupère tout l’état de l’entité gérée avant le détachementL’application rattache l’entité par la méthode merge pour récupérer l’état manquant
Récupérer une association avant le détachement
Si le but est une entité (OneToOne ou ManyToOne), il suffit de lire une des propriétés de l’entité
R. Grin JPA page 173
Si le but est une collection (OneToMany ou ManyToMany), l’appel de la méthode size()de la collection suffit le plus souvent
Entité détachée et concurrence
Avant que les modifications sur l’entité détachée ne soient enregistrées dans la BD, l’entité doit être rattachée à un contexte de persistance (celui d’origine ou un autre)
R. Grin JPA page 174
A ce moment, ou au moment du commit qui enregistre vraiment les modifications, le contexte de persistance vérifie qu’il n’y a pas de conflit de concurrence
30
Conflit de concurrence
Si les données de la BD associée à un objet détaché ont été modifiées depuis le détachement de l’objet, merge lance une exception
R. Grin JPA page 175
Tout se passe donc comme si un conflit de concurrence avait été détecté (avec une stratégie optimiste)
Détachement automatique
Dans Java SE, quelques situations provoquent un détachement automatique des entités gérées :n Après un rollback
A è l d GE
R. Grin JPA page 176
n Après un clear du GEn Après la fermeture du GEnUn nouvel objet créé à partir à partir d’une
entité sérialisée est détaché (situation fréquente pour les applications distribuées)
Configuration d’une unité de persistance
R. Grin JPA page 177
d une unité de persistance
Fichier persistence.xml
Les informations doivent être données dans un fichier persistence.xmlDans un environnement non géré, ce fichier doit se situer dans le répertoire META-INF d’un des é t i d l th
R. Grin JPA page 178
répertoires du classpathDans un serveur d’applications, le répertoire META-INF peut se trouver à divers endroits : dans WEB-INF/classes, un fichier jar de WEB-INF/lib, un fichier jar de la racine d’un fichier EAR,… (voir spécification pour la liste complète)
Configuration d’une unité de persistance
Dans un environnent non géré par un serveur d’applications, il est nécessaire de donner les informations pour la connexion à la base de données
R. Grin JPA page 179
donnéesSouvent une application n’utilise qu’un seul compte utilisateur dans la base de données ; en ce cas, le nom et le mot de passe de ce compte sont donnés dans le fichier persistence.xml
Configuration d’une unité de persistance
Le fichier persistence.xml donne d’autres informations, comme n les noms des classes « gérées » dont les attributs
seront enregistrés dans la base (entités, classes
R. Grin JPA page 180
g ( ,Embeddable, classes « mapped superclasses »)
n la configuration de l’environnement de mis au point (logging, affichage des ordres SQL lancés dans le SGBD,…)
n des propriétés particulières au driver du fournisseur de persistance
31
persistence.xml<persistencexmlns="http://java.sun.com/xml/ns/persistence"version= "1.0" ><persistence-unit name="Employes"
transaction-type="RESOURCE_LOCAL"><class>p1.Employe</class> noms des
R. Grin JPA page 181
p p y /<class>p1.Dept</class><properties>
. . . <!– Voir transparent suivant--></properties>
</persistence-unit></persistence>
noms desclasses gérées
Section properties
La section properties dépend du fournisseur des fonctionnalités décrites dans la spécification JPAElle contient les informations pour la
R. Grin JPA page 182
connexion mais aussi d’autres informations pour le logging ou la création automatique des tables si elle n’existent pas déjà ; il faut consulter la documentation du fournisseur
Exemple de section properties
<properties><property
name="eclipselink.jdbc.driver"value="oracle.jdbc.OracleDriver"/>
<propertyli li k jdb l
R. Grin JPA page 183
name="eclipselink.jdbc.url"value="jdbc:oracle:thin:@...:INFO "/>
<property name="eclipselink.jdbc.user" value="toto"/>
<property name="eclipselink.jdbc.password" value="mdp"/>
</properties>
Source de données
Lorsque JPA est utilisé avec un serveur d’applications, une source de données fournie par le serveur d’applications est le plus souvent utilisée pour obtenir les connexions (voir D t S dans le cours sur JDBC)
R. Grin JPA page 184
(voir DataSource dans le cours sur JDBC)On pourra alors trouver :<persistence-unit name="Employe"><jta-data-source>jdbc/EmployeDS</jta-
data-source></persistence-unit name=Employe>
Configuration dynamique (1)
Les informations pour la connexion à la base, en particulier le nom et le mot de passe de connexion à la base, peuvent n’être connues qu’à l’exécution et pas au moment de l’é it d dl’écriture du codeEn ce cas, il est possible de ne donner les informations que lors de la création de la fabrique par la classe Persistence
R. Grin JPA page 185
Configuration dynamique (2)
Il suffit de passer à la méthode createEntityManagerFactory une mapqui contient les propriétés qui écrasent les valeurs contenues dans persistence.xml
R. Grin JPA page 186
32
Exemple de configuration dynamique
// Saisie du nom et du mot de passe. . .// Configuration de la connexionMap props = new HashMap();
t(" li li k jdb " )props.put("eclipselink.jdbc.user", nom);props.put("eclipselink.jdbc.password", mdp);EntityManagerFactory emf = Persistence.createEntityManagerFactory(
"employes", props);
R. Grin JPA page 187
Les ajouts de EclipseLink
EclipseLink a des extensions par rapport à la norme JPAPar exemple,n logging
R. Grin JPA page 188
gg gn génération automatique de tablesn…
Génération automatique des tablesLa propriété eclipselink.ddl-generationpermet de créer automatiquement les tables au moment de la création de la fabrique de gestionnaires d’entitésCette propriété peut avoir 3 valeurs :
R. Grin JPA page 189
n none : aucune création automatiquen create-tables : les tables sont créées si
elles n’existent pas déjàn drop-and-create-tables : si une table
existe déjà, elle est d’abord supprimée avant d’être recréée (utile pour les tests)
Exemple
<properties>. . .<propertyname="eclipselink.ddl-generation"
l "d d t t bl "/>
R. Grin JPA page 190
value="drop-and-create-tables"/></properties>
eclipselink.ddl-generation.output-modeindique ce qui sera fait avec les fichiers DDLValeurs possibles : n sql-script génère les fichiers mais ne
R. Grin JPA page 191
les exécute pasn database exécute les ordres DDL mais
ne génère pas les fichiersn both génère les fichiers et les exécute
(valeur par défaut)
eclipselink.application-locationindique le nom du répertoire qui contiendra les fichiers contenant les ordres DDL de création et de suppression des tables (le é t i t déf t)
R. Grin JPA page 192
répertoire courant par défaut)
33
eclipselink.create-ddl-jdbc-file-nameindique le nom du fichier qui contiendra les ordres de création des tables ; par défaut, createDDL.jdbc
R. Grin JPA page 193
eclipselink.drop-ddl-jdbc-file-nameidem pour la suppression des tables ; par défaut, dropDDL.jdbc
logging (1)
La propriété eclipselink.logging.levelpeut avoir les valeursn OFF : aucune information
n SEVERE : uniquement les erreurs
R. Grin JPA page 194
n WARNING : les avertissements (et les erreurs)
n INFO (valeur par défaut) : assez peu d’information en plus
n CONFIG : donne des informations au moment du déploiement sur la configuration
logging (2)
n FINE : donne des informations sur les ordres SQL utile pendant les tests et la mise au point
n FINER : encore plus d’informations, par exemple sur les transactionsFINEST l d’i f ti
R. Grin JPA page 195
n FINEST : encore plus d’informations, par exemple sur l’utilisation des séquences
Fichiers XML
R. Grin JPA page 196
Placement des fichiers XML
Par défaut les fichiers XML contenant les méta données sont placées dans le fichier META-INF/orm.xml, sous un répertoire du classpath
R. Grin JPA page 197
Il est possible d’indiquer d’autres emplacements avec le tag <mapping-file>dans le fichier persistence.xml qui définit l’unité de persistance
Exemple
<persistence-unit name="xxxx">...<mapping-file>META-INF/queries.xml</mapping-file>< i fil >META INF/ titi l
R. Grin JPA page 198
<mapping-file>META-INF/entities.xml</mapping-file>
</persistence-unit>
34
Méta-données par défautL’élément <persistence-unit-defaults>contient des valeurs par défaut qui s’appliquent à toutes les entités de l’unité de persistanceLes sous-éléments peuvent être : <schema>(donner le nom du schéma relationnel),
R. Grin JPA page 199
(donner le nom du schéma relationnel), <catalog> (idem <schema> pour les SGBD qui supportent les catalogues), <access>(accès pour toutes les classes non annotées), <cascade-persist> (pour imposer la persistance par transitivité) et <entity-listeners> (écouteurs par défaut)
Fichiers XML – annotationsAvantages des annotations :nméta données proches du coden pas besoin de donner le contexte comme
dans les fichiers XMLn plus simple
R. Grin JPA page 200
n plus simpleInconvénients :n changement des méta données nécessite
une recompilationLes informations données dans les fichiers XML l’emportent sur les annotations
Fichiers XML – annotations
Les annotations sont le plus souvent utiliséesLes fichiers de configuration XML peuvent être préférés lorsque l’information n est dépendante de l’environnement
d’ é i l l d
R. Grin JPA page 201
d’exécution ; par exemple pour les noms de tables ou de colonnes
n concerne plusieurs classes ; par exemple @TableGenerator qui donne le nom de la table qui génère automatiquement les clés d’identification des entités
Exemple de fichier orm.xml (en-tête)
<?xml version="1.0" encoding="UTF-8" ?><entity-mappings xmlns="http://java.sun.com/xml/ns/persistence.orm"xmlns:xsi:"http://www w3 org/2001/XMLSchema
R. Grin JPA page 202
xmlns:xsi: http://www.w3.org/2001/XMLSchema-instance"xsi-schemaLocation ="http://java.sun.com/xml/ns/persistence.xml http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" version="1.0">
Exemple de fichier orm.xml (fin)
<entity class="jpa.Employe"><table name="EMP" /><named-query name="findEmpByName"
<query>select e from Employe e where e nom like :nomEmploye</query>
R. Grin JPA page 203
e.nom like :nomEmploye</query></named-query>
</entity></entity-mappings>
JPA et DAO
R. Grin JPA page 204
35
Utilité des DAO ?
La possibilité de détacher et rattacher une entité rend souvent les DTO inutiles (voir cours sur DAO et modèles de conception associés)
R. Grin JPA page 205
Puisque JPA augmente fortement la portabilité du code chargé de la persistance, est-ce que les DAOs sont encore nécessaires ?
Les DAOs ne sont pas toujours utiles
Pour les petites applications à existence courte, avec peu de fonctionnalités « métier », l’utilisation directe de JPA depuis le code métier (sans utilisation de DAOs) simplifie le code lié à la persistance
R. Grin JPA page 206
code lié à la persistanceLes DAO ne sont alors pas vraiment utiles car JPA offre malgré tout une meilleure portabilité que JDBC et l’utilisation des DAOs rend le code un peu plus complexe
Les DAOs sont encore utiles
Pour les applications plus complexes, les DAOs peuvent apporter une meilleure abstraction et une meilleure factorisation du code lié à la persistanceLes détails techniques liés à JPA (ou à une
R. Grin JPA page 207
Les détails techniques liés à JPA (ou à une version de JPA) peuvent être cachés dans la couche DAOLes DAOs pourront aussi faciliter le passage éventuel à un autre type de persistance, par exemple à un SGBD objet
Exemple de factorisation du code
Un DAO peut contenir des méthodes spécialement adaptées au « métier »Par exemple, une méthode qui renvoie la liste des candidats qui ont obtenu leur examen
R. Grin JPA page 208
avec une certaine mention, passée en paramètreCette méthode peut être appelée de plusieurs endroits du code « métier »
Variantes des DAOs
Parmi toutes les variantes des méthodes des DAOs (voir cours sur les DAOs), celles qui prennent en paramètre ou qui renvoient des objets (des entités JPA dans ce cas) sont le
l t h i i
R. Grin JPA page 209
plus souvent choisiesEn effet, les entités détachées peuvent jouer le rôle des DTOs qui sont donc inutiles, et les variantes qui utilisent plusieurs paramètres dont les valeurs correspondent aux propriétés des objets compliquent inutilement le code
DAOs génériques
La généricité permet d’écrire une classe mère générique de tous les DAOs, ce qui allège d’autant les classes filles représentant chacune des classes DAOs
R. Grin JPA page 210
Le type des entités gérées et le type de l’identificateur sont les paramètres de type de ce DAO génériqueLes transparents suivants sont un exemple d’implémentation que vous pouvez modifier à votre convenance
36
Interface DAO générique
public interface DaoGenerique<T,ID extends Serializable> {T findById(ID id);List<T> findAll();void create(T objet);
R. Grin JPA page 211
void create(T objet);void delete(T entite);T update(T entite);
}
Interface DAO pour une entité
public interface StyloDao extends DaoGenerique<Stylo, Long> { }
On peut ajouter des méthodes adaptée au « métier », comme cette méthode qui renvoie toutes les marques de stylos en vente :
R. Grin JPA page 212
toutes les marques de stylos en vente :public interface StyloDao extends DaoGenerique<Stylo, Long> {List<String> findMarques();
}
Classe DAO générique (1)public abstract class DaoGeneriqueJpa<T, ID extends Serializable> implements DaoGenerique<T, ID> {private Class<T> classeEntite;private EntityManager em;bli G i () {
R. Grin JPA page 213
public DaoGeneriqueJpa() {// classe concrète de T passée à findthis.classeEntite = (Class<T>)
((ParameterizedType)getClass()..getGenericSuperclass()).getActualTypeArguments()[0];
}
Classe DAO générique (2)public void insert(T objet) {
em.persist(objet);}public void delete(T objet) {
em.remove(objet);}
R. Grin JPA page 214
}public T findById(ID id) {
return em.find(classeEntite, id);}public T update(T entite) {
return em.merge(entite);}
Classe DAO générique (3)
public void setEntityManager(EntityManager em) {
this.em = em;}protected EntityManager
R. Grin JPA page 215
getEntityManager() {return this.em;
}}
DAO pour une entité
public class StyloDaoJpaextends DaoGeneriqueJpa<Stylo, Long> implements StyloDao {
public List<Stylo> findAll() {Query query = getEntityManager()
R. Grin JPA page 216
Q y q y g y g ().createNamedQuery("Stylo.findAll");
return(List<Stylo>)query.getResultList();
}
}
37
Cache des données
R. Grin JPA page 217
UtilitéTous les fournisseurs de persistance utilisent un cache de second niveau (en plus du contexte de persistance) pour éviter des accès aux bases de données, et donc améliorer les performances
R. Grin JPA page 218
Tous les gestionnaires d’entités d’une même unité de persistance utilisent le même cache de second niveauSi une donnée est lue par un gestionnaire et gardée dans le cache, elle pourra être lue par les autres gestionnaires sans accès à la BD
Utilité
La spécification JPA 2 standardise en partie l’utilisation du cache de 2ème niveau ; il faut consulter la documentation du fournisseur de persistance pour connaître les autres
ibilité é t ll
R. Grin JPA page 219
possibilités éventuelles
Utilisation des caches
Il n’est pas toujours facile d’utiliser correctement le cache de 2ème niveauLe plus souvent on peut se contenter d’indiquer quelles classes utiliseront ce cache
R. Grin JPA page 220
Par exemple, il peut être très intéressant d’utiliser un cache pour des classes dont les données ne changent que très rarement comme les noms des pays
Problèmes éventuels
Parfois ce cache pose des problèmes si la base de données est utilisée en parallèle par d’autres applicationsPar exemple, si des entités liées à une entité
R. Grin JPA page 221
sont supprimées de la base en dehors de l’application, le cache peut penser modifier une ligne avec un UPDATE au lieu d’en insérer une nouvelle avec un INSERT, ce qui provoque une erreur
Solutions (1)
L’utilisation de la méthode refresh de EntityManager permet de récupérer dans la base de données des données « fraiches », sans jamais passer par le cache
R. Grin JPA page 222
Un blocage pessimiste peut parfois être la solution si on souhaite interdire la modification de lignes par une autre application (implémentation dépendante du fournisseur de persistance)
38
Solutions (2)
Parfois la seule solution est de vider le cache ou d’indiquer au fournisseur de ne pas utiliser le cache pour effectuer certaines opérations (la façon de faire dépend en partie du f i d i t )
R. Grin JPA page 223
fournisseur de persistance)JPA 2 permet d’effectuer certains réglages pour optimiser l’utilisation du cache, et éviter les problèmes liés aux données qui ne sont pas à jour dans le cache
Utilisation ou non du cacheLe fichier persistence.xml peut indiquer si une unité de persistance utilisera ou non un cache avec l’élément caching qui peut avoir 4 valeurs possibles : ALL, NONE, ENABLE DELECTIVE et _DISABLE_SELECTIVE
Exemple :<caching>ENABLE_SELECTIVE</caching>
Pas de valeur par défaut ; il faut donc toujours donner une valeur pour avoir une application portable entre les fournisseurs de persistance
R. Grin JPA page 224
Utilisation ou non du cache
ALL : un cache sera utilisé pour toutes les entitésNONE : aucun cache ne sera utiliséENABLE_SELECTIVE : par défaut, les entités _ne sont pas cachées ; seules le sont celles qui sont annotées par @Cacheable(true)DISABLE_SELECTIVE : par défaut, les entités sont cachées ; seules ne le sont pas celles qui sont annotées par @Cacheable(false)
R. Grin JPA page 225
Annotation @Cacheable
Une classe d’entités peut être annotée pour indiquer si les entités de la classe seront conservées dans le cache (voir élément caching de persistence.xml pour plus d é i i )de précisions)Exemple :@Entity@Cacheable(true)public class Pays { ... }
R. Grin JPA page 226
@Cacheable suffit cartrue est la valeur par défaut
Interface CacheJPA 2 a introduit cette interface simplifiée avec le cache pour affiner les indications données par @CacheablePar exemple, pour une classe d’entités « cacheable », il est possible d’enlever une certaine entité du cacheSi le cache n’est pas utilisé, les méthodes ne font rien, à part contains qui renvoie toujours falseLe cache s’obtient avec la méthode getCache() de EntityManagerFactory
R. Grin JPA page 227
Méthodes de l’interface Cache
boolean contains(Class cls, Object cléPrimaire) indique si le cache contient une entitévoid evict(Class cls) enlève du cache t t l tité d’ t i ltoutes les entités d’une certaine classevoid evict(Class cls, Object cléPrimaire) enlève du cache une certaine entitévoid evictAll() vide le cache
R. Grin JPA page 228
39
Propriétés liées au cache
En plus de toutes les possibilités déjà exposées, on peut donner des indications pour les méthodes find, refresh et pour les requêtes (Query) exécutées par un
ti i d’ tité ( i é ifi tigestionnaire d’entités (voir spécification pour plus de détails)Exemple :em.setProperty(QueryHints.CACHE_RETRIEVE_MODE, CacheRetrieveMode.USE);
R. Grin JPA page 229
Propriétés pour un find
Il est possible de donner des indications d’utilisation du cache pour un seul findExemple :HashMap props = new HashMap();
t(Q Hi t CACHE RETRIEVE MODEprops.put(QueryHints.CACHE_RETRIEVE_MODE, CacheRetrieveMode.BYPASS);
em.find(MyEntity.class, id, props);
R. Grin JPA page 230
Propriétés pour les commit
Il est aussi possible d’indiquer si les données validées par un commit seront mises dans le cache
R. Grin JPA page 231
Optimisations
R. Grin JPA page 232
Les performances d’une application peuvent être grandement améliorées parn un choix adapté du mode de récupération
des entités associées
R. Grin JPA page 233
n l’utilisation d’opérations de modifications en volume, sans création d’entités
n une bonne utilisation du cache de 2ème
niveau
Méthode callback et listener
R. Grin JPA page 234
40
Méthodes « callback » Des méthodes peuvent être annotées pour indiquer qu’elles seront appelées par le fournisseur de persistance quand une entité passera dans une nouvelle étape de son cycle de vie
R. Grin JPA page 235
Ces méthodes peuvent appartenir à une classe entité (entity) ou classe mère « mappedSuperclass » ou à une classe « écouteur » (listener)Une méthode peut être annotée par plusieurs de ces annotations
Annotations@PrePersist : quand persist (ou merge) s’est terminé avec succès@PostPersist : après l’insertion dans la BD@PreRemove : quand remove est appelé
R. Grin JPA page 236
@PostRemove : après suppression dans la BD@PreUpdate : avant modification dans la BD@PostUpdate : après modification dans la BD@PostLoad : après la lecture des données de la BD pour construire une entité
Callback dans fichier XML
Les méthodes callback peuvent aussi être indiquées dans un fichier XMLUn fichier XML peut aussi indiquer des listeners par défaut qui seront appelées pour
R. Grin JPA page 237
toutes les entités, dans le sous-élément <entity-listeners> de l’élément <persistence-unit-defaults>
Exemples d’utilisation
Un trigger de la base de données peut donner les valeurs de l’utilisateur qui a modifié pour la dernière fois une entité, avec la date de cette modification
R. Grin JPA page 238
Pour remplacer un tel trigger, il est possible d’ajouter une méthode annotée par @PrePersist qui remplit ces valeurs dans les entitésUne méthode callback peut aussi initialiser des attributs non persistants d’une entité
Callback dans une entité
Ces méthodes ne doivent aucune paramètre et le type retour doit être voidElles ne doivent pas avoir de clause throwsSi elles renvoient une exception non
ôlé l é ll é h d
R. Grin JPA page 239
contrôlée, les éventuelles méthodes callbacks suivantes ne sont pas appelées et la transaction est marquée pour un rollback(elle ne pourra pas être validée) Une seule méthode callback d’un certain type (par exemple PrePersist) par entité
Callback dans un écouteur
La signature doit avoir un paramètre compatible avec le type de l’entité gérée pour qu’il puisse contenir l’instance de l’entité« Compatible » signifie que le type doit être un
R. Grin JPA page 240
« surtype » de la classe entité : la classe de l’entité, ou une classe mère, ou une interface implémentée par la classe de l’entitéUne classe listener ne doit contenir aucune variable d’instance (elle doit être « sans état ») et doit avoir un constructeur sans paramètre
41
Attacher un écouteur
L’annotation @EntityListeners permet d’attacher un ou plusieurs écouteurs à une classe entitéExemple :
R. Grin JPA page 241
@Entity@EntityListeners({C1.class, C2.class})public class Entite {
Ordre d’appel
Lorsque un événement du cycle de vie survient, les différentes méthodes sont appelées dans cet ordre :
1. Méthodes des listeners par défaut
R. Grin JPA page 242
2. Méthodes des listeners3. Méthode de l’entité en cause
Validation des données
R. Grin JPA page 243
Généralités
JPA 2 permet de valider les données automatiquement durant le cycle de vie d’une entité, dans les phases « pre-persist », « pre-update » et « pre-remove », ce qui permet d’é i l’é it d’ é t d t ld’économiser l’écriture d’un écouteur dont le rôle ne serait que de valider des entitésCette validation s’appuie sur l’API de validation (JSR 303) qui peut être utilisée par tout code Java (Java SE ou Java EE) ; cette API ne sera pas détaillée dans ce cours
R. Grin JPA page 244
Configuration
Par défaut, les entités sont validées dans les phases pre-persist et pre-update pour le groupe de validation Default (voir API de validation) ; aucune contrainte n’est validée
l hpour la phase pre-removeIl est possible de configurer le mode de validation (validation ou non) par l’élément <validation-mode> du fichier persistence.xml ou par une propriété passée à la méthode createEntityManagerFactory
R. Grin JPA page 245
Exemple d’entité
@Entitypublic class Client {@Id @NotNullprivate int id;@N tN ll @Si ( 60)@NotNull @Size(max=60)private String nom;@(groups=Remove.class,min=0,max=0)private int nbLivraisonsEnCours;...
}
R. Grin JPA page 246
42
Validations standard (1/3)
@Null, @NotNullPour toutes les contraintes suivantes, null est considéré comme valide@AssertTrue, @AssertFalse : valeur booléenne imposée (boolean ou Boolean)@Min, @Max : bornes pour un entier (long) ; tous les types de nombres sont supportés, sauf double et float (à cause des erreurs d’arrondi)@DecimalMin, @DecimalMax : bornes pour une valeur décimale (String)
R. Grin JPA page 247
Validations standard (2/3)
@Size avec les attributs min et max ; types supportés : String, Collection, Map, tableau@Digits avec les attributs integer et
fffraction : nombres maximums de chiffres dans les parties entières et décimales ; types supportés : BigDecimal, String, tous les types entiers (int, Integer, byte,...), y compris BigInteger, mais pas les types double et float (erreurs d’arrondi)
R. Grin JPA page 248
Validations standard (3/3)
@Past, @Future : date avant ou après la date actuelle ; types supportés : Date et Calendar du paquetage java.util@Pattern : la valeur correspond à une
é è ’expression régulière ; l’attribut flags peut comporter des valeurs utiles pour vérifier la concordance (voir javadoc de java.util.regex.Pattern) : UNIX_LINES, CASE_INSENSITIVE, MULTILINE,...
R. Grin JPA page 249
Validations particulières
Il est possible d’ajouter des validations aux validations standard ; pas étudié dans ce cours
R. Grin JPA page 250
Précisions sur le schéma de la base de données
R. Grin JPA page 251
de la base de données
Génération du schéma
Tous les fournisseurs de persistance fournissent des propriétés pour indiquer que le schéma de la base de données sera généré automatiquement à partir des l J
R. Grin JPA page 252
classes JavaEn ce cas, il est possible de donner des précisions sur le schéma généré
43
Contrainte d’unicité sur une colonne
@Column(unique=true)
Une contrainte d’unicité sera ajoutée dans la définition de la colonne de la tableComme dans toute cette section, l’attribut unique ne servira à rien (sinon pour la documentation du code) si le schéma n’est pas généré automatiquement
R. Grin JPA page 253
Contrainte d’unicité sur une table
@Table(uniqueConstraints =@UniqueConstraint(columnNames = {"COL1", "COL2"}))
Une contrainte d’unicité sera ajoutée dans la définition de la table
R. Grin JPA page 254
Contraint NULL
@Column(nullable=false)Une contrainte NOT NULL sera ajoutée à la définition de la colonne
R. Grin JPA page 255
Types de données
@Column(length=25)précise la taille de la colonne de type chaîne de caractères (longueur 255 par défaut)@Column(precision=10, scale=2)précise la précision (le nombre total de chiffres) et le nombre de chiffres après la virgule pour une colonne de type nombre décimal (float, double,…)
R. Grin JPA page 256
Définition quelconque
On peut même donner la définition du type d’une colonne pour prendre en compte des particularités de certains SGBDL’attribut columnDefinition des annotations @Col mn @JoinCol mnannotations @Column, @JoinColumn, @PrimaryKeyJoinColumn, @DiscriminatorColumn permet de définir un type particulierExemple : @Column(columnDefinition="NVARCHAR2(8)")
R. Grin JPA page 257
Bibliographie
R. Grin JPA page 258
44
Sites Web
Spécification officielle de JPA 2 :http://jcp.org/en/jsr/detail?id=317Eclipselink :http://www.eclipse.org/eclipselink/jpa.php
R. Grin JPA page 259
Hibernate : http://www.hibernate.org/hib_docs/entitymanager/reference/en/html/Open JPA :http://openjpa.apache.org/
Livres
Pro JPA 2: Mastering the Java™ Persistence APIde Mike Keith et Merrick SchincariolEdition Apress
R. Grin JPA page 260
Java Persistence with Hibernatede Christian Bauer et Gavin KingEdition Manning(en anglais ; pour la version 1 de JPA)