Vous êtes sur la page 1sur 33

Plan de la partie 1

JPA ‰ Présentation de JPA

(Java Persistence API) ‰ Entités persistantes


‰ Gestionnaire de persistance
- Partie 1 ‰ Compléments sur les entités : identité,
associations, héritage
Université de Nice - Sophia Antipolis
Version 1.1 – 27/11/07
Richard Grin

R. Grin JPA page 2

EJB 3.0
‰ Java EE 5 (Enterprise Edition) est une plate-
forme de développement et un ensemble de
spécifications pour le développement
Présentation de JPA d’applications d’entreprises multi-tiers
‰ EJB 3.0
3 0 fait partie de Java EE 5 ; cc’est
est une
spécification récente (mai 2006) d’un cadre
(framework) pour l’utilisation de composants
métier réutilisables par des serveurs
d’applications Java

R. Grin JPA page 3 R. Grin JPA page 4

JPA Solution ORM


‰ JPA (Java persistence API) est la partie de la ‰ Transparente : les classes des entités
spécification EJB 3.0 qui concerne la persistance persistantes sont indifférentes au mécanisme de
des composants dans une base de données persistance
relationnelle ‰ Automatique : des appels simples de haut niveau
‰ Peut ss’appliquer
appliquer sur toutes les applications Java, pour gérer la persistance, tels que persist(objet)
même celles qui s’exécutent en dehors d’un pour rendre un objet persistant ; pas d’appel de
serveur d’applications bas niveau comme avec JDBC

R. Grin JPA page 5 R. Grin JPA page 6

1
JPA Avertissement
‰ JPA est un standard pour la persistance des ‰ JPA est le plus souvent utilisé dans le
objets Java contexte d’un serveur d’applications
‰ Pour plus de précisions, lire la spécification à ‰ Ce cours étudie l’utilisation de JPA par une
l’adresse application autonome, en dehors de tout
http://jcp org/aboutJava/communityprocess/pfd/js
http://jcp.org/aboutJava/communityprocess/pfd/js serveur d’applications
d applications
r220/index.html ‰ Quelques informations sur l’utilisation de JPA
avec un serveur d’applications sont données
dans ce cours mais un autre support
complémentaire est en préparation

R. Grin JPA page 7 R. Grin JPA page 8

Fournisseur de persistance Entités


‰ Comme pour JDBC, l’utilisation de JPA ‰ Les classes dont les instances peuvent être
nécessite un fournisseur de persistance qui persistantes sont appelées des entités dans la
implémente les classes et méthodes de l’API spécification de JPA
‰ GlassFish, est l’implémentation de référence ‰ Le développeur indique qu’une classe est une
de la spécification EJB 3 entité en lui associant l’annotation
l annotation @Entity
‰ GlassFish utilise TopLink essentials comme ‰ Ne pas oublier d’importer
fournisseur de persistance pour JPA (produit javax.Persistence.Entity dans les
Oracle) classes entités (idem pour toutes les
annotations)
‰ D’autres implémentations : TopLink,
Hibernate Entity Manager, BEA Kodo
R. Grin JPA page 9 R. Grin JPA page 10

Vocabulaire Exemple d’entité – les champs


‰ Dans la suite de ce cours et quand il n’y aura @Entity
pas ambiguïté, « entité » désignera soit une public class Departement {
classe entité, soit une instance de classe private int id;
entité, suivant le contexte private String nom;
private String lieu;
private Collection<Employe> employes =
new List<Employe>();

R. Grin JPA page 11 R. Grin JPA page 12

2
Les constructeurs Exemple d’entité – l’identificateur
/** @Id
* Constructeur sans paramètre @GeneratedValue
* obligatoire. public int getId() {
*/ return id;
}
public Departement() { }
public void setId(int id) {
public Departement(String nom,
this.id = id;
String lieu) {
}
this.nom = nom;
this.lieu = lieu;
}

R. Grin JPA page 13 R. Grin JPA page 14

Exemple d’entité – une propriété Exemple d’entité – une association


L’association inverse
dans la classe Employe
public String getNom() { @OneToMany(mappedBy="dept")
return nom; public Collection<Employe> getEmployes() {
} return employes;
public void setNom(String
p ( g nom)
) { }
this.nom = nom; public void setEmployes(Collection<Employe>
emps) {
}
this.employes = emps;
}
}

R. Grin JPA page 15 R. Grin JPA page 16

Fichiers de configuration XML Configuration de la connexion


‰ Les annotations @Entity (et toutes les autres ‰ Il est nécessaire d’indiquer au fournisseur de
annotations JPA) peuvent être remplacées ou/et persistance comment il peut se connecter à
surchargées (les fichiers XML l’emportent sur les la base de données
annotations) par des informations enregistrées
‰ Les informations doivent être données dans
dans un fichier de configuration XML un fichier
f persistence.xml situé é dans un
‰ Exemple : répertoire META-INF dans le classpath
<table-generator name="empgen"
table="ID_GEN" pk-column-value="EmpId"/> ‰ Ce fichier peut aussi comporter d’autres
informations ; il est étudié en détails dans la
‰ La suite n’utilisera que les annotations
suite du cours

R. Grin JPA page 17 R. Grin JPA page 18

3
Exemple (début) Exemple (fin)
<persistence
xmlns="http://java.sun.com/xml/ns/persistence" <property name="toplink.jdbc.user"
version="1.0"> value="toto"/>
<persistence-unit name="Employes"> <property name="toplink.jdbc.password"
<class>jpa.Departement</class> value="xxxxx"/>
<class>jpa.Employe</class>
/ </properties>
<properties> </persistence-unit>
<property name="toplink.jdbc.driver" </persistence>
value="oracle.jdbc.OracleDriver"/>
<property name="toplink.jdbc.url"
value="jdbc:oracle:thin:@cl.truc.fr:1521:XE"/>

R. Grin JPA page 19 R. Grin JPA page 20

Gestionnaire d’entités Exemple de code (1)

‰ Classe EntityManagerFactory emf = Persistence.


javax.persistence.EntityManager createEntityManagerFactory("employes");
EntityManager em =
‰ Le gestionnaire d’entités (GE) est emf.createEntityManager();
l’interlocuteur principal pour le développeur EntityTransaction tx = em em.getTransaction();
getTransaction();
‰ Il fournit les méthodes pour gérer les entités : tx.begin();
les rendre persistantes, les supprimer de la Dept dept = new Dept("Direction", "Nice");
base de données, retrouver leurs valeurs em.persist(dept); sera enregistré dans
dans la base, etc. dept.setLieu("Paris"); la base de données…
tx.commit();
…au moment du
R. Grin JPA page 21 R. Grin JPA
commit page 22

Exemple de code (2)


String queryString =
"SELECT e FROM Employe e " ‰ Remarque : le code précédent devrait être
+ " WHERE e.poste = :poste"; inclus dans un bloc try – finally (les
Query query = em.createQuery(queryString); close finaux dans le finally), et pourrait
query.setParameter("poste", "INGENIEUR"); contenir éventuellement des blocs catch
Li t<Em l
List<Employe>
> li
liste
t = pour attraper
tt les
l exceptions
ti (d
(des
query.getResultList(); RuntimeException) lancées par les
for (Employe e : liste) {
méthodes de EntityManager
System.out.println(e.getNom());
} ‰ Ce bloc try – finally a été omis pour ne
em.close(); pas alourdir le code
emf.close()
R. Grin JPA page 23 R. Grin JPA page 24

4
Contexte de persistance GE – contexte de persistance
‰ Dans le cadre d’une application autonome, la
‰ La méthode persist(objet) de la classe
relation est simple : un GE possède un
EntityManager rend persistant un objet
contexte de persistance, qui n’appartient qu’à
‰ L’objet est ajouté à un contexte de persistance lui et il le garde pendant toute son existence
qui est géré par le GE
‰ Lorsque
L lle GE estt géré
é é par un serveur
‰ Toute modification apportée à un objet du d’applications, la relation est plus complexe ;
contexte de persistance sera enregistrée dans un contexte de persistance peut se propager
la base de données d’un GE à un autre et il peut être fermé
‰ L’ensemble des entités gérées par un GE automatiquement à la fin de la transaction en
s’appelle un contexte de persistance cours (voir support complémentaire)
R. Grin JPA page 25 R. Grin JPA page 26

Caractéristiques
‰ Seules les entités peuvent être
n renvoyées par une requête (Query)

n passées en paramètre d’une méthode d’un


Entités EntityManager ou d’un Query
le but d’une association
n

n référencées dans une requête JPQL

‰ Une classe entité peut utiliser d’autres classes


pour conserver des états persistants
(MappedSuperclass ou Embedded étudiées
plus loin)
R. Grin JPA page 27 R. Grin JPA page 28

Conditions pour les classes entités Conditions pour les classes entités
‰ Elle doit posséder un attribut qui représente la ‰ Une classe entité ne doit pas être une classe
clé primaire dans la BD (@Id) interne
‰ Une classe entité doit avoir un constructeur ‰ Une entité peut être une classe abstraite mais
sans paramètre protected ou public elle ne peut être une interface
‰ Elle ne doit pas être final
‰ Aucune méthode ou champ persistant ne doit
être final
‰ Si une instance peut être passée par valeur en
paramètre d’une méthode comme un objet
détaché, elle doit implémenter Serializable
R. Grin JPA page 29 R. Grin JPA page 30

5
Convention de nommage JavaBean 2 types d’accès
‰ Un JavaBean possède des propriétés ‰ Le fournisseur de persistance accédera à la
‰ Une propriété est représentée par 2 valeur d’une variable d’instance
accesseurs (« getter » et « setter ») qui n soit en accédant directement à la variable

doivent suivre la convention de nommage d’instance (par introspection)


suivante : n soit en passant par ses accesseurs (getter
si prop est le nom de la propriété, le getter ou setter)
doit être getProp (ou isProp si la propriété ‰ Le type d’accès est déterminé par
est de type boolean) et le setter setProp l’emplacement des annotations (associées aux
‰ Souvent une propriété correspond à une variables d’instance ou aux getter)
variable d’instance
R. Grin JPA page 31 R. Grin JPA page 32

Accès par propriété


Vocabulaire JPA
‰ Les accesseurs (setter et getter) doivent être
protected ou public ‰ Un champ désigne une variable d’instance
‰ Ils peuvent contenir d’autres instructions que ‰ JPA parle de propriété lorsque l’accès se fait
le seul code lié à la valeur de la variable en passant par les accesseurs (getter ou
sous-jacente setter)
‰ Ces instructions seront exécutées par le
‰ Lorsque le type d’accès est indifférent, JPA
fournisseur de persistance parle d’attribut
‰ Si une exception est levée par un accesseur,
la transaction est invalidée ; les exceptions
contrôlées sont enveloppées par une
PersistenceException (non contrôlée,
sous RuntimeException)
R. Grin JPA page 33 R. Grin JPA page 34

Choix du type d’accès Attributs persistants


‰ Le choix doit être le même pour toutes les ‰ Par défaut, tous les attributs d’une entité sont
classes d’une hiérarchie d’héritage (interdit de persistants
mélanger les 2 façons)
‰ L’annotation @Basic indique qu’un attribut
‰ En programmation objet il est conseillé d’utiliser
est persistant mais elle n’est donc
plutôt les accesseurs que les accès directs aux indispensable que si on veut préciser des
champs (meilleur contrôle des valeurs) ; c’est informations sur cette persistance (par
aussi le cas avec JPA exemple, une récupération retardée)
‰ Rappel : le choix est déterminé par
‰ Seuls les attributs dont la variable est
l'emplacement des annotations ; elles sont transient ou qui sont annotés par
associées soit aux accesseurs, soit aux @Transient ne sont pas persistants
variables d'instance ; ne pas mélanger les 2 !
R. Grin JPA page 35 R. Grin JPA page 36

6
Cycle de vie d’une instance d’entité Cycle de vie d’une instance d’entité
n détachée : elle a une identité dans la base
‰ L’instance peut être mais elle n’est plus associée à un contexte
n nouvelle (new) : elle est créée mais pas de persistance (une entité peut, par
associée à un contexte de persistance exemple, devenir détachée si le contexte
nggérée ppar un g
gestionnaire de ppersistance ; de persistance est vidé ou si elle est
elle a une identité dans la base de données envoyée dans une autre JVM par RMI)
(un objet peut devenir géré par la méthode n supprimée : elle a une identité dans la
persist, ou merge d’une entité détachée ; base ; elle est associée à un contexte de
un objet géré peut aussi provenir d’une persistance et ce contexte doit la
requête faite par un gestionnaire d’entités ou supprimer de la base de données (passe
d’une navigation à partir d’un objet géré) dans cet état par la méthode remove)
R. Grin JPA page 37 R. Grin JPA page 38

Cycle de vie d’une entité Les tables de la base de données


refresh ‰ Dans les cas simples, une table correspond à
une classe
persist clear n le nom de la table est le nom de la classe
Nouvelle Gérée Détachée
n les noms des colonnes correspondent
p aux
merge
find noms des attributs persistants
remove ‰ Par exemple, les données de la classe
Query
Departement sont enregistrées dans la table
Departement (ou DEPARTEMENT) dont les
BD
Supprimée colonnes se nomment id, nom, lieu

R. Grin JPA page 39 R. Grin JPA page 40

Configuration « par exception » Nom de table


‰ La configuration des classes entités suppose
‰ Pour donner à la table un autre nom que le
des valeurs par défaut
non de la classe, il faut ajouter une
‰ Il n’est nécessaire d’ajouter des informations annotation @Table
de configuration que si ces valeurs par défaut
‰ Exemple :
ne conviennent pas @Entity
‰ Par exemple, @Entity suppose que la table @Table(name="AUTRENOM")
qui contient les données des instances de la public class Classe {
classe a le même nom que la classe ...
}

R. Grin JPA page 41 R. Grin JPA page 42

7
Nom de colonne Classe Embeddable
‰ Les entités persistantes ne sont pas les seules
‰ Pour donner à une colonne de la table un autre classes persistantes
nom que le nom de l’attribut correspondant, il
‰ Il existe aussi des classes « insérées » ou
faut ajouter une annotation @Column
« incorporées » (embedded) dont les données
‰ Cette annotation peut aussi comporter des n’ont
n ont pas d’identité
d identité dans la BD mais sont
attributs pour définir plus précisément la insérées dans une des tables associées à une
colonne entité persistante
‰ Exemple :
‰ Elles peuvent être annotées comme les entités
@Column(name="AUTRENOM",
updatable=false, length=80) (avec @Column par exemple)
public String getTruc() { ... } ‰ Par exemple, une classe Adresse dont les
valeurs sont insérées dans la table Employe
R. Grin JPA page 43 R. Grin JPA page 44

Classe Embeddable Exemple


@Embeddable
‰ Comme les entités, ces classes doivent avoir public class Adresse {
un constructeur sans paramètre private int numero;
‰ Les types permis pour leurs attributs sont les private String rue;
mêmes que les types permis pour les private String ville;
attributs des entités . . .
}
@Entity
public class Employe {
@Embedded private Adresse adresse;
...
Rappel : à mettre sur getAdresse()
R. Grin JPA page 45 R. Grin si accès JPA
par propriété page 46

Restrictions Classes insérées partagées


‰ La version actuelle de JPA a plusieurs ‰ Une classe entité peut référencer plusieurs
restrictions (peut-être enlevées dans une instances d’une même classe insérée
prochaine version) : ‰ Par exemple, la classe Employe peut
n une entité ne peut posséder une collection comporter l’adresse du domicile et l’adresse
d’objets
d objets insérés du travail des employés
n un objet inséré ne peut référencer un autre ‰ En ce cas, les noms des colonnes dans la
objet inséré ni avoir une association avec table de l’entité ne peuvent être les mêmes
une entité pour chacune des utilisations
‰ Un objet inséré ne peut être référencé par ‰ L’annotation @AttributeOverride peut
plusieurs entités différentes résoudre le problème
R. Grin JPA page 47 R. Grin JPA page 48

8
@AttributeOverride(s) Exemple
‰ Un champ annoté par @Embedded peut être @Entity
complété par une annotation public class Employe {
@AttributeOverride, ou plusieurs de ces @Embedded
annotations insérées dans une annotation @AttributeOverrides({
@AttributeOverrides @AttributeOverride(
name="ville",
‰ Ces annotations permettent d’indiquer le nom
column=@column(name="ville_travail")),
d’une ou de plusieurs colonnes dans la table
de l’entité @AttributeOverride(...)
})
‰ Elles peuvent aussi être utilisées si une
// Adresse du travail
classe insérée est référencée par plusieurs
private Adresse adresseTravail;
classes entités différentes
R. Grin JPA page 49 R. Grin JPA page 50

Annotation pour LOB Annotation pour énumération


‰ L’annotation @Lob permet d’indiquer qu’un ‰ Une annotation spéciale n’est pas nécessaire
attribut est un LOB (Large OBject) : soit un si un attribut est de type énumération si
CLOB (Character LOB, tel un long résumé de l’énumération est sauvegardée dans la BD
livre), soit un BLOB (Binary LOB, tel une sous la forme des numéros des constantes
i
image ou une séquence
é vidéo)
idé ) d l’é
de l’énumération
é ti (d (de 0 à n))
‰ Le fournisseur de persistance pourra ainsi ‰ Si on souhaite sauvegarder les constantes
éventuellement traiter l’attribut de façon sous la forme de la forme d’une String qui
spéciale (utilisation de flots d’entrées-sorties représente le nom de la valeur de
par exemple) l’énumération, il faut utiliser l’annotation
‰ Exemple : @Lob private byte[] image @Enumerated
R. Grin JPA page 51 R. Grin JPA page 52

Exemple Types temporels


‰ Lorsqu'une classe entité a un attribut de type
@Enumerated(EnumType.STRING) temporel (Calendar ou Date de
private TypeEmploye typeEmploye; java.util), il est obligatoire d'indiquer de
quel type temporel est cet attribut par une
annotation @Temporal
‰ C tt indication
Cette i di ti permettra
tt au ffournisseur
i d
de
persistance de savoir comment déclarer la
colonne correspondante dans la base de
données : une date (un jour), un temps sur 24
heures (heures, minutes, secondes à la
milliseconde près) ou un timeStamp (date +
heure à la microseconde près)
R. Grin JPA page 53 R. Grin JPA page 54

9
Annotation pour les types temporels Exemple
‰ 3 types temporels dans l’énumération
@Temporal(TemporalType.DATE)
TemporalType : DATE, TIME, TIMESTAMP
private Calendar dateEmb;
‰ Correspondent aux 3 types de SQL ou du
paquetage java.sql : Date, Time et
Timestamp

R. Grin JPA page 55 R. Grin JPA page 56

Tables multiples Schéma relationnel


‰ Dans le cas où le schéma relationnel est
‰ Il est possible de sauvegarder une entité sur construit automatiquement à partir des
plusieurs tables annotations, il est possible de préciser des
‰ Voir @SecondaryTable dans la spécification informations sur les tables générées ou les
JPA colonnes de ces tables
‰ C’est surtout utile pour les cas où la base de ‰ Par exemple, une contrainte d'unicité, ou "not
données existe déjà et ne correspond pas null", la longueur des colonnes de type
tout à fait au modèle objet varchar, la précision des nombres à virgule,
ou même le texte entier qui permet de définir
une colonne
‰ Voir la spécification JPA pour plus de détails
R. Grin JPA page 57 R. Grin JPA page 58

Exemple Types persistants


@Entity ‰ Les champs des entités peuvent être d’à peu
@Table(name="PARTICIPATION2", près n’importe quel type :
uniqueConstraints = ‰ Types primitifs
@UniqueConstraint( ‰ Types String, BigInteger, BigDecimal, classes
columnNames
l N = enveloppes
l d
de type primitifs,
i i if DDate (d
(des
{"EMPLOYE_ID", "PROJET_ID"}) paquetages util et sql), Calendar, Time,
) Timestamp, et plus généralement Serializable
public class Participation { ‰ Enumérations
...
‰ Entités et collections d’entités, classes
}
« Embeddable »
R. Grin JPA page 59 R. Grin JPA page 60

10
Principe de base
‰ La persistance des entités n’est pas
transparente
Gestionnaire d’entités ‰ Une instance d’entité ne devient persistante
que lorsque l’application appelle la méthode
(Entity Manager),
) GE appropriée
ié d
du gestionnaire
ti i d’entité
d’ tité (persist
( i
ou merge)
‰ Cette conception a été voulue par les
concepteurs de l’API par souci de flexibilité et
pour permettre un contrôle fin sur la
persistance des entités
R. Grin JPA page 61 R. Grin JPA page 62

Unité de persistance Configuration d’une unité


de persistance
‰ C’est une configuration nommée qui contient les
informations nécessaires à l’utilisation d’une base ‰ Les informations sur une unité de persistance
de données sont données dans un fichier
persistence.xml situé dans un sous-
‰ Elle est associée à un ensemble de classes
répertoire META INF d
META-INF d’un
un des répertoires du
entités
classpath
‰ Voir section « Configuration d’une unité de
persistance » dans la suite du cours

R. Grin JPA page 63 R. Grin JPA page 64

Contexte de persistance (1) Contexte de persistance (2)


‰ Les entités gérées par un gestionnaire d’entités ‰ Un contexte de persistance ne peut appartenir
forment un contexte de persistance qu’à une seule unité de persistance
‰ Quand une entité est incluse dans un contexte de ‰ Une unité de persistance peut contenir plusieurs
persistance (persist ou merge), l’état de l’entité contextes de persistance
est automatiquement sauvegardé dans la base ‰ C’est
C’ lla responsabilité
bili é d
de l’l’application
li i d de
au moment du commit de la transaction s’assurer qu’une entité n’appartient qu’à un seul
‰ Propriété importante : dans un contexte de contexte de persistance (afin que 2 entités de 2
persistance il n’existe pas 2 entités différentes qui contextes de persistance différents ne puissent
représentent des données identiques dans la correspondre à des données identiques dans la
base base de données)
R. Grin JPA page 65 R. Grin JPA page 66

11
Contexte de persistance - cache Contexte de persistance - cache
‰ Le contexte de persistance joue le rôle de ‰ Ce fonctionnement peut être bénéfique :
cache et évite ainsi des accès à la base meilleures performances, isolation « lecture
‰ Si le code veut récupérer des données (par répétable » sans modifier les paramètres de
un find ou un query) qui correspondent à la base de données
une entité du contexte, ce sont les données ‰ S’il est au contraire malvenu, il est possible
du cache qui sont renvoyées de récupérer des modifications effectuées en
‰ Important : si les données de la base ont été parallèle sur les données d’une entité en
modifiées (et validées) en parallèle dans la utilisation la méthode refresh de
base, les données récupérées ne tiennent EntityManager
pas compte de ces modifications
R. Grin JPA page 67 R. Grin JPA page 68

Interface EntityManager Types de GE


‰ Elle représente un GE ‰ GE géré par le container (uniquement
disponible dans un serveur d’applications ; pas
‰ Implémentation fournie par le fournisseur de
étudié dans ce cours) ; le contexte de
persistance
persistance peut être limité à une seule
transaction
‰ GE géré par l’application (seul type disponible
en dehors d’un serveur d’applications) ; le
contexte de persistance reste attaché au GE
pendant toute son existence

R. Grin JPA page 69 R. Grin JPA page 70

Cycle de vie d’un GE Fabrique de GE


‰ La classe Persistence permet d’obtenir une
‰ En dehors d’un serveur d’applications, c’est fabrique de gestionnaire d’entités par la
l’application qui décide de la durée de vie méthode createEntityManagerFactory
d’un GE ‰ 2 variantes surchargées de cette méthode :
‰ La méthode createEntityManager() de n 1 seul p
paramètre q
qui donne le nom de l’unité
la classe EntityManagerFactory créé un de persistance (définie dans le fichier
GE persistence.xml)
‰ Le GE est supprimé avec la méthode n Un 2
ème paramètre de type Map qui contient
close() de la classe EntityManager ; il des valeurs qui vont écraser les propriétés
ne sera plus possible de l’utiliser ensuite par défaut contenues dans persistence.xml

R. Grin JPA page 71 R. Grin JPA page 72

12
A savoir Mauvaise configuration
‰ Une EntityManagerFactory est « thread- ‰ Si elle rencontre une mauvaise configuration
safe » (dans le fichier persistence.xml, dans les
‰ Un EntityManager ne l’est pas annotations, y compris dans la syntaxe des
‰ Créer une EntityManagerFactory est une
requêtes nommées ou dans les fichiers XML)
opération lourde l méthode
la éth d
Persistence.createEntityManagerFactory
‰ Créer un EntityManager est une opération
ne se terminera pas correctement et lancera
légère une exception
‰ Il est donc intéressant de conserver une
EntityManagerFactory entre 2 utilisations

R. Grin JPA page 73 R. Grin JPA page 74

Méthodes de EntityManager Méthodes de EntityManager


‰ void persist(Object entité) ‰ void lock(Object entité,
‰ <T> T merge(T entité) LockModeType lockMode)
‰ void remove(Object entité) ‰ void refresh(Object entité)

‰ <T> T find(Class<T> classeEntité, ‰ void clear()


Object cléPrimaire) ‰ boolean contains(Object entité)
‰ <T> T getReference(Class<T> ‰ Query createQuery(String requête)
classeEntité, Object cléPrimaire) ‰ Query createNamedQuery(String nom)
‰ void flush()
‰ void setFlushMode(FlushModeType
flushMode)
R. Grin JPA page 75 R. Grin JPA page 76

Méthodes de EntityManager flush


‰ Query createNativeQuery(String
requête) ‰ Toutes les modifications effectuées sur les
‰ Query createNativeQuery(String
entités du contexte de persistance gérées par
requête, Class classeRésultat) le GE sont enregistrées dans la BD lors d’un
flush du GE
‰ void joinTransaction()
‰ Au moment du flush, le GE étudie ce qu’il doit
‰ void close()
faire pour chacune des entités qu’il gère et il
‰ boolean isOpen() lance les commandes SQL adaptées pour
‰ EntityTransaction getTransaction() modifier la base de données (INSERT,
UPDATE ou DELETE)

R. Grin JPA page 77 R. Grin JPA page 78

13
flush flush
‰ Soit X une des entités gérée, avec une
‰ Un flush est automatiquement effectué au association de X vers une entité Y
moins à chaque commit de la transaction en ‰ Si cette association est notée avec
cours cascade=persist ou cascade=all, Y est
‰ Une exception elle aussi flushée
TransactionRequiredException est ‰ Sinon, si Y est new ou removed, une
levée si la méthode flush est lancée en exception IllegalStateException sera
dehors d’une transaction levée et la transaction est invalidée (rollback)
‰ Sinon, si Y est détachée et X possède
l’association, Y est flushée ; si Y est la
propriétaire, le comportement est indéfini
R. Grin JPA page 79 R. Grin JPA page 80

Mode de flush persist


‰ Normalement (mode FlushMode.AUTO) un
flush des entités concernées par une requête ‰ Une entité « nouvelle » devient une entité
est effectué avant la requête pour que le gérée
résultat tienne compte des modifications ‰ L’état de l’entité sera sauvegardé dans la BD
effectuées en mémoire sur ces entités
au prochain flush ou commit
‰ Il est possible d'éviter
d éviter ce flush avec la
méthode setFlushMode : ‰ Aucune instruction ne sera nécessaire pour
em.setFlushMode(FlushMode.COMMIT); faire enregistrer au moment du commit dans
‰ En ce cas, un flush ne sera lancé qu'avant un la base de données les modifications
commit effectuées sur l’entité par l’application ; en
‰ Il est possible de modifier ce mode pour une effet le GE conserve toutes les informations
seule requête (voir Query) nécessaires sur les entités qu’il gère
R. Grin JPA page 81 R. Grin JPA page 82

persist(A) remove
‰ Si A est nouvelle, elle devient gérée
‰ Une entité gérée devient « supprimée »
‰ Si A était déjà gérée, persist est ignorée
‰ Les données correspondantes seront
mais l’opération persist « cascade » sur les
entités associées si l’association a l’attribut supprimées de la BD
yp
CascadeType.PERSIST
‰ Si A est supprimée (a été passée en
paramètre à remove), elle devient gérée
‰ Si A est détachée, une
IllegalArgumentException est lancée

R. Grin JPA page 83 R. Grin JPA page 84

14
remove(A) refresh
‰ Le GE peut synchroniser avec la BD une
‰ Si A est gérée, elle devient « supprimée »
entité qu’il gère en rafraichissant son état en
(les données correspondantes de la base
mémoire avec les données actuellement
seront supprimées de la base au moment du
dans la BD :
flush du contexte de persistance)
em.refresh(entite);
( );
‰ Ignorée si A est nouvelle ou supprimée
‰ Les données de la BD sont copiées dans
‰ Si A est détachée, une
l’entité
IllegalArgumentException est lancée
‰ Utiliser cette méthode pour s’assurer que
‰ Ne peut être utilisé que dans le contexte l’entité a les mêmes données que la BD
d’une transaction
‰ Peut être utile pour les transactions longues

R. Grin JPA page 85 R. Grin JPA page 86

refresh(A) find

‰ Ignorée si A est nouvelle ou supprimée ‰ La recherche est polymorphe : l'entité


‰ Si A est nouvelle, l’opération « cascade » sur récupérée peut être de la classe passée en
les associations qui ont l’attribut paramètre ou d'une sous-classe (renvoie null
CascadeType.REFRESH si aucune entité n’a l’identificateur passé en
‰ Si A est détachée, une paramètre)
IllegalArgumentException est lancée ‰ Exemple :
Article p =
em.find(Article.class, 128);
peut renvoyer un article de n'importe quelle
sous-classe de Article (Stylo, Ramette,…)
R. Grin JPA page 87 R. Grin JPA page 88

lock(A) Entité détachée (1)


‰ Le fournisseur de persistance gère les accès ‰ Une application distribuée sur plusieurs
concurrents aux données de la BD ordinateurs peut utiliser avec profit des
représentées par les entités avec une entités détachées
stratégie optimiste ‰ Une entité gérée par un GE peut être
‰ lock permet de modifier
f la manière
è de géreré détachée de son contexte de persistance ;
les accès concurrents à une entité A par exemple, si le GE est fermé ou si l’entité
‰ Sera étudié plus loin dans la section sur la est transférée sur une autre machine en
concurrence dehors de la portée du GE

R. Grin JPA page 89 R. Grin JPA page 90

15
Entité détachée (2) merge(A)
‰ Renvoie une entité gérée A’ ; plusieurs cas :
‰ Une entité détachée peut être modifiée
‰ Si A est une entité détachée, son état est copié
‰ Pour que ces modifications soient
dans une entité gérée A’ qui a la même identité
enregistrées dans la BD, il est nécessaire de que A (si A’ n’existe pas déjà, il est créé)
rattacher l’entité à un GE (pas
‰ Si A est nouvelle,
nouvelle une nouvelle entité gérée A' A
nécessairement celui d’où elle a été
détachée) par la méthode merge est créé et l’état de A est copié dans A’ (un id
automatique ne sera mis dans A’ qu’au commit)
‰ Si A est déjà gérée, merge renvoie A ; en plus
merge « cascade » pour tous les associations
avec l’attribut CascadeType.MERGE

R. Grin JPA page 91 R. Grin JPA page 92

merge(A) merge(A)
‰ Attention, la méthode merge n’attache pas A
‰ Si A a été marquée « supprimée » par la
‰ Elle retourne une entité gérée qui a la même
méthode remove, une
IllegalArgumentException est lancée identité dans la BD que l’entité passée en
paramètre, mais ça n’est pas le même objet
((sauf si A était déjà
j ggérée))
‰ Après un merge, l’application devra donc, sauf
cas exceptionnel, ne plus utiliser l’objet A ; on
pourra avoir ce type de code :
a = em.merge(a);
l’objet anciennement pointé par a ne sera plus
référencé
R. Grin JPA page 93 R. Grin JPA page 94

En dehors d’une transaction (1) En dehors d’une transaction (2)


‰ Les méthodes suivantes (read only) peuvent ‰ En fait, certains SGBD se mettent en mode
être lancées en dehors d’une transaction : autocommit lorsque des modifications sont
find, getReference, refresh et requêtes effectuées sur des entités gérées en dehors
(query)
d’une transaction, ce qui peut poser de sérieux
‰ Les méthodes persist, remove, merge
problèmes (en cas de rollback de la
peuvent être exécutées en dehors d’une
d une transaction par l’application, ces modifications
transaction ; les modifications qu’elles ont
provoquées seront enregistrées par un flush ne seront pas invalidées)
dès qu’une transaction est active ‰ Il est donc conseillé de n’effectuer les
‰ Les méthodes flush, lock et modifications modifications sur les entités gérées que dans
de masse (executeUpdate) ne peuvent être le contexte d’une transaction, ou au moins de
lancées en dehors d’une transaction tester le comportement du SGBD
R. Grin JPA page 95 R. Grin JPA page 96

16
Transaction non terminée
‰ Il ne faut jamais oublier de terminer une
transaction par commit() ou rollback()
car le résultat dépend du fournisseur de
persistance et du SGBD Identité des entités

R. Grin JPA page 97 R. Grin JPA page 98

Clé primaire Annotation


‰ Une entité doit avoir un attribut qui correspond ‰ L’attribut clé primaire est désigné par
à la clé primaire dans la table associée l’annotation @Id
‰ La valeur de cet attribut ne doit jamais être ‰ Pour une clé composite on utilise
modifiée par l’application dès que l’entité @EmbeddedId ou @IdClass
correspond à une ligne de la base
‰ Cet attribut doit être défini dans l’entité racine
d’une hiérarchie d’héritage (uniquement à cet
endroit dans toute la hiérarchie d’héritage)
‰ Une entité peut avoir une clé primaire
composite (pas recommandé)
R. Grin JPA page 99 R. Grin JPA page 100

Type de la clé primaire Génération automatique de clé


‰ Le type de la clé primaire (ou des champs ‰ Si la clé est de type numérique entier,
d’une clé primaire composée) doit être un des l’annotation @GeneratedValue indique que
types suivants : la clé primaire sera générée automatiquement
n type primitif Java par le SGBD
n classe qui enveloppe un type primitif ‰ Cette annotation peut avoir un attribut
n java.lang.String strategy qui indique comment la clé sera
n java.util.Date
générée (il prend ses valeurs dans
l’énumération GeneratorType)
n java.sql.Date

‰ Ne pas utiliser les types numériques non


entiers
R. Grin JPA page 101 R. Grin JPA page 102

17
Types de génération Précisions sur la génération
‰ AUTO : le type de génération est choisi par le
‰ Les annotations @SequenceGenerator et
fournisseur de persistance, selon le SGBD @TableGenerator permettent de donner
(séquence, table,…) ; valeur par défaut
plus de précisions sur la séquence ou la table
‰ SEQUENCE : utilise une séquence est utilisée qui va permettre de générer la clé
‰ IDENTITY : une colonne de type IDENTITY ‰ Elles définissent des générateurs
est utilisée d'identificateur qui pourront être utilisés dans
‰ TABLE : une table qui contient la prochaine toute l'unité de persistance, et pas seulement
valeur de l’identificateur est utilisée dans la classe dans laquelle elles sont
‰ On peut aussi préciser le nom de la séquence
ou de la table avec l’attribut generator
R. Grin JPA page 103 R. Grin JPA page 104

Générateurs d’identificateurs Exemple de générateur


‰ Les annotations @SequenceGenerator et @SequenceGenerator(
@TableGenerator peuvent annoter name = "emp_seq",
l'identificateur de l'entité ou l'entité elle-même sequence_name = "emp_seq",
qui les utilise, ou même une autre entité allocation_size = 10,
‰ Si le générateur ne sert que dans une seule i iti lV l
initialValue = 600)
classe, il vaut mieux mettre l’annotation avec
l’annotation @Id de la classe
‰ Sinon, une entrée dans un des fichiers XML
de configuration de JPA, plutôt qu’une
annotation, peut être un bon emplacement
R. Grin JPA page 105 R. Grin JPA page 106

Exemple d'utilisation du générateur Valeur d'incrément d'une séquence


@Id ‰ Si une séquence utilisée par JPA est créée
@GeneratedValue( en dehors de JPA, il faut que la valeur de
strategy = GenerationType.SEQUENCE, pré-allocation de JPA (égale à 50 par défaut)
generator = "emp_seq") corresponde à la valeur d’incrémentation de
public
bli llong getId()
tId() { Identificateur
d ifi l séquence
la é ; on aura alors
l souventt ce ttype
return id; du générateur d’annotation :
} @SequenceGenerator(
name="seq3", sequenceName="seq3",
initialValue="125, allocationSize="20")

R. Grin JPA page 107 R. Grin JPA page 108

18
persist et id automatique Clé composite
‰ La spécification n’impose rien sur le moment
‰ Pas recommandé, mais une clé primaire peut
où la valeur de l’identificateur est mise dans
l’objet géré être composée de plusieurs colonnes
‰ Peut arriver quand la BD existe déjà ou
‰ La seule assurance est qu’après un flush
dans la base de données (donc un commit) quand la classe correspond à une table
l’identificateur aura déjà reçu sa valeur association (association M:N)
‰ 2 possibilités :
‰ Avec TopLink et Oracle (et peut-être avec
n @IdClass
d’autres produits), la valeur de l’identificateur
est mise dès l’appel de persist, sans n @EmbeddedId et @Embeddable

attendre le commit, mais il est risqué pour la


portabilité de l’utiliser
R. Grin JPA page 109 R. Grin JPA page 110

Classe pour la clé composite @EmbeddedId


‰ Dans les 2 cas, la clé primaire doit être ‰ @EmbeddedId correspond au cas où la
représentée par une classe Java dont les classe entité comprend un seul attribut
attributs correspondent aux composants de la annoté @EmbeddedId
clé primaire ‰ La classe clé primaire est annoté par
‰ La classe doit être
ê public, posséderé un @Embeddable
constructeur sans paramètre, être sérialisable ‰ Le type d’accès (par champs ou propriétés)
et redéfinir equals et hashcode de la classe « embeddable » doit être le
même que celui de l’entité dont la clé
primaire est définie

R. Grin JPA page 111 R. Grin JPA page 112

Exemple avec @EmbeddedId @IdClass


@Entity
public class Employe { ‰ @IdClass correspond au cas où la classe
@EmbeddedId entité comprend plusieurs attributs annotés
private EmployePK employePK; par @Id
...
‰ La classe entité est annotée par @IdClass
}
qui prend en paramètre le nom de la classe
@Embeddable
public class EmployePK {
clé primaire
private String nom; ‰ La classe clé primaire n’est pas annotée ; ses
private Date dateNaissance; attributs ont les mêmes noms et mêmes
... types que les attributs annotés @Id dans la
} classe entité
R. Grin JPA page 113 R. Grin JPA page 114

19
Exemple avec @IdClass Quelle solution choisir ?
@Entity
@IdClass(EmployePK) ‰ @IdClass existe pour assurer une
public class Employe { compatibilité avec la spécification EJB 2.0
@Id private String nom;
‰ Quand c'est possible il vaut mieux utiliser
@Id Date dateNaissance;
@EmbeddedId
...
} ‰ @IdClass a aussi son utilité, par exemple,
public class EmployePK { pour les associations M:N si on veut que la
private String nom; clé primaire de la table association soit
private Date dateNaissance; composée des clés étrangères vers les tables
... qui sont associées (voir section sur les
} associations)
R. Grin JPA page 115 R. Grin JPA page 116

Généralités
‰ Une association peut être uni ou
bidirectionnelle
‰ Elle peut être de type 1:1, 1:N, N:1 ou M:N
Associations ‰ Les associations doivent être indiquées
q p
par
une annotation sur la propriété
correspondante, pour que JPA puisse les
gérer correctement

R. Grin JPA page 117 R. Grin JPA page 118

Exemple Représentation des associations


1:N et M:N
@ManyToOne
public Departement getDepartement() { ‰ Elles sont représentées par des collections ou
... maps qui doivent être déclarées par un des
}
types interface suivants (de java.util) :
n Collection

n Set

n List

n Map

‰ Les variantes génériques sont conseillées ;


par exemple Collection<Employe>
R. Grin JPA page 119 R. Grin JPA page 120

20
Types à utiliser Ordre dans les collections
‰ Le plus souvent Collection sera utilisé ‰ L’ordre d’une liste n'est pas nécessairement
‰ Set peut être utile pour éliminer les doublons préservé dans la base de données
‰ Les types concrets, tels que HashSet ou ‰ De plus, l’ordre en mémoire doit être
ArrayList, ne peuvent être utilisés que pour maintenu par le code (pas automatique)
des entités « nouvelles » ; dès que l’entité est ‰ Tout ce qu’on peut espérer est de récupérer
gérée, les types interfaces doivent être utilisés les entités associées dans la liste avec un
(ce qui permet au fournisseur de persistance certain ordre lors de la récupération dans la
d’utiliser son propre type concret) base, en utilisant l’annotation @OrderBy
‰ List peut être utilisé pour conserver un ordre
mais nécessite quelques précautions
R. Grin JPA page 121 R. Grin JPA page 122

@OrderBy
Exemples
‰ Cette annotation indique dans quel ordre sont
récupérées les entités associées @Entity
‰ Il faut préciser un ou plusieurs attributs qui public class Departement {
déterminent l'ordre ...
@OneToMany(mappedBy="departement")
‰ Chaque attribut peut être précisé par ASC ou
@O d B ("
@OrderBy("nomEmploye")
E l ")
DESC (ordre ascendant ou descendant);
ASC par défaut public List<Employe> getEmployes() {
...
‰ Les différents attributs sont séparés par une
virgule
@OrderBy("poste DESC, nomEmploye ASC")
‰ Si aucun attribut n'est précisé, l'ordre sera
celui de la clé primaire
R. Grin JPA page 123 R. Grin JPA page 124

Associations bidirectionnelles Bout propriétaire


‰ Le développeur est responsable de la gestion ‰ Pour les associations autres que M:N ce bout
correcte des 2 bouts de l’association correspond à la table qui contient la clé
‰ Par exemple, si un employé change de étrangère qui traduit l’association
département, les collections des employés ‰ Pour les associations M:N le développeur
des départements concernés doivent être peut choisir arbitrairement le bout propriétaire
modifiées ‰ L’autre bout (non propriétaire) est qualifié par
‰ Un des 2 bouts est dit « propriétaire » de l’attribut mappedBy qui donne le nom de
l’association l’attribut dans le bout propriétaire qui
correspond à la même association

R. Grin JPA page 125 R. Grin JPA page 126

21
Exemple Méthode de gestion de l’association
‰ Pour faciliter la gestion des 2 bouts d'une
‰ Dans la classe Employe :
association le code peut comporter une
@ManyToOne
public Departement getDepartement() { méthode qui effectue tout le travail
return departement; ‰ En particulier, dans les associations 1-N, le
} bout « 1 » peut comporter ce genre de
‰ Dans la classe Departement : méthode (dans la classe Departement d’une
@OneToMany(mappedBy="departement") association département-employé) :
public Collection<Employe> getEmployes() { public void ajouterEmploye(Employe e) {
return employes; this.employes.add(e);
} employe.setDept(this);
}

R. Grin JPA page 127 R. Grin JPA page 128

Annotation @JoinColumn Exemple


‰ Cette annotation donne le nom de la colonne ‰ Pour l'association qui détermine le
clé étrangère qui représente l'association département d'un employé
dans le modèle relationnel ‰ Par défaut, la colonne clé étrangère placée
‰ Elle doit être mise du côté propriétaire (celui dans la table EMPLOYE s'appellera
qui contient la clé étrangère) Departement ID
Departement_ID
‰ Sans cette annotation, le nom est défini par ‰ Pour changer ce nom (dans la classe
défaut : Employe) :
@ManyToOne
<entité_but>_<clé_primaire_entité_but> @JoinColumn(name="DEPT_ID")
public Departement getDepartement() {

R. Grin JPA page 129 R. Grin JPA page 130

Annotation @JoinColumns Exemple


‰ L’annotation @JoinColumns permet @JoinColumns({
d’indiquer le nom des colonnes qui @JoinColumn(name="n1",
constituent la clé étrangère dans le cas où il y referencedColumnName="c1"),
en a plusieurs (si la clé primaire référencée @JoinColumn(name="n2",
contient
ti t plusieurs
l i colonnes)
l ) referencedColumnName="c2")
f dC l N " 2")
‰ En ce cas, les annotations @JoinColumn })
doivent nécessairement comporter un attribut
referencedColumnName pour indiquer
quelle colonne est référencée (parmi les
colonnes de la clé primaire référencée)
R. Grin JPA page 131 R. Grin JPA page 132

22
Persistance par transitivité Pas si simple
‰ Maintenir une cohérence automatique des
‰ Un service de persistance implémente la valeurs persistantes n’est pas si simple
persistance par transitivité (reachability) si
‰ Par exemple, si un objet devient non
une instance devient automatiquement
persistant, faut-il aussi rendre non persistants
persistante lorsqu’elle est référencée par une
tous objets
j q
qu'il a rendu p
persistants p
par
i t
instance déjà persistante
i t t
transitivité ?
‰ C’est un comportement logique : un objet
‰ De plus, le service de persistance doit alors
n’est pas vraiment persistant si une partie
examiner toutes les références des objets qui
des valeurs de ses propriétés n’est pas
sont rendus persistants, et ce de façon
persistante
récursive, ce qui peut nuire grandement aux
performances
R. Grin JPA page 133 R. Grin JPA page 134

Le choix de JPA Cohérence des données


‰ Par défaut, JPA n’effectue pas de persistance ‰ Si le code a mal géré cette cohérence, une
par transitivité automatique : rendre exception est lancée
persistant un objet ne suffit pas à rendre ‰ Par exemple, si un département est rendu
automatiquement et immédiatement persistant alors que la collection des
persistants
i t t tous
t les
l objets
bj t qu’il
’il référence
éfé employés du département contient des
‰ Comme la cohérence n'est pas gérée employés non persistants, une
automatiquement, c'est le code de IllegalStateException va être lancée
l’application qui se doit de conserver cette au moment du commit, et la transaction va
cohérence, au moins au moment du commit être invalidée (rollback)

R. Grin JPA page 135 R. Grin JPA page 136

Persistance automatique Attribut cascade


‰ Afin de faciliter le maintien de cette ‰ Les annotations qui décrivent les associations
cohérence, il est possible d’indiquer à JPA entre objets peuvent avoir un attribut cascade
que les objets associés à un objet persistant pour indiquer que certaines opérations du GE
doivent être automatiquement rendus doivent être appliquées aux objets associés
persistants
i t t ‰ Ces opérations sont PERSIST, REMOVE,
‰ Pour cela il suffit d’ajouter un attribut REFRESH et MERGE ; ALL correspond à toutes
« cascade » dans les informations de ces opérations
mapping de l’association ‰ Par défaut, aucune opération n’est appliquée
transitivement

R. Grin JPA page 137 R. Grin JPA page 138

23
Exemples Association 1:1
‰ @OneToMany( ‰ Annotation @OneToOne
cascade=CascadeType.PERSIST)
‰ Représentée par une clé étrangère ajoutée
‰ @OneToMany(
cascade={CascadeType.PERSIST,
dans la table qui correspond au côté
CascadeType.MERGE})
yp propriétaire
‰ Exemple :
@OneToOne
public Adresse getAdresse() {

}

R. Grin JPA page 139 R. Grin JPA page 140

Association 1:1 sur les clés Exemple


‰ 2 classes peuvent être reliées par leur @OneToOne
identificateur : 2 entités sont associées ssi elles @PrimaryKeyJoinColumn
ont les mêmes clés private Employe employe
‰ L’annotation @PrimaryKeyJoinColumn doit
alors être utilisée pour indiquer au fournisseur
de persistance qu’il ne faut pas utiliser une clé
étrangère à part pour représenter l’association
‰ Attention, c’est au développeur de s’assurer que
les entités associées ont bien les mêmes clés

R. Grin JPA page 141 R. Grin JPA page 142

Associations 1:N et N:1 Exemple


‰ class Employe {
‰ Annotations @OneToMany et @ManyToOne ...
@ManyToOne
‰ Représentée par une clé étrangère dans la public Departement getDepartement() {
table qui correspond au côté propriétaire ...
(obligatoirement le côté « Many ») }
}
‰ class Departement {
...
@OneToMany(mappedBy="departement")
public List<Employe> getEmployes() {
...
}
}

R. Grin JPA page 143 R. Grin JPA page 144

24
Cas particulier 1:N unidirectionnelle
‰ Une association unidirectionnelle 1:N est public class Dept {
traduite pas une table association ...
‰ Si on traduisait l’association par une clé @OneToMany
étrangère dans la table du côté « N », ça @JoinTable(name="DEPT_EMP",
poserait un problème pour rendre une j i C l
joinColumns=@JoinColumn(name="DEPT_ID"),
@J i C l ( "DEPT ID")
instance du côté « N » persistante : que inverseJoinColumns=
mettre dans la clé primaire ? Avec la table @JoinColumn(name="EMP_ID"))
association, c’est seulement quand l’instance private Collection<Employe> employes;
du côté « 1 » sera rendu persistant qu’une ...
ligne sera ajoutée dans la table association }

R. Grin JPA page 145 R. Grin JPA page 146

Association M:N Association M:N (1)


‰ Annotation @ManyToMany ‰ Les valeurs par défaut :
n le nom de la table association est la
‰ Représentée par une table association
concaténation des 2 tables, séparées par
«_»
n les
l noms d des colonnes
l clés
lé éétrangères
è sont
les concaténations de la table référencée,
de « _ » et de la colonne « Id » de la table
référencée

R. Grin JPA page 147 R. Grin JPA page 148

Association M:N (2) @JoinTable


‰ Si les valeurs par défaut ne conviennent pas, ‰ Donne des informations sur la table
le côté propriétaire doit comporter une association qui va représenter l’association
annotation @JoinTable ‰ Attribut name donne le nom de la table
‰ L’autre côté doit toujours comporter l’attribut ‰ Attribut joinColumns donne les noms des
mappedBy
dB colonnes de la table qui référencent les clés
primaires du côté propriétaire de l’association
‰ Attribut inverseJoinColumns donne les
noms des colonnes de la table qui
référencent les clés primaires du côté qui
n’est pas propriétaire de l’association

R. Grin JPA page 149 R. Grin JPA page 150

25
Exemple (classe Employe) Exemple (classe Projet)
@ManyToMany @ManyToMany(mappedBy="projets")
@JoinTable( public Collection<Employe> getEmps() {
name="EMP_PROJET"
joinColumns=@JoinColumn(name="matr")
i
inverseJoinColumns=
J i C l
@JoinColumn(name="codeProjet")
)
public Collection<Projet> getProjets() {

R. Grin JPA page 151 R. Grin JPA page 152

Association M:N avec information Classe association pour une


portée par l'association association M:N
‰ Une association M:N peut porter une ‰ L'association sera traduite par une classe
information association
‰ Exemple : ‰ 2 possibilités pour cette classe, suivant
Association entre les employés et les projets qu'elle contient ou non un attribut
Un employé a une (et une seule) fonction identificateur (@Id) unique
dans chaque projet auquel il participe ‰ Le plus simple est de n'avoir qu'un seul
‰ En ce cas, il n'est pas possible de traduire attribut identificateur
l'association en ajoutant 2 collections (ou
maps) comme il vient d'être décrit
R. Grin JPA page 153 R. Grin JPA page 154

Exemple - 1 identificateur Exemple - 1 identificateur


‰ Si le schéma relationnel est généré d’après les
‰ Association entre les employés et les projets
informations de mapping par les outils associés
‰ Cas d'un identificateur unique : la classe au fournisseur de persistance, on peut ajouter
association contient les attributs id (int), une contrainte d'unicité sur (EMPLOYE_ID,
p y ((Employe),
employe p y ) projet
p j ((Projet)j ) et PROJET ID) qui traduit le fait qu'un
PROJET_ID) qu un employé
fonction (String) ne peut avoir 2 fonctions dans un même projet :
‰ L'attribut id est annoté par @Id @Entity
@Table(uniqueConstraints=@UniqueConstrain
‰ Les attributs employe et projet sont
t(columnNames={"EMPLOYE_ID","PROJET_ID"})
annotés par @ManyToOne public class Participation {

R. Grin JPA page 155 R. Grin JPA page 156

26
Exemple - 2 identificateurs (1) Exemple - 2 identificateurs (2)
‰ Si la base de données existe déjà, il sera ‰ En ce cas, la solution est plus complexe, et
fréquent de devoir s'adapter à une table pas toujours portable dans l'état actuel de la
association qui contient les colonnes spécification JPA
suivantes (pas de colonne id): ‰ La solution donnée dans les transparents
n employe_id, clé ééétrangère
è vers EMPLOYE suivants convient pour TopLink Essentials et
n projet_id, clé étrangère vers PROJET Hibernate, les 2 fournisseurs de persistance
fonction
n
les plus utilisés ; elle n’a pas été testée sur
d’autres fournisseurs
‰ et qui a (employe_id, projet_id) comme
clé primaire

R. Grin JPA page 157 R. Grin JPA page 158

Exemple - 2 identificateurs Exemple - 2 identificateurs


‰ La difficulté vient de l'écriture de la classe ‰ Pour éviter les conflits au moment du flush,
Participation les colonnes qui correspondent aux
‰ L'idée est de dissocier la fonction identificateurs sont marqués non modifiables
d'identificateur des attributs employeId
p y et ni insérables (pas de persistance dans la BD)
projetId de leur rôle dans les associations ‰ En effet, leur valeur sera mise par le mapping
avec les classes Projet et Employe des associations 1-N vers Employe et Projet
qui sera traduite par 2 clés étrangères

R. Grin JPA page 159 R. Grin JPA page 160

Classes Employe et Projet Classe Participation


‰ @Entity public class Employe { (2 choix pour Id)
@Id public int getId() { ... }
@OneToMany(mappedBy="employe") ‰ On peut utiliser une classe « Embeddable »
public Collection<Participation> ou une « IdClass » pour représenter la clé
getParticipations() { ... }
. . .
composite de Participation
} ‰ Le code suivant utilise une « IdClass »
‰ @Entity public class Projet {
@Id public int getId() { ... }
@OneToMany(mappedBy="projet")
public Collection<Participation>
getParticipations() { ... }
. . .
}
R. Grin JPA page 161 R. Grin JPA page 162

27
Classe Participation (Id) Classe Participation (champs)
@Entity private Employe employe;
@IdClass(ParticipationId.class) private long employeId;
public class Participation { private Projet projet;
// Les identificateurs "read-only" private long projetId;
@Id private String fonction;
@Column(name="EMPLOYE_ID", public Participation() { }
insertable="false", updatable="false")
public int getEmployeId() { … }
@Id
@Column(name="PROJET_ID",
insertable="false", updatable="false")
public int getProjetId() { … }
R. Grin JPA page 163 R. Grin JPA page 164

Participation (constructeurs) Établir une association


public Participation() { }
‰ Il faut éviter la possibilité qu’un bout
// Constructeur pour faciliter une
seulement de l’association soit établie et pour
// bonne gestion des liens
cela, il faut qu’une seule classe s’en charge
public Participation(Employe e,
Projet
j p) { ‰ Pour cela, on peut faire gérer l’association
this.employe = e;
entière
è par Projet, par Employe ou par
Participation
this.projet = p;
e.getParticipations().add(this); ‰ Le transparent suivant montre comment la
p.getParticipations().add(this); faire gérer par le constructeur de
} Participation

R. Grin JPA page 165 R. Grin JPA page 166

Participation (constructeurs) Participation (associations)


public Participation() { } // Pour JPA // Les associations
public Participation(Employe employe, @ManyToOne
Projet projet,
public Employe getEmploye() {
String fonction) {
return employe;
this.employe
p y = employe;
p y
}
this.employeId = employe.getId();
this.projet = projet; @ManyToOne
this.projetId = projet.getId(); public Projet getProjet() {
employe.getParticipations.add(this); return projet;
projet.getParticipations.add(this); }
this.fonction = fonction; . . .
} }
R. Grin JPA page 167 R. Grin JPA page 168

28
Classe ParticipationId Récupération des entités associées
public class ParticipationId ‰ Lorsqu’une entité est récupérée depuis la
implements Serializable { base de données par une requête (Query) ou
private int employeId; par un find, est-ce que les entités associées
private int projetId;
doivent être elles aussi récupérées ?
public int g
p getEmployeId()
p y { ... }
public void setEmployeId(int employeId) ‰ Si elles sont récupérées, est-ce que les entités
{ ... } associées à ces entités doivent elles aussi
public int getProjetId() { ... } être récupérées ?
public void setProjetId(int projetId)
‰ On voit que le risque est de récupérer un très
{ ... } grand nombre d’entités qui ne seront pas
// Redéfinir aussi equals et hasCode utiles pour le traitement en cours
R. Grin JPA page 169 R. Grin JPA page 170

EAGER ou LAZY Récupération retardée (LAZY)


‰ JPA laisse le choix de récupérer ou non ‰ Dans le cas où une entité associée n’est pas
immédiatement les entités associées, suivant récupérée immédiatement, JPA remplace
les circonstances l’entité par un « proxy », objet qui permettra
‰ Il suffit de choisir le mode de récupération de
de récupérer l’entité plus tard si besoin est
ll’association
association (LAZY ou EAGER) ‰ Ce proxy contient la clé primaire qui

‰ Une requête sera la même, quel que soit le correspond à l’entité non immédiatement
mode de récupération récupérée
‰ Dans le mode LAZY les données associées ‰ Il est possible de lancer une requête avec

ne sont récupérées que lorsque c’est une récupération immédiate, même si une
vraiment nécessaire association est en mode LAZY (join fetch de
JPQL étudié plus loin)
R. Grin JPA page 171 R. Grin JPA page 172

Comportement par défaut de JPA Indiquer le type de récupération


des entités associées
‰ Par défaut, JPA ne récupère immédiatement
‰ L’attribut fetch d’une association permet
que les entités associées par des
associations dont le but est « One » (une d’indiquer une récupération immédiate des
entités associées (FetchType.EAGER) ou une
seule entité à l’autre bout) : OneToOne et
récupération
p retardée ((FetchType.LAZY)
yp ) si le
M
ManyToOne
T O (mode
( d EAGER)
comportement par défaut ne convient pas
‰ Pour les associations dont le but est
‰ Exemple :
« Many » (une collection à l’autre bout), @OneToMany(mappedBy="departement",
OneToMany et ManyToMany, par défaut, les fetch=FetchType.EAGER)
entités associées ne sont pas récupérées public Collection<Employe>
immédiatement (mode LAZY) getEmployes()

R. Grin JPA page 173 R. Grin JPA page 174

29
Mode de récupération
Map pour association
des attributs
‰ Les attributs aussi peuvent être récupérés en
‰ A la place d’une collection il est possible
mode « retardé »
d’utiliser une map
‰ Le mode de récupération par défaut est le
‰ En ce cas il faut annoter l’association avec
mode EAGER pour les attributs (ils sont @MapKey qui précise la clé de la map qui doit
chargés en même temps que l’entité)
l entité)
être un des attributs de l’entité contenue dans
‰ Si un attribut est d’un type de grande la map
dimension (LOB), il peut aussi être marqué par
‰ La classe de la clé doit avoir sa méthode
@Basic(fetch=FetchType.LAZY)
hashCode compatible avec sa méthode
(à utiliser avec parcimonie) equals et chaque clé doit être unique parmi
‰ Cette annotation n’est qu’une suggestion au toutes les autres clés de la map
GE, qu’il peut ne pas suivre
R. Grin JPA page 175 R. Grin JPA page 176

Map pour association Exemple


‰ Si l’association est traduite par une map mais ‰ Les employés d’un département peuvent être
n’a pas d’annotation @KeyMap, la clé sera enregistrés dans une map dont les clés sont
considérée être l’identificateur de l’entité les noms des employés (on suppose que 2
employés n’ont pas le même nom)
public class Departement {
...
@OneToMany(mappedBy="nom")
public Map<String,Employe> getEmployes(){
...
}
R. Grin JPA page 177 R. Grin JPA page 178

Stratégies
‰ A ce jour, les implémentations de JPA doivent
obligatoirement offrir 2 stratégies pour la
traduction de l’héritage :
Héritage n une seule table pour une hiérarchie
d’héritage
d héritage (SINGLE
(SINGLE_TABLE)
TABLE)
n une table par classe ; les tables sont jointes
pour reconstituer les données (JOINED)
‰ La stratégie « une table distincte par classe
concrète » est seulement optionnelle
(TABLE_PER_CLASS)
R. Grin JPA page 179 R. Grin JPA page 180

30
Une table par hiérarchie Exemple
@Entity A mettre dans la
‰ Sans doute la stratégie la plus utilisée classe racine de
‰ Valeur par défaut de la stratégie de traduction
@Inheritance(strategy= la hiérarchie
InheritanceType.SINGLE_TABLE)
de l’héritage
public abstract class Personne {...}
‰ Elle est p
performante et p
permet le
polymorphisme @Entity
« Employe » par défaut
‰ Mais elle induit beaucoup de valeurs NULL @DiscriminatorValue("E")
dans les colonnes si la hiérarchie est public class Employe extends Personne {
complexe ...
}

R. Grin JPA page 181 R. Grin JPA page 182

Nom de la table Colonne discriminatrice (1)


‰ Si on choisit la stratégie « une seule table ‰ Une colonne de la table doit permettre de
pour une arborescence d’héritage » la table a différencier les lignes des différentes classes
le nom de la table associée à la classe racine de la hiérarchie d’héritage
de la hiérarchie ‰ Cette colonne est indispensable pour le bon
fonctionnement des requêtes qui se limitent à
une sous-classe
‰ Par défaut, cette colonne se nomme DTYPE
et elle est de type Discriminator.STRING
de longueur 31 (autres possibilités pour le
type : CHAR et INTEGER)
R. Grin JPA page 183 R. Grin JPA page 184

Colonne discriminatrice (2) Exemple


‰ L’annotation @DicriminatorColumn @Entity
permet de modifier les valeurs par défaut @Inheritance
‰ Ses attributs : @DiscriminatorColumn(
name="TRUC",
n name
discriminatorType="STRING",
yp ,
n discriminatorType length=5)
n columnDefinition fragment SQL pour public class Machin {
créer la colonne …
}
n length longueur dans le cas où le type

est STRING (31 par défaut)

R. Grin JPA page 185 R. Grin JPA page 186

31
Valeur discriminatrice Une table par classe
‰ Toutes les classes, même les classes
‰ Chaque classe est différenciée par une abstraites, sont représentées par une table
valeur de la colonne discriminatrice ‰ Nécessite des jointures pour retrouver les
‰ Cette valeur est passée en paramètre de propriétés d’une instance d’une classe
l’annotation @DiscriminatorValue ‰ Une colonne discriminatrice est ajoutée
j dans
‰ Par défaut cette valeur est le nom de la la table qui correspond à la classe racine de
classe la hiérarchie d’héritage
‰ Cette colonne permet de simplifier certaines
requêtes ; par exemple, pour retrouver les
noms de tous les employés (classe Personne
à la racine de la hiérarchie d’héritage)
R. Grin JPA page 187 R. Grin JPA page 188

Exemple Une table par classe concrète


@Entity ‰ Stratégie seulement optionnelle
@Inheritance(strategy=
‰ Pas recommandé car le polymorphisme est
InheritanceType.JOINED)
public abstract class Personne {...}
plus complexe à obtenir (voir cours sur le
mapping objet-relationnel)
@Entity ‰ Chaque classe concrète correspond à une
@DiscriminatorValue("E") seule table totalement séparée des autres
public class Employe extends Personne { tables
...
‰ Toutes les propriétés de la classe, même celles
}
qui sont héritées, se retrouvent dans la table

R. Grin JPA page 189 R. Grin JPA page 190

Exemple Entité abstraite


‰ @Entity ‰ Une classe abstraite peut être une entité
@Inheritance(strategy= (annotée par @Entity)
InheritanceType.TABLE_PER_CLASS)
public abstract class Personne {...} ‰ Son état sera persistant et sera utilisé par les
‰ @Entity
y sous-classes entités
@Table(name=EMPLOYE) ‰ Comme toute entité, elle pourra désigner le
public class Employe extends Personne { type retour d’une requête (query) pour une
...
requête polymorphe
}

R. Grin JPA page 191 R. Grin JPA page 192

32
Compléments Classe mère persistante
‰ L’annotation @Entity ne s’hérite pas ‰ Une entité peut aussi avoir une classe mère
‰ Les sous-classes entités d’une entité doivent dont l’état est persistant, sans que cette
être annotée par @Entity classe mère ne soit une entité
‰ Une entité p
peut avoir une classe mère q qui ‰ En ce cas, la classe mère doit être annotée
n’est pas une entité ; en ce cas, l’état de cette par @MappedSuperclass
classe mère ne sera pas persistant ‰ L’état de cette classe mère sera rendu
persistant avec l’état de la classe entité fille,
dans la même table que cette classe entité

R. Grin JPA page 193 R. Grin JPA page 194

Classe mère persistante Exemple


‰ Cette classe mère n’est pas une entité ‰ Si toutes les entités ont des attributs pour
‰ Donc elle ne pourra pas être renvoyée par enregistrer la date de la dernière modification
une requête, ne pourra pas être passée en et le nom de l’utilisateur qui a effectué cette
paramètre d’une méthode d’un modification, il peut être intéressant d’avoir
EntityManager ou d’un ’ Query et ne pourra une classe abstraite Base,
B mère de toutes les
être le but d’une association entités qui contient ces attributs
‰ Cette classe mère sera annotée avec
@MappedSuperclass

R. Grin JPA page 195 R. Grin JPA page 196

Code de l’exemple Classe mère « non persistante »


@MappedSuperclass
public abstract class Base { ‰ Une classe entité peut aussi hériter d’une
@Id @GeneratedValue classe mère dont l’état n’est pas persistant
private Long Id; ‰ En ce cas, l’état hérité de la classe mère ne
@Version sera pas persistant
private
i t I Integer
t g version;
i
‰ La classe mère ne comportera aucune
@ManyToOne
private User user; annotation particulière liée à la persistance
@Temporal(value = TemporalType.TIMESTAMP)
private Date dateModif;
...
}
R. Grin JPA page 197 R. Grin JPA page 198

33

Vous aimerez peut-être aussi