Dans le cadre de votre mission pour Carlib Assurances, vous avez en partie implĂ©mentĂ© une fonctionnalitĂ© pour afficher les produits dâassurance auto.
Cette fonctionnalitĂ© dâaffichage a Ă©tĂ© implĂ©mentĂ©e dans le chapitre prĂ©cĂ©dent.
Vous Ă©tiez satisfait de votre travail, quand soudain, on vous informe dans lâoreillette que lâaffichage unitaire dâun produit souffre dâun manque dâinformations. 
Pourtant on a bien récupéré toutes les informations du produit ?
Câest vrai ! Mais lâaffichage dâun produit va au-delĂ des informations propres Ă ce dernier. La fonctionnalitĂ© implique dâafficher le produit, mais Ă©galement les commentaires associĂ©s.
Et ceci est un exemple parfait de relation ! LâentitĂ© Product est associĂ©e Ă lâentitĂ© Comment dans la mesure oĂč un produit peut avoir plusieurs commentaires.
Cette notion de relation trouve son pendant au sein de la base de données.
Je vous conseille trĂšs fortement de le lire si vous nâĂȘtes pas familiarisĂ© avec les relations en base de donnĂ©es ! 
Voici un récapitulatif des éléments clés :
Nous le disions au préalable, ces relations concernent nos entités donc notre code objet. Ainsi, la premiÚre chose à savoir est que ces relations sont modélisables dans vos entités grùce aux annotations @OneToOne, @OneToMany, @ManyToOne,@ManyToMany. Et nous allons voir comment les utiliser.
Mais pas si vite ! Je me dois de vous prĂ©senter la notion de directionnalitĂ© avant de vous montrer concrĂštement le code. Il en existe 2 : lâunidirectionnalitĂ© et la bidirectionnalitĂ©. Laissez-moi vous expliquer.
Une relation concerne 2 objets ! Par exemple, un produit peut avoir plusieurs commentaires, mais un commentaire est associĂ© Ă un seul produit. Il sâagit ici dâune relation 1 Ă plusieurs.
Ah, ok ! Comment est-ce que cette relation sera implémentée dans le code ?
IdĂ©alement, il nous faut un attribut dans la classe Product. Cet attribut sera la liste des commentaires associĂ©s au produit. Dans la classe Comment, il nây aucune rĂ©fĂ©rence vers le produit. Câest donc une relation unidirectionnelle !
Le produit connaĂźt les commentaires, mais la rĂ©ciproque nâest pas vraie.
Supposons maintenant que lâon dĂ©sire avoir un attribut dans la classe Comment qui correspond au Product. Alors dans ce cas, la classe Product fait rĂ©fĂ©rence aux commentaires, et la classe Comment fait rĂ©fĂ©rence au produit. Nous obtenons une relation bidirectionnelle.
Par expérience, dans la majorité des cas, la relation unidirectionnelle sera suffisante. Mais parfois, il est bien utile de mettre en place la bidirectionnalité.
Commençons par implĂ©menter une relation unidirectionnelle entre Product et Comment, comme dans lâexemple ci-dessus.
Dans le screencast qui suit, je vous montre comment faire.
Nous avons donc modifié la classe Product comme ceci :
package com.openclassrooms.datalayer.model;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name = "produit")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="produit_id")
private int produitId;
@Column(name="nom")
private String name;
@Column(name="description")
private String description;
@Column(name="cout")
private int cost;
@OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.EAGER)
@JoinColumn(name = "produit_id")
List<Comment> comments = new ArrayList<>();
// Add getters & setters
}
Notons avec intĂ©rĂȘt le nouvel attribut âcommentsâ. 2 annotations y sont associĂ©es :
@OneToMany : cette annotation permet de spĂ©cifier une relation âune Ă plusieursâ. Pour un produit, il y a plusieurs commentaires possibles.
La propriĂ©tĂ© âcascadeâ permet de dĂ©finir quel impact lâaction sur une entitĂ© aura sur son entitĂ© associĂ©e. Le type âALLâ signifie que toutes les actions sur lâentitĂ© Produit seront propagĂ©es sur lâentitĂ© Commentaires. Exemple : si on supprime le produit, les commentaires associĂ©s seront Ă©galement supprimĂ©s.
La propriĂ©tĂ© orphanRemoval=true permet dâactiver un mĂ©canisme qui garantit la non-existence de commentaire orphelin de son produit. Si on supprime un commentaire de la liste des commentaires du Product, alors le commentaire devient orphelin, et il est supprimĂ© de la base de donnĂ©es.
La propriĂ©tĂ© fetch possĂšde la valeur EAGER, et cela signifie quâĂ la rĂ©cupĂ©ration du produit, tous les commentaires seront Ă©galement rĂ©cupĂ©rĂ©s.
@JoinColumn : Cette annotation permet dâindiquer le nom de la clĂ© Ă©trangĂšre dans la table de lâentitĂ© concernĂ©e.
Vous pouvez dĂ©sormais tester ce code et cela sans aucune modification du ProductService. En effet, la modification que nous avons faite aura un impact sur la crĂ©ation de lâobjet Product lors de lâappel des mĂ©thodes du Repository. Or, la crĂ©ation est gĂ©rĂ©e par Spring Data JPA, donc nous nâavons rien d'autre Ă faire dans notre code, gĂ©nial non ?! 
Implémentez le code que je vous ai montré, puis testez-le en récupérant un produit et en affichant le contenu de chaque commentaire associé.
Avez-vous réussi ? Voici une correction pour la classe DataLayerApplication :
package com.openclassrooms.datalayer;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.openclassrooms.datalayer.model.Product;
import com.openclassrooms.datalayer.service.ProductService;
@SpringBootApplication
public class DataLayerApplication implements CommandLineRunner {
@Autowired
private ProductService productService;
public static void main(String[] args) {
SpringApplication.run(DataLayerApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
Optional<Product> optProduct = productService.getProductById(1);
Product productId1 = optProduct.get();
System.out.println(productId1.getName());
productId1.getComments().forEach(
comment -> System.out.println(comment.getContent()));
}
}
 Le code est également disponible sur le repository du cours, branche p2c3.
Lâapplication Carlib Assurances nous confronte Ă un autre type de relation. En effet, lâune des fonctionnalitĂ©s du produit consiste Ă afficher la liste des catĂ©gories, et vous avez implĂ©mentĂ© la rĂ©cupĂ©ration des catĂ©gories dans le chapitre 2 de cette partie de cours. 
Simplement voilĂ , afficher le nom des catĂ©gories nâest pas suffisant ! Lâobjectif est de rĂ©cupĂ©rer les produits de chaque catĂ©gorie.
Trop facile ! Il suffit de faire comme dans la section précédente, non ?
Pas tout Ă fait, car il ne sâagit pas ici dâune relation un Ă plusieurs, mais dâune relation plusieurs Ă plusieurs. Car une catĂ©gorie peut ĂȘtre associĂ©e Ă plusieurs produits, et un produit peut ĂȘtre rattachĂ© Ă plusieurs catĂ©gories !
Je vais donc vous apprendre Ă utiliser lâannotation @ManyToMany. Are you ready ? 
Comme prĂ©cĂ©demment, je commence par vous montrer lâutilisation de lâannotation et en l'occurrence, câest dans la classe Category :
package com.openclassrooms.datalayer.model;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
@Entity
@Table(name = "categorie")
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="categorie_id")
private int categoryId;
@Column(name="nom")
private String name;
@ManyToMany(
fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
}
)
@JoinTable(
name = "categorie_produit",
joinColumns = @JoinColumn(name = "categorie_id"),
inverseJoinColumns = @JoinColumn(name = "produit_id")
)
private List<Product> products = new ArrayList<>();
}
Des explications sont nécessaires !
Nous utilisons 2 propriétés de @ManyToMany :
La propriĂ©tĂ© fetch possĂšde la valeur LAZY, et cela signifie quâĂ la rĂ©cupĂ©ration de la catĂ©gorie, les produits ne sont pas rĂ©cupĂ©rĂ©s. Par voie de consĂ©quence, les performances sont meilleures (la requĂȘte est plus lĂ©gĂšre) ; cependant, lorsqu'ultĂ©rieurement dans votre code vous accĂ©derez aux produits Ă partir de lâobjet CatĂ©gorie en question, une nouvelle requĂȘte sera exĂ©cutĂ©e.Â
Cascade : contrairement aux exemples prĂ©cĂ©dents, nous ne voulons pas un CascadeType.ALL qui impliquerait une cascade dans le cas de la suppression. Je spĂ©cifie donc uniquement PERSIST et MERGE, la cascade sâapplique donc tant en crĂ©ation quâen modification.
Il est Ă©galement nĂ©cessaire dâutiliser lâannotation @JoinTable avec les propriĂ©tĂ©s suivantes :
name : correspond au nom de la table de jointure en base de données ;
joinColumns correspond à la clé étrangÚre dans la table de jointure ;
inverseJoinColumns correspond à la clé étrangÚre dans la table de jointure de la seconde entité concernée par la relation.
La classe CategoryService nâa pas Ă ĂȘtre modifiĂ©e, mais nous devons dĂ©sormais adapter le code de DataLayerApplication :
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.openclassrooms.datalayer.model.Category;
import com.openclassrooms.datalayer.service.CategoryService;
@SpringBootApplication
public class DataLayerApplication implements CommandLineRunner {
@Autowired
private CategoryService categoryService;
public static void main(String[] args) {
SpringApplication.run(DataLayerApplication.class, args);
}
@Override
@Transactional
public void run(String... args) throws Exception {
Optional<Category> optCategory = categoryService.getCategoryById(1);
Category categoryId1 = optCategory.get();
System.out.println(categoryId1.getName());
categoryId1.getProducts().forEach(
product -> System.out.println(product.getName()));
}
}
 Si le contenu de la mĂ©thode run nâa rien de nouveau, jâattire votre attention sur la ligne 26 et lâannotation @Transactional.
Câest lâoccasion parfaite pour moi pour vous parler dâun point important : les transactions.
Les transactions sont le mécanisme qui permet de respecter les propriétés ACID :
AtomicitĂ© : Une transaction sâeffectue entiĂšrement, ou pas du tout.
CohĂ©rence : Le contenu dâune base doit ĂȘtre cohĂ©rent au dĂ©but et Ă la fin dâune transaction.
Isolation : Les modifications dâune transaction ne sont visibles/modifiables que quand celle-ci a Ă©tĂ© validĂ©e.
DurabilitĂ© : Une fois la transaction validĂ©e, lâĂ©tat de la base est permanent (non affectĂ© par les pannes ou autre).
Le respect de ces propriétés est synonyme de fiabilité dans le traitement de vos données.
ImplĂ©menter des transactions peut vite se rĂ©vĂ©ler ĂȘtre une tĂąche complexe, bien que possible grĂące Ă lâAPI JDBC.
Encore une fois, Spring Boot se rĂ©vĂšle une aide de grande valeur, en Ă©vitant toutes les manipulations complexes grĂące Ă lâannotation @Transactional.
Cette annotation peut ĂȘtre appliquĂ©e Ă lâĂ©chelle dâune classe ou dâune mĂ©thode. Si cette derniĂšre est utilisĂ©e, alors Spring utilisera des transactions pour lâexĂ©cution des requĂȘtes SQL, garantissant ainsi pour vous le respect des propriĂ©tĂ©s ACID.
Sachez Ă©galement que lâutilisation du LAZY au niveau de lâattribut products dans lâentitĂ© Category nous oblige, avec Spring Data JPA, Ă lâutilisation des transactions dans ce contexte.
Pourquoi ?
Lors de lâutilisation du LAZY, lâentitĂ© Category est rĂ©cupĂ©rĂ©e et son attribut products est substituĂ© par un proxy (un proxy est une classe qui substitue une autre classe). Câest ainsi quâon obtient le gain de performances, car un proxy est bien Ă©videmment beaucoup plus lĂ©ger Ă charger que lâobjet rĂ©el.
Cependant, lorsquâon voudra accĂ©der Ă lâattribut products dans le code, alors le proxy devra laisser place aux vraies donnĂ©es. Et ces donnĂ©es sont accessibles uniquement si nous sommes dans le contexte de la transaction. DĂšs que nous sortons de la transaction, impossible de substituer un proxy par lâobjet correspondant.
Conclusion, lâutilisation du LAZY requiert la mise en place de transactions.
Le code de cette partie de cours est disponible sur le repository du cours Ă la branche p2c3.
Ă titre dâexercice, je vous propose de supprimer la relation @ManyToMany que nous avons implĂ©mentĂ©e cĂŽtĂ© Category, et de lâimplĂ©menter cĂŽtĂ© Product.
Je vous laisse vous y essayer, et je suis convaincu de votre réussite !
Pour la correction, vous pouvez regarder le code du repository Ă la branche p2c3_2.
Les relations sont une composante importante du framework ORM et répondent à des besoins métiers clés.
Il existe plusieurs types de relations, comme @OneToMany, @OneToOne ou @ManyToMany.Â
Chaque relation peut ĂȘtre :
unidirectionnelle : une entité A référence une entité B.
bidirectionnelle : une entitĂ© A rĂ©fĂ©rence une entitĂ© B, et lâentitĂ© B rĂ©fĂ©rence aussi lâentitĂ© A.
Les transactions sont des blocs de requĂȘtes Ă exĂ©cuter, et ont une trĂšs forte valeur ajoutĂ©e garantissant le respect des propriĂ©tĂ©s ACID.