Département Génie Informatique
Java Entreprise Edition
Pr. Aimad QAZDAR
[email protected] 28 octobre 2024
JPA - Mapping des
Associations
Les annotations ORM
● Un des fondements du modèle de données relationnelles repose sur les relations qui
peuvent intervenir entre les tables.
● Les relations utilisables dans le monde relationnel et le monde objet sont cependant
différentes:
− Les relations du monde objets :
• Elles sont réalisées via des références entre objets
• Elles peuvent mettre en œuvre l'héritage et le polymorphisme
− Les relations du monde relationnel :
• Elles sont gérées par des clés étrangères et des jointures entre tables
● Hibernate supporte plusieurs types de relations :
− relation de type 1 - 1 (one-to-one)
− relation de type 1 - n (one-to-many)
− relation de type n - 1 (many-to-one)
− relation de type n - n (many-to-many)
184
Direction d’une associations
● Les associations permettent de manifester les liens entre les classes du modèle
de persistance.
● Une association possède deux rôles, un à chacune de ses extrémités.
● La cardinalité d’une association dépend de la cardinalité de ces deux rôles.
185
Navigabilité
● Capacité d’une instance de C1 (resp. C2) à accéder aux instances de C2 (resp. C1)
● Bidirectional (Par défaut) : Navigabilité dans les deux sens, les objets peuvent traverser
les deux côtés de la relation:
− C1 a un attribut de type C2 et C2 a un attribut de type C1
● Unidirectional : Spécification de la navigabilité, Orientation de la navigabilité, les objets
peuvent uniquement traverser d'un côté de la relation, par exemple:
− C1 a un attribut du type de C2, mais pas l’inverse
186
Mapping des associations
Bidirectional, exemple:
● Compte et Transaction sont propriétaires de l’association
− Etant donné un objet Compte, il peut obtenir ses objets Transaction.
− Etant donné un objet Transaction, il peut obtenir son propre objet Compte.
187
Mapping des associations
Unidirectional, exemple:
● Compte : propriétaire de l’association
− Etant donné un objet Compte, il peut obtenir ses objets Transaction.
− Etant donné un objet Transaction, il ne peut pas obtenir son propre objet Compte.
● Transaction: propriétaire de l’association
− Etant donné un objet Transaction, il peut obtenir son objet Compte.
− Etant donné un objet Compte, il ne peut pas obtenir ses propres objets Transaction
188
Annotations
Classe 1 Classe 2
@annotation multi2 multi1 @annotation
Classe2 maClasse2 Classe1 maClasse1
Classe 1 Classe 2
@ maClasse 2 multi1 multi2 @ maClasse 1
One To One 0..1 0..1 One To One
One To One | Not Null 1..1 1..1 One To One | Not Null
Many To One 0..1 0..* One To Many
Many To One | Not Null 1..1 1..* One To Many | Not Null
One To Many 0..* 0..1 Many To One
One To Many | Not Null 1..* 1..1 Many To One | Not Null
Many To Many 0..* 0..* Many To Many
Many To Many | Not Null 1..* 1..* Many To Many | Not Null
189
Direction d’une associations
● La réduction de la portée de l'association est souvent réalisée en phase d'implémentation.
● C’est le développeur qu’est responsable de la gestion de la navigabilité des objets, c’est
dire les deux bouts de l’association.
● Un des bouts est le propriétaire de l'association (Master-Slave):
− Pour les associations autres que M-N, ce bout correspond à la table qui contient la clé
étrangère qui traduit l’association.
− Pour les associations M-N, le développeur choisit le bout propriétaire (de manière arbitraire).
− L’autre bout (non propriétaire) est qualifié par l’attribut mappedBy qui donne le nom de
l’association correspondante dans le bout propriétaire:
• @annotation( mappedBy= "ObjetAutreBout").
190
Direction d’une associations
Compte Transaction
@ManyToOne
@OneToMeny(mappedBy= "compte" ) 0..1 0..* @JoinColumn(name="code_cpt")
private Set<Transaction> transactions;
private Compte compte;
Compte Transaction
@OneToMeny
@JoinColumn(name="code_cpt")
0..1 0..*
private Set<Transaction> transactions;
Compte Transaction
@ManyToOne
0..1 0..* @JoinColumn(name="code_cpt")
private Compte compte;
191
Direction d’une associations
Voiture
Agent
0..1 0..1
@OneToOne
@OneToOne(mappedBy= "agent" )
@JoinColumn(name="Num_Ag")
private Voiture voiture;
private Agent agent;
Agent Voiture
0..1 0..1 @OneToOne
@JoinColumn(name="Num_Ag")
private Agent agent;
193
Récupération d'associations
● Vous avez la possibilité de récupérer les entités associées:
− soit immédiatement ("eager"),
− soit à la demande ("lazy").
● Le paramètre fetch peut être positionné à FetchType.LAZY ou à FetchType.EAGER
● FetchType.LAZY : indique que la relation doit être chargée à la demande ;
● FetchType.EAGER : indique que la relation doit être chargée en même temps que l'entité
qui la porte.
@Entity @Entity
public class Agence { public class Agence {
@Id @Id
private String code; private String code;
private String libelle; private String libelle;
@ManyToMany(fetch=FetchType.LAZY) @ManyToMany(fetch=FetchType.EAGER)
private Set<Client> clients = new private Set<Client> clients = new
HashSet<>(); HashSet<>();
… …
} }
194
Les composants (component)
● Une entité existe par elle-même indépendamment de toute autre entité, et peut être
rendue persistante par insertion dans la base de données, avec un identifiant propre.
● Un composant est un objet sans identifiant, qui ne peut être persistant que par
rattachement à une entité.
● La notion de composant résulte du constat qu’une ligne dans une base de données
peut parfois être décomposée en plusieurs sous-ensemble dotés chacun d’une logique
autonome.
● Cette décomposition mène à une granularité fine de la représentation objet, dans laquelle
on associe plusieurs objets à une ligne de la table.
● @Embedded nous indique que ce composant est embarqué.
● @Embeddable nous indique que cette classe sera utilisée comme composant.
195
Les composants (component)
@Embeddable
Agence
1..1 0..1 Adresse
@Embedded
private Adresse adresse;
@Entity @Embeddable
public class Agence { public class Adresse {
@Id private String ville;
private String code;
private String libelle;
private String pays;
private int codePostal;
@Embedded
private Adresse adresse; public Adresse() {}
public Agence() { } public Adresse(String ville,
public Agence(String code, String libelle, String pays, int codePostal) {
Adresse adresse) { super();
super();
this.code = code;
this.ville = ville;
this.libelle = libelle; this.pays = pays;
this.adresse = adresse; this.codePostal = codePostal;
} }
//getters et setters // getters et setters
} }
196
Clé composée
● Les clefs primaires composées utilisent une classe embarquée comme représentation de
la clef primaire,
● 3 possibilités:
− @Id et @Embeddable: un seul attribut Id dans la classe entité
− @IdClass: correspond à plusieurs attributs Id dans la classe entité
− @EmbeddedId un seul attribut Id dans la classe entité
● Dans les trois cas, la clé doit être représentée par une classe Java:
− les attributs correspondent aux composants de la clé
− la classe doit être publique
− la classe doit avoir un constructeur sans paramètre
− la classe doit être sérialisable.
− La classe doit redéfinir equals() et hashcode().
197
Clé composée
Une Clé composée : @IdClass( ClassePK.class )
@Entity public class CommandePK implements Serializable {
@IdClass(CommandePK.class)
public class Commande { private int num;
private int code;
@Id
private int num;
@Id public CommandePK() {}
private int code; // les gettrs et les setters
public int hashCode() {
private int quantite; …
private double prix; }
public Commande() {}
public boolean equals(Object obj) {
// les getters et les setters …
}
} }
198
Clé composée
Une Clé composée : @Id et @Embeddable
@Embeddable
@Entity public class CommandePK implements Serializable {
public class Commande {
private int num;
@Id private int code;
private CommandePK pk;
public CommandePK() {}
private int quantite; // les gettrs et les setters
private double prix; public int hashCode() {
…
public Commande() {} }
// les getters et les setters public boolean equals(Object obj) {
…
} }
}
199
Clé composée
Une Clé composée : @EmbeddedId
@Entity public class CommandePK implements Serializable {
public class Commande {
private int num;
@EmbeddedId private int code;
private CommandePK pk;
public CommandePK() {}
private int quantite; // les gettrs et les setters
private double prix; public int hashCode() {
…
public Commande() {} }
// les getters et les setters public boolean equals(Object obj) {
…
} }
}
200
Exemple : Gestion d’école
coordonnees
Id_Crd (auto) etudiant
avoir noter matiere
Email_Crd Apogee_Etd
(1,1) (1,1) (1,*) (0,*) Code_Mat
Tel_Crd Nom_Etd
note Libelle_Mat
Adresse_Crd Prenom_Etd
Ville_Crd
(1,*)
enseigant
departement affecter enseigner
Code_Ens (auto)
Code_Dep (1,*) (1,1) (1,*)
Nom_Ens
Libelle_Dep
Prenom_Ens
201
Association un-à-plusieurs
● Associations 1-N et N-1 : les associations “un à plusieurs” et “plusieurs à un”
● Annotations @OneToMany et @ManyToOne
● Représentée par une clé étrangère dans la table qui correspond au côté propriétaire
(obligatoirement le côté Many)
● Exemple : Enseignant-Département, cette association peut se représenter de 3 manières :
− dans un Enseignant, on place un lien vers le Département (unidirectionnel); @ManyToOne
− dans un Département, on place des liens vers les Enseignants qu’il lui sont affectés
(unidirectionnel); @OneToMany
− Ou bien, on représente les liens des deux côtés (bidirectionnel).mappedBy
● Généralement, dans une base relationnelle, une association représentée par une clé
202
@ManyToOne
@Entity
● Association Unidirectionnelle. @Table(name="enseignant")
public class Enseignant {
➔ Enseignant est le maître de l’association @Id
● Dans la classe Enseignant, @ManyToOne @GeneratedValue(strategy=GenerationType.
IDENTITY)
nous indique qu’un objet lié nommé
@Column(name="Code_Ens")
departement (clé étrangère) est de la classe private int code;
Departement. @Column(name="Nom_Ens", length=30)
private String nom;
● L’annotation @JoinColumn indique la clé @Column(name="Prenom_Ens", length=30)
étrangère dans la table Enseignant qui permet private String prenom;
de rechercher le Département concerné @ManyToOne
@JoinColumn(name="Code_Dep")
private Departement departement;
public Enseignant() {}
public Departement getDepartement() {
return departement;}
public void setDepartement(Departement
departement) {this.departement =
departement;}
// les getters et les setters
}
203
@ManyToOne
public class MappingTest {
public static void main(String[] args) {
EntityManagerFactory emf
=Persistence.createEntityManagerFactory("myDB");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Departement dep=new Departement("GINFO","Génie
Département
Informatique");
Column Type Null Primary em.persist(dep);
Code_Dep varchar(6) No yes Enseignant ens=new Enseignant("Aimad", "QAZDAR",dep);
Libelle_Dep varchar(50) Yes em.persist(ens);
em.getTransaction().commit();
}
Enseignant
}
Column Type Null Primary
Code_Ens int(11) No AUTO_INCREMENT
Nom_Ens varchar(30) Yes
Prenom_Ens varchar(30) Yes
Code_Dep varchar(6) Yes FK
204
Association plusieurs-à-plusieurs
● Quand on représente une association plusieurs-plusieurs en relationnel, l’association
devient une table dont la clé est la concaténation des clés des deux entités de
l’association.
● La valeur par défaut du nom de la table association est la concaténation des 2 tables,
séparées par _
● Annotation @ManyToMany est représentée par une table association
● Deux cas se présentent :
− l’association n’est pas porteuse d’aucun attribut.
− l’association est porteuse des attributs (traitée comme @OneToMany dans les deux côtés de
l’association).
● Dans notre exemple:
− un enseignant enseigne plusieurs matières.
− une matière est enseignée par plusieurs enseignants.
207
@ManyToMany
@Entity
● Association Unidirectionnelle.
@Table(name="matiere")
➔ Matière est le maître de public class Matiere {
l’association @Id
@Column(name="Code_Mat", length=6)
● Dans la classe Matière, private String code;
@ManyToMany nous indique que la @Column(name="Libelle_Mat", length=100)
private String libelle;
collection des objets liés nommé @ManyToMany
enseignants, est de la classe private Set<Enseignant> enseignants=new HashSet<>();
Enseignant. public Set<Enseignant> getEnseignants() {
return enseignants;
}
public void setEnseignants(Set<Enseignant>
enseignants) {
this.enseignants = enseignants;
}
public void ajouterEnseigant(Enseignant ens){
this.enseignants.add(ens);}
//les getters et les setters
}
208
@ManyToMany
public class MappingTest2 {
public static void main(String[] args) {
EntityManagerFactory emf
=Persistence.createEntityManagerFactory("myDB");
Enseignant
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Column Type Null Primary
Departement dep=new Departement("GINFO","Génie
Code_Ens int(11) No PK
Informatique");
Nom_Ens varchar(30) Yes NULL
Enseignant ens1=new Enseignant("Aimad","QAZDAR",dep);
Prenom_Ens varchar(30) Yes NULL
em.persist(ens1);
matiere_enseignant
Matiere mat1=new Matiere("JEE","Java Entreprise
Column Type Null Primary
Edition");
mat1.ajouterEnseigant(ens1);
Matiere_Code_Mat varchar(6) No FK
Matiere mat2=new Matiere("SOA","Architecture Orientée
enseignants_Code_Ens int(11) No FK
Services");
Matière mat2.ajouterEnseigant(ens1);
Column Type Null Primary em.persist(mat1);
Code_Mat varchar(6) No PK em.persist(mat2);
Libelle_Mat varchar(100) Yes NULL em.getTransaction().commit();
}
}
209
Clé composée
● Clé composé :
− Table existante avec clé multi-attributs
− Association N-M (Many-to-Many)
● 3 possibilités:
− @Id et @Embeddable: un seul attribut Id dans la classe entité
− @IdClass: correspond à plusieurs attributs Id dans la classe
entité
− @EmbeddedId un seul attribut Id dans la classe entité
● Dans les trois cas, la clé doit être représentée par une classe
Java:
− les attributs correspondent aux composants de la clé
− la classe doit être publique
− la classe doit avoir un constructeur sans paramètre
− la classe doit être sérializable et redéfinir equals et hashcode.
210
Clé composite : @Id et @Embeddable
● Correspond au cas où la classe entité @Embeddable
comprend un seul attribut annoté @Id public class Noter_PK implements
Serializable{
● La classe clé primaire est annotée par private static final long serialVersionUID
@Embeddable = 1L;
@Entity @Column(name="Code_Mat")
@Table(name="noter") private String codeMat;
public class Noter { @Column(name="Apogee_Etd")
@Id private int apogeeEtd;
private Noter_PK pk; public Noter_PK(String codeMat, int
private double note; apogeeEtd) {this.codeMat = codeMat;
public Noter() {super();} this.apogeeEtd = apogeeEtd;}
public Noter(int apogee, String matiere, public Noter_PK() {super();}
double note) {this.pk = new public String getCodeMat() {
Noter_PK(matiere,apogee); return codeMat;}
this.note = note;} public void setCodeMat(String codeMat) {
public Noter_PK getPk() {return pk;} this.codeMat = codeMat;}
public void setPk(Noter_PK pk) {this.pk = public int getApogeeEtd() {
pk;} return apogeeEtd;}
public double getNote() {return note;} public void setApogeeEtd(int apogeeEtd) {
public void setNote(double note) {this.note = this.apogeeEtd = apogeeEtd;}
note;} }
211
Clé composite : @Id et @Embeddable
public class MappingTest3 {
public static void main(String[] args) {
// TODO Auto-generated method stub
EntityManagerFactory emf
=Persistence.createEntityManagerFactory("myDB");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Noter note1=new Noter(2300, "JEE", 16.75);
em.persist(note1);
Noter note2=new Noter(5376, "JEE", 18.00);
em.persist(note2);
em.getTransaction().commit();
}
}
212
Association un-à-un
● Annotation @OneToOne représente l’association 1-1
● Dans l'exemple ci-dessous, chaque « étudiant » ne peut avoir qu'une seule
«Coordonnées» et une « coordonnées » ne peut appartenir que à un seul « étudiant ».
● Cette relation peut se traduire de plusieurs manières dans la base de données :
− Une seule table qui contient les données de l’étudiant et ses coordonnés.
− Deux tables, une pour les étudiants et une pour les coordonnés avec une clé primaire
partagée
− Deux tables, une pour les étudiants et une pour les coordonnés avec une seule clé étrangère
● Il y a plusieurs façons de traiter ce cas avec une ou deux tables dans la base de données:
− Deux tables et une relation One-to-One
− Une seule table avec un Component
214
@OneToOne
● L’annotation @Embedded & @Embeddable
@Entity
● Une seul table avec un Component
@Table(name="etudiant")
● Dans la classe Etudiant, @Embedded nous public class Etudiant {
indique que ce composant est embarqué @Id
(@Embeddable). @Column(name="Apogee_Etd")
private int apogee;
@Column(name="Nom_Etd", length=30)
private String nom;
@Column(name="Nom_Prenom", length=30)
private String prenom;
@Embedded
private Coordonnees coordonnées;
// Constructeurs
// getters et setters
}
215
@OneToOne
● L'annotation @Embeddable permet de
@Embeddable
préciser que la classe sera utilisée comme
public class Coordonnees {
un component. Un tel élément n'a pas @Column(name="adresse_Crd")
d'identifiant puisque celui utilisé sera celui private String adresse;
de l'entité englobant. @Column(name="ville_Crd")
private String ville;
@Entity @Column(name="email_Crd")
@Table(name="etudiant")
public class Etudiant {
private String email;
@Id @Column(name="tel_Crd")
@GeneratedValue(strategy=GenerationType.IDENTITY) private String tel;
@Column(name="Apogee_Etd") public Coordonnees() {}
private int apogee;
@Column(name="Nom_Etd", length=30) public Coordonnees(String adresse,
private String nom; String ville, String tel, String email)
@Column(name="Nom_Prenom", length=30) {
private String prenom;
@Embedded
this.adresse = adresse;
private Coordonnees coordonnées; this.ville = ville;
this.tel = tel;
// Constructeurs this.email = email;
// getters et setters
} }
}
216
@OneToOne
public class MappingTest4 {
public static void main(String[] args) {
EntityManagerFactory emf
=Persistence.createEntityManagerFactory("myDB");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Coordonnees crd=new Coordonnees("ENSA-agadir", "Agadir",
"0525008800",
"
[email protected]");
etudiant Etudiant etd1=new Etudiant(3322,"Ahmed", "RABII",crd);
Column Type Null Default em.persist(etd1);
Apogee_Etd int(11) No
adresse_Crd varchar(255) Yes NULL em.getTransaction().commit();
email_Crd varchar(255) Yes NULL
}
tel_Crd varchar(255) Yes NULL
}
ville_Crd varchar(255) Yes NULL
Nom_Etd varchar(30) Yes NULL
Nom_Prenom varchar(30) Yes NULL
217