Les associations entre entités avec JPA
Source | |
Branch |
|
Java |
|
Docker |
|
JPA offre une prise en charge complète pour la gestion des relations entre entités de différents types, que ce soit en mode un-vers-un (OneToOne), un-vers-plusieurs (OneToMany), ou plusieurs-vers-plusieurs (ManyToMany). Ces relations peuvent être configurées de manière unidirectionnelle ou bidirectionnelle.
1 One To One (1-1)
Dans certaines situations, lors de la modélisation orientée objet, il est nécessaire d’associer deux classes dans une relation un-à-un, mais en gardant une seule relation.
Pour cela JPA propose des annotations spécifiques :
@Embeddable: Cette annotation indique que les membres d’une classe Java peuvent être persistés en tant qu’attributs de la classe qui les utilise. En d’autres termes, elle spécifie qu’une classe peut être utilisée comme composant réutilisable dont les propriétés peuvent être incorporées dans une autre entité.
@Embedded: Utilisée au niveau d’un membre de classe annoté avec @Embeddable, cette annotation indique que le membre annoté doit être intégré dans la relation. Cela signifie que les propriétés de la classe annotée avec @Embeddable seront incluses dans la table de la classe qui les utilise.
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Embeddable
public class Biography {
@Column(name = "BIO_BRIEF")
private String brief;
@Lob
@Column(name = "BIO_EXTENDED")
private String extended;
}
@Getter
@Setter
@ToString
@RequiredArgsConstructor(staticName = "of")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "CUSTOMER", schema = "ex_biography")
public class Customer {
@Id
@GeneratedValue
private long id;
@Column(length = 50, nullable = false)
@NonNull
private String name;
@Embedded
@Column()
private Biography biography;
}
Customer.java
On peut donc rendre persistente une instance de Customer
associé à une instance de Biography
.
Creation et persistence d’un Customer
import fr.univtln.bruno.demos.jpa.hello.samples.ex_biography.Customer;
try (EntityManager entityManager = emf.createEntityManager()) {
.getTransaction().begin();
entityManager
= Customer.of("Jim");
Customer customer .setBiography(Biography.builder()
customer.brief("bla")
.extended("bla bla")
.build());
.persist(customer);
entityManager
.getTransaction().commit();
entityManager}
dans une seule relation
id | name | bio_brief | bio_extended |
---|---|---|---|
1 | Jim | bla | 70666 |
2 One to Many (1-N)
@Getter
@Setter
@ToString
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Embeddable
@Table(name = "LINE", schema = "EX_ONE_TO_MANY_B")
public class Line {
@NonNull
private String product;
private double price;
}
@Getter
@Setter
@ToString
@NoArgsConstructor
@Builder
@AllArgsConstructor
@Entity
@Table(name = "EORDER", schema = "EX_ONE_TO_MANY_B")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
@Builder.Default
@Column(nullable = false)
private LocalDateTime date = LocalDateTime.now();
@ElementCollection
@CollectionTable(name = "LINE", schema = "EX_ONE_TO_MANY_B")
@Singular
private Set<Line> lines = new HashSet<>();
}
2.1 Avec deux entités
Dans le cas général, l’attribut de type Collection<T>
est annoté avec @OneToMany
.
L’attribut optionnel mappedBy
indique le nom de l’attribut correspondant dans le type T
si l’on souhaite une relation bi-directionnelle. L’autre entité (de Type T
) est alors annotée avace @ManyToOne
@Getter
@Setter
@ToString
@NoArgsConstructor
@Entity
@Table(name = "EORDER", schema = "EX_ONE_TO_MANY_A")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
private LocalDateTime date = LocalDateTime.now();
@OneToMany(mappedBy = "order",
= {CascadeType.PERSIST,
cascade .REMOVE},
CascadeType= true)
orphanRemoval @ToString.Exclude
private Set<Line> lines = new HashSet<>();
public Order addLine(Line line) {
.setOrder(this);
line.add(line);
linesreturn this;
}
public Order removeLine(Line line) {
.setOrder(null);
line.remove(line);
linesreturn this;
}
}
@Getter
@Setter
@ToString
@RequiredArgsConstructor(staticName = "of")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "LINE", schema = "EX_ONE_TO_MANY_A")
public class Line {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
@NonNull
private String product;
@NonNull // just for requiredargsconstructor
private double price;
@ManyToOne
@JoinColumn(nullable = false)
@ToString.Exclude
private Order order;
public void setOrder(Order order) {
if (this.order != null) {
this.order.getLines().remove(this);
}
this.order = order;
if (order != null) {
.getLines().add(this);
order}
}
}
order_id | order_date | line_id | product | price |
---|---|---|---|---|
1 | 2025-01-30T18:10:25.293Z | 1 | Scissor | 8 |
1 | 2025-01-30T18:10:25.293Z | 2 | Pen | 1 |
1 | 2025-01-30T18:10:25.293Z | 3 | Paper | 5 |
2 | 2025-01-30T18:10:25.302Z | 4 | Moto | 8000 |
2 | 2025-01-30T18:10:25.302Z | 5 | Car | 20000 |
3 Many to Many (N-M)
L’annotation @ManyToMany en Java Persistence API (JPA) est utilisée pour modéliser une relation many-to-many entre deux entités. La table d’association sera alors générée et utilisée implicitement.
@Getter
@Setter
@ToString
@RequiredArgsConstructor(staticName = "of")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "CUSTOMER", schema = "EX_MANY_TO_MANY")
public class Customer {
@Id
@GeneratedValue
private long id;
@NonNull
private String name;
@ManyToMany
@ToString.Exclude
@JoinTable(schema = "EX_MANY_TO_MANY")
private Set<Address> places = new HashSet<>();
public void addPlace(Address address) {
this.places.add(address);
.getOccupants().add(this);
address}
public void removePlace(Address address) {
this.places.remove(address);
.getOccupants().remove(this);
address}
}
@Getter
@Setter
@ToString
@RequiredArgsConstructor(staticName = "of")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "ADDRESS", schema = "EX_MANY_TO_MANY")
public class Address {
@Id
@GeneratedValue
private long id;
@NonNull
private String addressDetail;
@ManyToMany(mappedBy = "places")
@ToString.Exclude
private Set<Customer> occupants= new HashSet<>();
public void addOccupant(Customer customer) {
this.occupants.add(customer);
.getPlaces().add(this);
customer}
public void removeOccupant(Customer customer) {
this.occupants.remove(customer);
.getPlaces().remove(this);
customer}
}
id | name |
---|---|
2 | Miss Hans Hagenes |
3 | Quinn Keeling DVM |
4 | Reva Welch |
5 | Kathy Hackett |
6 | Maxine Vandervort |
occupants_id | places_id |
---|---|
2 | 1 |
3 | 1 |
5 | 1 |
6 | 1 |
2 | 2 |
4 | 2 |
5 | 2 |
id | addressdetail |
---|---|
1 | Suite 304 71024 Deckow Park, Lehnermouth, AK 76082 |
2 | Apt. 432 8182 Linwood Parkways, Tovaville, HI 56620 |