Plan de ce cours

 Présentation de JPA JPA  Entité  Gestionnaire d’entités (Jakarta Persistence API)  Compléments sur les entités : pour Jakarta EE identité, associations, héritage  Requêtes, JPQL ITU - Université Côte d’Azur  Modification en volume Richard Grin  Concurrence Version O 1.37 – 10/3/20  Adaptation à une base préexistante  Bibliographie

R. Grin JPA page 2

1 2

JPA

 Spécification pour la persistance des objets dans une base de données (BD) relationnelle  ORM, Object-Relational mapping, fait Présentation de JPA correspondre le monde relationnel et le monde des objets  Au-dessus de JDBC ; donc un driver JDBC du SGBD doit être installé sur le serveur d’application ou dans l’application  Ce support étudie JPA dans Jakarta EE mais JPA peut aussi être utilisé avec Java SE

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

3 4

Principales propriétés de JPA Fournisseur de persistance

 Des appels simples permettent de gérer la  L’utilisation de JPA nécessite un fournisseur persistance, par exemple « persist(objet) » de persistance qui implémente les classes et pour rendre un objet persistant interfaces de l’API  Les données de la base de données relationnelle  EclipseLink et sont des fournisseurs sont traitées avec une vision objet, en utilisant les de persistance classes Java entités

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

5 6

1 Entité Exemple d’entité – identificateur et attributs  Classe dont les instances peuvent être persistantes @Entity Clé primaire  Annotée par @Entity public class Departement { dans la base – obligatoire  « entité » peut aussi désigner une instance de @Id classe entité @GeneratedValue valeur générée private Long id; automatiquement – optionnel private String nom; Par défaut tous les attributs private String lieu; sont persistants

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

7 8

Exemple d’entité – constructeurs Exemple d’entité – une association Un département - Autre bout de l’association plusieurs employés dans classe Employe public Departement() { } Obligatoire @OneToMany(mappedBy="dept") private List employes = public Departement(String nom, new ArrayList<>(); Comment l’association sera String lieu) { traduite dans classe Employe ? this.nom = nom; public List getEmployes() { this.lieu = lieu; return employes; } } public void addEmploye(Employe emp) { employes.add(emp); }

R. Grin JPA page 9 R. Grin JPA page 10

9 10

Fichiers de configuration XML persistence.  META-INF/persistence.xml : informations conserver les entités java:app/jdbc/b1  Contient le nom JNDI de la source de données nom JNDI dans le serveur d’application tables créées si n’existent pas  Des informations plus techniques peuvent être propriété non standard du fournisseur de dire si les ordres SQL générés par JPA sont persistance ; requêtes SQL générées par enregistrés dans les logs du serveur JPA enregistrées dans les logs du serveur R. Grin JPA page 11 R. Grin JPA page 12

11 12

2 Définition d’une source de données Exemple 1Nom JNDI dans persistence.xml  Soit directement dans le serveur d’application @DataSourceDefinition( (pas standardisé) name = "java:app/jdbc/b1", className = "com.mysql.cj.jdbc.MysqlDataSource",  Soit dans l’application, portNumber = 3306,  de façon standard serverName = "localhost", — par @DataSourceDefinition user = "root", password = "fflkq7dlfj", databaseName = "db1", Nom de la BD — dans un fichier de configuration standard properties= {"characterEncoding=UTF-8", dans le SGBD (web.xml par exemple) "useUnicode=true",…})  ou d’une façon particulière au serveur @Stateless Propriétés d’application (-resources.xml par public class MonEJB { ... } non standards exemple)

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

13 14

Exemple 2 Gestionnaire d’entités (EM) Pour MySQL  Interface java:app/jdbc/b1 javax.persistence.EntityManager com.mysql.cj.jdbc.MysqlDataSource machin.unice.fr  Interlocuteur principal pour le développeur 3306testDB Fournit les méthodes pour gérer les entités : toto  les rendre persistantes fs78jiia Avantage par rapport à  @DataSourceDefinition : supprimer leurs valeurs dans la BD useUnicode le mot de passe peut être  retrouver leurs valeurs dans la BD true modifié au déploiement  … ... Pourquoi un avantage ?

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

15 16

Contexte de persistance Exemple 1 nom dans persistence.xml ;  em.persist(entité) @Stateless optionnel si une seule unité rend entité persistant  sera géré par em public class DepartementFacade {  Toute modification apportée à entité sera @PersistenceContext(unitName = "employes") enregistrée dans la BD par em au moment du private EntityManager em; EM injecté par le container commit public void create(Dept dept) {  L’ensemble des entités gérées par un EM em.persist(dept); s’appelle un contexte de persistance (CP) dept.setLieu("Paris"); enregistré dans la BD ? } Transaction démarrée EM CP ... } au début de la méthode Pourquoi ? et fermée à la fin

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

17 18

3 Exemple 2 Que fait cette méthode ?

public void augmenter(String poste, int prime) { String requete = "SELECT e FROM Employe e " Pas du SQL, c’est du JPQL ; + " WHERE e.poste = :poste"; Employe est une classe Java Query query = em.createQuery(requete); query.setParameter("poste", poste); Entité List liste = query.getResultList(); for (Employe e : liste) { e.setSalaire(e.getSalaire() + prime); } Est-ce que les augmentations } seront dans la base de données ?

Oui, car les employés récupérés dans la BD sont ajoutés automatiquement par em dans le CP

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

19 20

Conditions pour une classe entité Types d’accès à une valeur persistante

 Constructeur sans paramètre protected ou  JPA peut accéder à la valeur d’un attribut de 2 public façons  Attribut qui représente la clé primaire dans la BD  par la variable d’instance ; accès « par champ »  Pas final  par les getters et setters ;  Méthodes ou champs persistants pas final accès « par propriété »  La classe ne doit pas nécessairement implémenter Serializable mais c’est requis dans certaines circonstances

R. Grin JPA page 21 R. Grin JPA page 22

21 22

Exemple Quel type d’accès choisir ?

 Accès par champ :  L’accès par propriété oblige à ajouter des getters @Id et des setters pour tous les attributs private int id;  Choisir plutôt l’accès par champ  Accès par propriété : @Id public int getId() { ... } Accès par champ ou par propriété, quelle est votre préférence ?

R. Grin JPA page 23 R. Grin JPA page 24

23 24

4 Attributs persistants Cycle de vie d’une instance d’entité

 Par défaut, tous les attributs d’une entité sont  L’instance peut être persistants  nouvelle : créée mais pas gérée par un EM  Exception : attribut avec un champ  gérée par un EM (méthode persist, ou transient (mot-clé Java lié à la sérialisation) instance récupérée dans la BD) ou annoté par @Transient  détachée : a une identité dans la BD mais n’est plus gérée par un EM  supprimée : gérée par un EM ; données seront supprimées dans la BD au commit (méthode remove)

R. Grin JPA page 25 R. Grin JPA page 26

25 26

Table de la base de données Exemple

 Dans les cas simples, une classe  une  @Entity  nom de la table = nom de la classe public class Departement { instances sauvegardées dans la table  noms des colonnes = noms des attributs Departement (ou DEPARTEMENT)  Par exemple, la classe Departement  @Entity correspond à la table Departement avec les @Table(name = "DEPT") colonnes id, nom, lieu public class Departement {  Convention plutôt que configuration pour JPA  @Column pour changer le nom ou les attributs d’une colonne dans la table

R. Grin JPA page 27 R. Grin JPA page 28

27 28

Types persistants Types « basic »  Types primitifs (int, double,…) ou enveloppes  Un attribut persistant peut être d’un des types de type primitifs (Integer,…) suivants :  String, BigInteger, BigDecimal  « basic »  Types de l’API « time » Java 8 (LocalDate,…),  « Embeddable » Date et Calendar (de java.util ; doivent être  collection d’éléments de type « basic » ou annoté par @Temporal), Date, Time et Embeddable Timestamp (de java. ; peu utilisés)  Énumérations  Tableaux de byte, Byte, char, Character  Plus généralement Serializable (sauvegardé comme un tout dans la base) R. Grin JPA page 29 R. Grin JPA page 30

29 30

5 Exemples Classe Embeddable

 private LocalDateTime dateOperationBancaire;  Classe dont les données sont insérées dans  // par défaut le numéro est enregistré dans la BD les lignes de la table d’une entité @Enumerated(EnumType.STRING) private TypeEmploye typeEmploye;

R. Grin JPA page 31 R. Grin JPA page 32

31 32

Exemple Collection d’éléments @Embeddable public class Adresse { Pas besoin d’id  Collection d’éléments de type basic ou private int numero; puisque les données embeddable private String rue; sont insérées dans private String ville; celles de Employe  Par exemple une collection de numéros de . . . téléphone, enregistrés dans une table à part : } @ElementCollection @Entity private Set telephones; public class Employe { 3 colonnes pour @Embedded optionnel l’adresse dans la table private Adresse adresse; des employés ... }

R. Grin JPA page 33 R. Grin JPA page 34

33 34

2 types d’EM dans un serveur d’application

 Géré par l’application : créé par l’application à Gestionnaire d’entités partir d’une fabrique d’EM injectée ; doit être fermé par l’application (Entity Manager)  Géré par le container : injecté par le serveur d’application ; automatiquement fermé par le container

R. Grin JPA page 35 R. Grin JPA page 36

35 36

6 EM géré par l’application 2 types d’EM géré par le container

@Stateful  Les 2 types diffèrent par la relation de l’EM public class DeptManager { avec le CP et la durée de vie du CP par @PersistenceUnit Fabrique EM injectée rapport aux transactions private EntityManagerFactory emf; par container private EntityManager em;  EM de portée transaction : le CP ne dure private Departement dept; que le temps de la transaction et il peut être lié à plusieurs EMs Le CP est attaché à la transaction public void init(int deptId) { em = emf.createEntityManager(); EM créé par application  EM étendu (ne peut exister que dans un dept = em.find(Departement.class, deptId); EJB stateful) : le CP est lié à un seul EM ; il } EJB stateful pour gérer peut exister après la fin de la transaction ... un seul département Le CP est attaché à l’EJB stateful

R. Grin JPA page 37 R. Grin JPA page 38

37 38

Portée transaction Propagation de contexte @Stateless public class DeptFacade {  Avec un EM de portée transaction, à chaque @PersistenceContext EM injecté par le container private EntityManager em; opération JPA, l’EM regarde si un CP est déjà attaché à la transaction public void create(Dept dept) {  Si c’est le cas, ce CP est utilisé par l’EM, sinon em.persist(dept); CP ne dure que le temps l’EM demande un nouveau CP au container et } de la transaction l’attache à la transaction public void edit(Dept dept) {  Permet à plusieurs EMs d’utiliser le même CP em.merge(dept); pendant une transaction entière } ... } R. Grin JPA page 39 R. Grin JPA page 40

39 40

Limitation d’une transaction EM étendu gérée par le container (CMT) @Stateful  Elle ne peut pas englober des entités gérées public class Caddy { par plusieurs unités de persistance (donc des @PersistenceContext(type=EXTENDED) données venant de plusieurs bases de EntityManager em; Même CP conservé données) ... pendant plusieurs transactions  Si on veut qu’une transaction englobe } plusieurs unités de persistance, il faut donc utiliser une transaction gérée par l’application  Permet d’établir une conversation longue (BMT) entre l’utilisateur et la BD, sans avoir à gérer des entités détachées (le contexte reste ouvert entre les transactions) R. Grin JPA page 41 R. Grin JPA page 42

41 42

7 Opérations en dehors transaction Choix du type d’EM

 Opérations permises avec EM étendu :  Plus simple de le faire gérer par le container  Opérations de lecture : find, refresh, detach (propagation du CP, pas besoin de le fermer) requêtes select  Le plus souvent l’EM est de portée transaction  Opérations de modification permises : persist, merge, remove  Avec EM de portée transaction :  Seules les opérations de lecture sont autorisées ; elles retournent des entités détachées Qui peut expliquer pourquoi cette différence entre types d’EM ?

R. Grin JPA page 43 R. Grin JPA page 44

43 44

Méthodes de EntityManager Méthodes de EntityManager

 void persist(Object entité)  void clear()  T merge(T entité)  void detach(Object entity)  void remove(Object entité)  Query createQuery(String requête)  T find(Class classeEntité,  Query createNamedQuery(String nom) Object cléPrimaire)  Query createNativeQuery(String  void flush() requête)  void lock(Object entité, LockModeType lockMode)  void refresh(Object entité)  void close()

R. Grin JPA page 45 R. Grin JPA page 46

45 46

Quand INSERT ? flush() Quand UPDATE ? refresh(entité) Quand DELETE ?  Enregistre dans la BD les modifications  entité doit être gérée (faire un merge en cas effectuées sur les entités d’un CP de besoin)  L’EM lance les commandes SQL pour  Données de la BD sont copiées dans l’entité modifier la BD (INSERT, UPDATE ou (qui doit être dans le CP), écrasant les DELETE) éventuelles modifications faites dans cette  Automatiquement effectué entité à chaque commit et,  Pour avoir la dernière version des données optionnellement, avant enregistrées dans la BD les requêtes flush() = commit ?

R. Grin JPA page 47 R. Grin JPA page 48

47 48

8 Contexte de persistance - cache Entité détachée

 Un CP est un cache pour les accès à la BD  Une entité gérée par un EM peut être  find, et Query qui retourne des entités détachée de son CP (par exemple par la entières, ramènent les données du CP si elles méthode detach, ou si l’EM est fermé) y sont déjà  Cette entité détachée peut être modifiée  Attention, les données du CP ne tiennent pas compte des modifications faites sans passer  Pour enregistrer ces modifications dans la BD par l’EM il est nécessaire de rattacher l’entité à un CP par la méthode merge  Peut poser des problèmes ; un refresh peut être la solution  Attention au cache de 2ème niveau partagé par tous les EMs d’une même unité de persistance R. Grin JPA page 49 R. Grin JPA page 50

49 50

merge(a) merge – une erreur à ne pas faire

 Attention, merge(a) ne met pas a dans le CP  On veut ajouter un employé à dept dans la BD :  C’est une copie de a qui est mise dans le CP ; em.merge(dept); Quel est le cette copie est retournée par la méthode dept.addEmploye(emp); problème ?  Si l’application utilise les données de a après  L’ajout du nouvel employé ne sera pas un merge, il faut écrire ce type de code : enregistré dans la BD car l’objet référencé par a = em.merge(a); dept n’est pas géré ; le bon code : dept = em.merge(dept); dept.addEmploye(emp);

R. Grin JPA page 51 R. Grin JPA page 52

51 52

Différence entre persist et merge

 persist sert pour les entités dont les données ne sont pas déjà dans la BD :  traduit par un INSERT  si l’entité a un id qui existe déjà dans la Identité des entités base, une exception est levée  Au contraire, merge sert le plus souvent pour les entités qui correspondent à des données déjà dans la BD et il est alors traduit par un UPDATE

R. Grin JPA page 53 R. Grin JPA page 54

53 54

9 Clé primaire Type de la clé primaire

 L’attribut annoté @Id correspond à la clé  Type primitif Java primaire dans la table associée  Classe qui enveloppe un type primitif  Ne jamais le modifier dès que l’entité  java.lang.String représente des données de la base  (java.util.Date)  (java.sql.Date)

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

55 56

Génération automatique de clé Exemple

@Id  Pour les clés de type nombre entier @GeneratedValue(  @GeneratedValue indique que la clé primaire strategy = GenerationType.SEQUENCE, sera générée par le SGBD generator = "EMP_SEQ")  Cette annotation peut avoir un attribut private Long id; strategy qui indique comment la clé sera générée  La valeur de l’id est mise automatiquement dans l’entité soit après un persist, soit après un flush

R. Grin JPA page 57 R. Grin JPA page 58

57 58

Clé composite

 Pas recommandé, mais une clé primaire peut  Faire TP 4 être composée de plusieurs colonnes  Peut arriver quand la BD existe déjà, en particulier quand la classe correspond à une table association (association M:N)  2 possibilités :  @EmbeddedId et @Embeddable  @IdClass

R. Grin JPA page 59 R. Grin JPA page 60

59 60

10 Rappels

 Exemples : association entre  les départements d’une entreprise et leurs employés Associations  les cours et les étudiants  Types d’association entre entités :  1-1, 1-N, N-1, M-N Donnez des exemples  uni ou bidirectionnelle de chacun de ces types

R. Grin JPA page 61 R. Grin JPA page 62

61 62

Bout propriétaire Exemple

 Un des 2 bouts d’une association bidirectionnelle  Dans la classe Employe : est dit propriétaire de l’association @ManyToOne private Departement departement;  Pour les associations 1 – N, c’est celui qui correspond à la table qui contient la clé  Dans la classe Departement : étrangère ; c’est le bout 1 ou N ? @OneToMany(mappedBy = "departement") private Collection employes;  L’autre bout contient l’attribut mappedBy qui donne le nom de l’attribut du bout propriétaire Quel est le bout propriétaire ? Dans quelle table sera la clé étrangère ?

Nom de la colonne clé étrangère : DEPARTEMENT_ID

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

63 64

Association bidirectionnelle - JPA Exemple

 Dans les associations 1-N bidirectionnelle, le bout  Le développeur est responsable de la gestion « 1 » peut comporter ce genre de méthode : correcte des 2 bouts de l’association public void ajouterEmploye(Employe e) {  Pour faciliter la gestion des 2 bouts, il est Departement d = e.getDept(); commode d’ajouter une méthode qui effectue if (d != null) { tout le travail d.employes.remove(e); } Dans quelle classe this.employes.add(e); est ce code ? e.setDept(this); } Qui veut expliquer ce code ?

R. Grin JPA page 65 R. Grin JPA page 66

65 66

11 Erreur à ne pas faire Configuration

 Attention, si on ne modifie que le bout non  Si une valeur par défaut ne convient pas, propriétaire (on oublie de modifier le bout ajouter une annotation dans le bout propriétaire) d’une association propriétaire bidirectionnelle, l’association n’est pas  Par exemple, pour indiquer le nom de la enregistrée dans la base de données au colonne clé étrangère : moment du commit @ManyToOne @JoinColumn(name="DEPT") private Departement departement; Ce code est dans quelle classe ? Que signifie l’annotation @JoinColumn ?

R. Grin JPA page 67 R. Grin JPA page 68

67 68

Association M:N Exemple - bidirectionnel Dans quelle  Traduite par 1 ou 2 collections annotées par @ManyToMany classe ? @ManyToMany private Collection projets;  Pourquoi « 1 ou 2 » ? @ManyToMany(mappedBy = "projets")  On peut choisir le bout propriétaire si private Collection employes; bidirectionnel Quel est le bout propriétaire ? Dans quelle classe ? Rappel : comment indique-t-on le Dans quelle table sera la clé étrangère ? bout propriétaire ?  @JoinTable sert à donner le nom de la table association et des colonnes clés étrangères de cette table si les valeurs par défaut ne conviennent pas R. Grin JPA page 69 R. Grin JPA page 70

69 70

Association M:N avec information Classe association portée par l’association  Chaque classe qui participe à l’association peut avoir une collection d’objets de la classe  Exemple : association (suivant directionnalité) Association entre les employés et les projets ; un employé a une fonction dans chaque  2 possibilités pour la classe association : projet auquel il participe  un attribut identificateur (@Id) unique (le Où mettre la fonction dans le modèle objet ? plus simple)   Association M:N traduite par une classe un attribut identificateur par classe association participant à l’association  Association bidirectionnelle pour le code des transparents suivants

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

71 72

12 Code classe association (début) Code classe association (fin)

public Participation() { } Obligatoire ? @Entity public class Participation { public Participation(Employe employe, @Id @GeneratedValue Projet projet, String fonction) { private int id; this.employe = employe; @ManyToOne this.projet = projet; private Employe employe; this.fonction = fonction; @ManyToOne } private Projet projet; // Accesseurs pour employe, projet et // fonction private String fonction; ...

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

73 74

Classes Employe et Projet Pas de persistance par transitivité automatique avec JPA  @Entity public class Employe { @Id private int id;  Par défaut, JPA n’effectue pas automatiquement @OneToMany(mappedBy="employe", cascade=CascadeType.ALL) la persistance par transitivité : private Collection participations; rendre persistant un département ne suffit pas à . . . rendre persistants ses employés }  @Entity public class Projet {  La cohérence des données de la BD repose @Id private int id; donc sur le code de l’application @OneToMany(mappedBy="projet", cascade=CascadeType.REMOVE) private Collection participations; . . . }

R. Grin JPA page 75 R. Grin JPA page 76

75 76

Cohérence des données Cascade

 Classe Departement contient une collection  Les opérations persist, remove, refresh, d’Employe merge, detach peuvent être transmises par  Au moment du commit, si un département est transitivité à l’autre bout d’une association persistant et si un des employés du département  Exemple : ne l’est pas, une IllegalStateException est @OneToMany( lancée cascade = { CascadeType.PERSIST, CascadeType.MERGE },  L’attribut cascade des associations peut simplifier mappedBy = "client") le code private Collection factures; Dans quelle classe est ce code ? Qu’indique l’attribut cascade ? R. Grin JPA page 77 R. Grin JPA page 78

77 78

13 Attribut orphanRemoval Exemple

 Pour traduire la « composition » UML @Entity  Si une annotation @OneToMany a l’attribut public class Facture { orphanRemoval à true, les entités enlevées de ... la collection seront supprimées de la BD @OneToMany(mappedBy="facture", cascade=CascadeType.ALL,  Implique une cascade sur remove orphanRemoval=true)  Facilite en particulier la suppression private Collection lignes; d’associations M-N avec classe association : ... sans cet attribut, il ne faudrait pas oublier de } lancer un remove de l’entité « association » Explication ? après l’avoir enlevée de la collection

R. Grin JPA page 79 R. Grin JPA page 80

79 80

Récupération des entités associées EAGER ou LAZY

 JPA laisse le choix de récupérer ou non  Lorsqu’une entité est récupérée depuis la BD immédiatement les entités associées (Query ou find), est-ce que les entités associées sont aussi récupérées ?  Mode EAGER : les données associées sont récupérées immédiatement  Si elles sont récupérées, est-ce que les entités associées à ces entités sont elles aussi  Mode LAZY (lazy loading) : les données récupérées ? associées ne sont récupérées que lorsque c’est vraiment nécessaire  Le risque est de récupérer un très grand nombre d’entités inutiles

R. Grin JPA page 81 R. Grin JPA page 82

81 82

Récupération tardive Mode de récupération par défaut des entités associées  Supposons une association en mode lazy entre un département et ses employés  Mode EAGER pour les associations OneToOne  On récupère un département et ManyToOne  Plus tard on veut avoir le nom d’un employé de  Mode LAZY pour les associations OneToMany ce département et ManyToMany  Cet employé est récupéré automatiquement par l’EM si l’entité département n’est pas Vous voyez une raison pour ce choix ? détachée  Et si le département est détaché ?  Il faut faire avant un merge du département

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

83 84

14 Indiquer le type de récupération des entités associées

 L’attribut fetch permet de modifier le comportement par défaut Signification ?  @OneToMany(mappedBy = "departement", fetch = FetchType.EAGER) Héritage private Collection employes;  Il est rare de toujours vouloir récupérer toujours une collection en mode EAGER ; le plus souvent on la récupère « au coup par coup », lors d’une requête ou d’un find (voir section « Requêtes – JPQL »)

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

85 86

Stratégies Toutes les classes traduites par une seule table  2 stratégies pour traduire une hiérarchie d’héritage en relationnel :  une seule table (SINGLE_TABLE) ; par défaut  une table par classe ; les tables sont jointes pour reconstituer les données (JOINED)  La stratégie « une table par classe non abstraite » est optionnelle (TABLE_PER_CLASS) Pour différencier les types de personnes R. Grin JPA page 87 R. Grin JPA page 88

87 88

Exemple « 1 table par hiérarchie » Une table par classe Dans la classe @Entity racine de la Colonne @Inheritance(strategy = hiérarchie ; discriminatrice InheritanceType.SINGLE_TABLE) optionnel public abstract class Personne {...}

@Entity « Employe » par défaut @DiscriminatorValue("E") public class Employe extends Personne { ... }

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

89 90

15 Préservation de l’identité Exemple

@Entity @Inheritance(strategy = InheritanceType.JOINED) public abstract class Personne {...}

Clé étrangère et primaire @Entity @DiscriminatorValue("E") « Employe » par défaut public class Employe extends Personne { ... }

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

91 92

@Entity public abstract class Personne {...}

@Entity Requêtes – JPQL public class Employe extends Personne { Java Persistence Query Language ... }

Quelle sera la stratégie utilisée pour traduire l’héritage ?

R. Grin JPA page 93 R. Grin JPA page 94

93 94

IMPORTANT Chercher par identité

 Cette section concerne les entités retrouvées  find retrouve une entité en donnant son en interrogeant la base de données identificateur dans la BD : Departement dept =  Rappel : ces entités sont automatiquement em.find(Departement.class, 10); gérées par le gestionnaire d’entités  Les modifications apportées à ces entités seront enregistrées au prochain commit

R. Grin JPA page 95 R. Grin JPA page 96

95 96

16 Étapes pour récupérer des données

 Il est possible de rechercher des données sur 1. Décrire ce qui est recherché (langage JPQL) des critères plus complexes que la simple 2. Créer une instance de type Query identité 3. Initialiser la requête (paramètres, pagination) 4. Lancer l’exécution de la requête String s = "select e from Employe as e"; Query query = em.createQuery(s); List listeEmployes = (List)query.getResultList();

cast obligatoire R. Grin JPA page 97 R. Grin JPA page 98

97 98

Exemple plus complexe Exemple plus complexe - chaînage

String q = "select e from Employe as e " String q = "select e from Employe as e " + "where e.departement.numero = :numero"; + "where e.departement.numero = :numero"; Query query = em.createQuery(q); Query query = em.createQuery(q); query.setParameter("numero", 10); query.setParameter("numero", 10) query.setMaxResults(30); .setMaxResults(30); for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) { query.setFirstResult(30 * i); query.setFirstResult(30 * i); List listeEmployes = List listeEmployes = (List)query.getResultList(); (List)query.getResultList(); ... // Traitement « page » numéro i + 1 ... // Traitement « page » numéro i + 1 } }

R. Grin JPA page 99 R. Grin JPA page 100

99 100

Langage JPQL Exemples de requêtes Alias de classe obligatoire  Java Persistence Query Language décrit ce qui  select e from Employe e est recherché en utilisant le modèle objet (pas  select e.nom, e.salaire from Employe e le modèle relationnel)  select e from Employe e where e.departement.nom = 'Direction'  Les seules classes qui peuvent être utilisées sont les entités et les Embeddable  select d.nom, avg(e.salaire) from Departement d join d.employes e group by d.nom having count(d.nom) > 5

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

101 102

17 Obtenir le résultat de la requête Type d’un élément du résultat

 Résultat = une seule valeur ou entité : Object getSingleResult()  Le type d’un élément du résultat est  Résultat = plusieurs valeurs ou entités :  Object si la clause select ne comporte List getResultList() qu’une seule expression  On peut aussi récupérer les données dans un  Object[] sinon Stream avec la méthode getResultStream() Attention, ne pas utiliser le stream pour faire des choses que le SGBD sait mieux faire, par exemple une sélection

R. Grin JPA page 103 R. Grin JPA page 104

103 104

Exemple Types de requête

texte = "select e.nom, e.salaire "  Requête dynamique : texte donné en + " from Employe as e"; paramètre de createQuery query = em.createQuery(texte);  Requête nommée : texte statique donné dans Qui peut expliquer ? List liste = une annotation d’une entité ; le nom est passé (List)query.getResultList(); en paramètre de createNamedQuery for (Object[] info : liste) {  Requête native (ou SQL) particulière à un System.out.println(info[0] + " gagne " SGBD : requête SQL (pas JPQL) avec tables + info[1]); et colonnes (pas classes et attributs) ; pour } créer des tables ou des vues ou pour une requête non standard ; createNativeQuery

R. Grin JPA page 105 R. Grin JPA page 106

105 106

Exemple de requête nommée Exemples de paramètres

@Entity  Query query = em.createQuery( @NamedQuery ( "select e from Employe as e " name = "employe.findNomsEmpsDept", + "where e.nom = :nom"); query = "select e.nom from Employe as e where query.setParameter("nom", "Dupond"); upper(e.departement.nom) = :nomDept"  Query query = em.createQuery( ) "select e from Employe as e " public class Employe extends Personne { + "where e.nom = ?1"); query.setParameter(1, "Dupond"); ...

Query q = em.createNamedQuery( "employe.findNomsEmpsDept");

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

107 108

18 TypedQuery Exemples

 Quand on connait le type T retourné par une String s = "select e from Employe as e"; requête, il vaut mieux utiliser l’interface TypedQuery query = TypedQuery plutôt que Query car le em.createQuery(s, Employe.class); compilateur peut faire plus de vérifications et List liste = query.getResultList(); on évite un cast Plus besoin de cast  Par exemple, getResultList() retourne List (type générique) et pas List TypedQuery query = em.createNamedQuery("Compte.findAll", Compte.class);

R. Grin JPA page 109 R. Grin JPA page 110

109 110

Clauses d’un select Navigation  Il est possible de suivre le chemin  select correspondant à une association avec la  from JPQL permet les sous-requêtes notation « pointée » et même les sous-requêtes  where synchronisées  Si e alias pour Employe,  group by  « e.departement » désigne le département  having d’un employé ; l’entité Employe doit avoir un attribut departement  order by  De même « e.projets » désigne les projets auxquels participe un employé

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

111 112

Règles pour les Contre-exemple expressions de chemin  « e.projets.nom » est interdit car  Si une navigation ne donne qu’une seule e.projets est une collection entité (OneToOne ou ManyToOne) on peut  Pour avoir les noms des employés du chaîner une navigation à la suite département « Direction » et les noms des  select e.nom, projets auxquels ils participent, il faut une e.departement.nom, jointure : e.superieur.departement.nom select e.nom, p.nom from Employe e from Employe e join e.projets p where e.departement.nom = 'Direction'

R. Grin JPA page 113 R. Grin JPA page 114

113 114

19 Jointure Récupération entités associées

 Interne (jointure standard join)  Le plus simple est la clause join fetch de  Externe (left outer join) JPQL (ou le pendant dans l’API « criteria »)  Avec récupération de données en mémoire  La méthode find (Map passée en dernier (join fetch) ; pour éviter le problème des paramètre), l’interface Query (méthode « N +1 selects » setHint) ou l’annotation @NamedQuery (attribut hints) permettent de donner des « hints »  Jointure « à la SQL » : égalité de 2 valeurs, sans tenir compte des associations entre (indications, conseils) entités  Par ces hints on peut désigner un graphe d’entités (pas étudié dans ce cours) qui indique quelles entités associées on veut récupérer

R. Grin JPA page 115 R. Grin JPA page 116

115 116

join fetch Problèmes des N + 1 selects

 On veut les noms des projets auxquels  select e from Employe as e participent les N employés join fetch e.participations  On commence par récupérer les employés avec Les participations sont créées dans la mémoire un select (1 select SQL) : centrale en même temps que les employés select e from Employe e (sans fetch join) (mais pas retournées par le select)  Pour chaque employé, e.getProjets() (pour  L’instruction récupérer les projets de l’employé) déclenche unEmploye.getParticipations() un select (N select SQL pour les N employés) ne nécessitera pas d’accès à la base  N + 1 select seront donc nécessaires, alors  Permet de résoudre le problème des « N + 1 qu’un seul select SQL (avec jointure externe) selects » suffit à récupérer toutes les informations R. Grin JPA page 117 R. Grin JPA page 118

117 118

Problèmes avec join fetch Graphe d’entités

 La requête SQL avec un join fetch fait une  Permet d’indiquer quels champs d’une entité, jointure pour récupérer les entités préchargées ou quelles entités associées, sont récupérés  Ce traitement peut occasionner des doublons dans la base de données lors d’un find ou d’une requête  Pour les éviter, il faut ajouter distinct dans le texte de la requête ou placer le résultat  Utile pour indiquer, pour chaque requête, ce dans une collection de type Set qu’on veut charger en mémoire  Un graphe d’entités est défini par annotation ou par programmation

R. Grin JPA page 119 R. Grin JPA page 120

119 120

20 select new Flush mode

 Pour retourner des instances d’une classe dont  Une requête est traduite par une requête SQL le constructeur prend en paramètre des qui ne tient pas nécessairement compte des informations récupérées dans la base de modifications déjà effectuées dans la données transaction  Classe doit être désignée par son nom complet  Pour en tenir compte, par défaut, un flush est  Exemple : effectué par JPA avant chaque requête select new p1.p2.Classe(e.nom, e.salaire)  Ce flush peut nuire aux performances ; from Employe e s’il est inutile, modifier ce comportement : query.setFlushMode(FlushModeType.COMMIT) Peut servir à créer des DTO « à la volée »

R. Grin JPA page 121 R. Grin JPA page 122

121 122

Fonctions dans une requête Exemples

 Pour les chaînes de caractères : concat,  select count(c) from Customer c substring, trim, lower, upper, length,  select a.description, locate case type(a)  Arithmétique : abs, sqrt, mod, size when Stylo then 'stylo' when Ramette then 'ramette'  Temporelles : current_date, current_time, else 'autre type' current_timestamp end  Fonctions de regroupement : count, max, min, from Article a avg ; count(distinct expression)  case (semblable CASE de SQL)

R. Grin JPA page 123 R. Grin JPA page 124

123 124

Fonction personnalisée Procédure stockée

 Une fonction personnalisée définie dans le  On peut utiliser une procédure stockée définie SGBD peut être appelée avec function() : dans le SGBD select a from Auteur a  La procédure stockée est décrite (noms, WHERE a.id = function('calculer', 1, 2) paramètres, valeur de retour) par @NamedStoredProcedureQuery (comme @NamedQuery)  Elle est transformée en StoredProcedureQuery par em.createNamedStoredProcedureQuery(), et exécutée par query.execute()

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

125 126

21 API « criteria »

 Tout ce qui peut être fait avec JPQL peut l’être avec cette API  Les requêtes JPQL sont des String qui peuvent contenir des erreurs (par exemple le Opération de modification nom d’une classe qui n’existe pas) en volume  L’avantage de l’API « critères » est que les requêtes peuvent être vérifiées à la compilation  L’inconvénient est que le code est un peu plus complexe à écrire, et sans doute moins lisible

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

127 128

Un cas d’utilisation Exemple

 Augmenter les 10 000 employés de 5% String ordre = "update Employe e " +  Une solution : " set e.salaire = e.salaire * 1.05"; 1. Créer toutes les entités « Employe » en Query q = em.createQuery(ordre); lisant les données dans la base int nbEntitesModif = q.executeUpdate();

2. Modifier le salaire de chaque entité

3. Enregistrer ces modifications dans la base  Mauvaise solution ! Pourquoi ?  JPQL permet de modifier les données de la base directement, sans créer les entités correspondantes et avec une seule requête SQL R. Grin JPA page 129 R. Grin JPA page 130

129 130

Gestion de la concurrence  Par défaut, les problèmes d’accès concurrents sont gérés avec une stratégie optimiste :  on pense qu’aucune autre transaction ne modifiera les données sur lesquelles on va Concurrence travailler  donc aucun blocage n’est effectué dans la base de données  au moment du commit, blocage court et vérifications pour voir si on avait raison d’être optimiste  si on avait tort, rollback de la transaction R. Grin JPA page 131 R. Grin JPA page 132

131 132

22 @Version lock(A, mode)

 Pour savoir si l’état d’une entité a été modifié  Protège une entité contre les accès entre 2 moments différents, un numéro de concurrents pour les cas où la protection version est ajouté à l’entité et enregistré dans offerte par défaut ne suffit pas la base de données  Permet en particulier d’effectuer un blocage  Exemple : pessimiste (blocage dans la base) @Version private int version;  Ce numéro est incrémenté automatiquement par JPA à chaque modification  Il ne doit jamais être modifié par l’application

R. Grin JPA page 133 R. Grin JPA page 134

133 134

Variantes avec lock Exemples

 Les méthodes find et refresh de  Personne p = em.find(Person.class, id, LockModeType.PESSIMISTIC_WRITE); EntityManager ont une variante qui permet  em.refresh(p, LockModeType.OPTIMISTIC_FORCE_INCREMENT); de bloquer les données liées aux entités  Query q = em.createQuery(...); retrouvées ou rafraichies q.setLockMode(LockModeType.PESSIMISTIC_FORCE_INCREMENT);  @NamedQuery(name="person.LockQuery",  L’interface Query a la méthode setLockMode query="SELECT p FROM Person p WHERE p.name LIKE :nom", pour bloquer les données récupérées par la lockMode=PESSIMISTIC_READ) requête  @NamedQuery a un attribut lockMode

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

135 136

Comparaison optimiste - pessimiste Concurrence et entité détachée

 Une entité est détachée de son EM  Un traitement pessimiste peut conduire à des blocages qui prennent du temps et aussi à  Elle est modifiée des interblocages  Quand elle est rattachée à un EM, l’EM vérifie  Si les conflits ne sont pas trop fréquents le au moment du commit que le numéro de traitement optimiste est recommandé version n’a pas changé  Sinon un traitement optimiste peut conduire à  Si le numéro a changé, une trop d’annulations de transactions et il vaut OptimisticLockException est levée mieux alors utiliser les blocages du traitement pessimiste

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

137 138

23 Position du problème

 Parfois les structures des bases préexistantes ne respectent pas les bons usages ou ne Configuration des tables SQL – correspondent pas aux valeurs par défaut Adaptation du code Java à une supposées par JPA, pour des raisons diverses base préexistante  C’est souvent le cas pour ce qui concerne la modélisation des associations entre entités  JPA peut s’adapter à presque toutes les situations préexistantes, le plus souvent en utilisant des annotations

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

139 140

Annotations Définition des colonnes

 @Column pour changer le nom  Si les tables relationnelles sont créées à  @Table pour changer le nom d’une table partir du code Java des entités, la notation @Column permet de donner une définition  @JoinColumn permet de changer le nom d’une colonne clé étrangère précise du type SQL utilisé pour un attribut d’une entité  On peut même répartir les données d’une entité sur plusieurs tables avec @SecondaryTable

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

141 142

Exemple Bibliographie

 Le type Java BigDecimal correspond par  Pro JPA 2 in Java EE 8 de Mike Keith, défaut au type SQL DECIMAL mais sans Merrick Schincariol, Massimo Nardone, chiffres après la virgule édtions Apress  @Column permet d’indiquer la définition que l’on veut : @Column(columnDefinition = "DECIMAL(12,2)") private BigDecimal prix;

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

143 144

24