Java Persistence API

Home (Contact)

Objectif du TP :

Présenter le standard JPA pour la persistance des objets en Java.

Support de cours, partie 1 Support de cours, partie 2


L'API pour la persistance des objets Java (JPA 2.0) permet de rendre persistant des objets Java sans avoir besoin d'un serveur d'application. Ce TP utilise l'implémentation de référence pour JPA, EclipseLink.

Il est impossible en un seul TP de voir toutes les possibilités offertes par JPA. En particulier, ce TP n'aborde pas les entités détachées qui sont pourtant importantes dans la pratique lorsque l'application est distribuée sur plusieurs couches distantes.


Librairies du projet

Créer un nouveau projet de type "Java Application".

2 librairies doivent être ajoutées au projet :

Pour le driver JDBC, il suffit d'ajouter dans la librairie que vous avez créée dans le TP sur JDBC.

Pour l'implémentation de JPA, ajoutez la libraire "EclipseLink (JPA 2.0)" qui est préinstallée dans NetBeans. 3 jars devraient être installés (ouvrir l'entrée Libraries pour vérifier).

Pour avoir facilement la javadoc des classes de JPA depuis NetBeans, vous allez modifier la définition de la librairie "EclipseLink (JPA 2.0)"

Javadoc API JPA

Spécification JPA 2.0


Modèle des données

Vous allez écrire les classes Java qui correspondent à la base de données des employés d'une entreprise. Vous allez utiliser la même base de données que pour le TP sur JDBC.

Classes :

Dans le deuxième exercice, pour illustrer les problèmes liés à l'héritage, vous ajouterez une classe Personne (avec un champ nom et un identificateur non significatif) dont hérite la classe Employe, et une classe Client (avec seulement une adresse pour simplifier) qui hérite aussi de Personne. La classe Adresse ne sera pas une entité mais une classe insérée (Embedded).

IMPORTANT : voici une description des associations que vos classes devront implémenter :

Pour vous faire gagner du temps, des squelettes de classes vous sont fournis dans un fichier zip. Pour le moment ne faites rien ; durant les exercices il vous faudra y ajouter des annotations ou même éventuellement d'autres informations. Dans ce zip vous trouverez aussi un fichier persistence.xml que vous devrez placer au bon endroit (dans un répertoire META-INF, dans le classpath) et modifier pour indiquer les bonnes informations pour la connexion à la base de données et pour ajouter des noms de classes entités. Des propriétés propres à EclipseLink ont été positionnées pour faire afficher des informations sur le déroulement de l'exécution de JPA, en particulier les ordres SQL générés (lisez la console pendant l'exécution du programme) et pour recréer les tables à chaque exécution (pratique pour tester mais, évidemment, à enlever en production) ; vous devrez enlever ou modifier la valeur de cette dernière propriété à partir d'un certain moment si vous voulez conserver les données déjà dans la base de données.

Dans la réalité on peut avoir besoin de changer le nom des tables créées automatiquement. Vous ferez générer les tables PROJET2, PARTICIPATION2 (remarquez le "2" final pour les 2 dernières tables) au lieu de PROJET et de PARTICIPATION. Toutes les autres tables auront les valeurs par défaut prévues par JPA pour les noms de tables ; par exemple, la table associée à la classe Departement s'appellera DEPARTEMENT (pour les différencier des classes, on mettra dans cet énoncé les noms des tables en majuscules).


Pour se chauffer...

Juste pour tester, vous allez travailler avec uniquement les départements, sans les autres classes.

  1. Ajoutez la classe Departement au projet en copiant la classe du fichier zip. Dans la classe Departement, mettez en commentaires tout le code qui est lié à l'association avec Employe (c'est-à-dire le code qui crée des erreurs puisque la classe Employe n'existe pas encore).
  2. Complétez la classe Departement pour la transformer en entité JPA. Les numéros de département devront être générés automatiquement.
  3. NetBeans va s'apercevoir qu'il vous manque une unité de persistance et va vous proposer d'en créer une. Acceptez ; donnez-lui le nom employes et indiquez qu'elle sera liée à la même base de données que vous avez utilisé dans le TP sur JDBC (base employes) ; un répertoire META-INF va être créé avec un fichier persistence.xml. Étudiez ce fichier ; pour voir le code XML, cliquez sur le bouton "Source" au-dessus de la fenêtre d'édition du fichier. Il vous reste à ajouter les classes entités et les propriétés supplémentaires qui sont dans le fichier persistence.xml du fichier zip qui vous a été fourni. Il vous faudra aussi ajouter dans persistence.xml la classe Departement dans la liste des classes gérées par JPA.
  4. La méthode main de la classe Test1 crée 3 départements dont seulement 2 seront rangés dans la base de données. Le lieu de l'emplacement d'un des départements est modifié après sa création et après l'appel de persist et pourtant cette modification est bien enregistrée dans la base (vérifiez-le). Si vous ne savez pas pourquoi, demandez à l'enseignant qui encadre votre TP car ce fait est lié à une notion importante.
  5. Lancez l'exécution de Test1 (n'oubliez pas de démarrer le SGBD avant). Lisez les informations affichées dans le volet Ouput durant l'exécution, en particulier les informations de niveau "Fine" sur les requêtes SQL qui créent les tables et qui insèrent les données dans les tables. A la fin de l'exécution de cette méthode main, la base de données devra contenir les données associées aux départements ajoutés dans la méthode main. Vérifiez aussi la structure de la table DEPARTEMENT (un refresh peut être nécessaire pour la voir). Remarquez aussi que le département de Nantes n'a pas été enregistré dans la base de données. Une table SEQUENCE a aussi été créée ; à quoi peut-elle servir ? Vous étudierez ces définitions à chaque fois que vous modifierez votre modèle objet dans les prochains exercices pour voir comment JPA effectue le mapping objet-relationnel.
  6. Que se passe-t-il si vous relancez une deuxième fois Test1 ? Et si vous modifiez le fichier persistence.xml pour ne pas supprimer toutes les tables à chaque nouvelle exécution ? Testez en remplaçant <property name="toplink.ddl-generation" value="drop-and-create-tables"/> par <property name="toplink.ddl-generation" value="create-tables"/> et en relançant Test1.

Correction :

Les classes
persistence.xml
Dernière question


Héritage et association 1:N

Enlevez les commentaires que vous avez mis dans la question précédente dans la classe Departement pour tout ce qui est lié à l'association avec la classe Employe. Complétez les classes Personne, Employe et Client. Vous les utiliserez avec cette classe Test2.

Utilisez la stratégie "une seule table par arborescence d'héritage".

A la fin de l'exécution de cette méthode main, la base de données devra contenir les bonnes informations. Vérifiez-le ; en particulier, regardez bien la table des employés.

Correction

Définition de l'unité de persistance
Les classes


Langage d'interrogation de JPA

Commencez par modifier le fichier persistence.xml pour ne pas écraser les tables existantes : remplacez drop-and-create par create. Vous pouvez aussi en profiter pour changer le niveau de log pour que les sorties des requêtes ne soient pas noyées par les messages de log : vous pouvez passer à SEVERE (et revenir à FINE ou FINEST si vous avez des problèmes).

  1. Cette fois-ci vous allez écrire une classe Test3 qui va récupérer tous les employés du département "DIRECTION" (supposez que le nom du département est toujours donné en majuscule ; attention, le nom dans la base n'est pas toujours en majuscule) et qui affiche ensuite leur nom. Le nom du département devra être un paramètre de la requête. La réponse ne devra pas tenir compte de la casse dans le nom du département. Dans cette question et la suivante vous utiliserez des requêtes dynamiques.
  2. Écrivez une 2ème version du programme qui ne récupère pas les employés mais seulement leur nom et leur salaire dans la base de données et qui les affiche.
  3. Écrivez une 3ème version qui affiche les noms des employés, en utilisant une requête nommée.
  4. Utilisez la requête de la première version pour augmenter de 5 % le salaire des employés récupérés et pour enregistrer les modifications dans la base.
  5. Mettez tous les salaires des employés à 2200 euros par une modification "en volume" (bulk update). Vérifiez que cette opération n'a pas modifié les entités en mémoire. A cause de ce fait, il ne faut pas lancer une modification en volume si le contexte de persistance contient déjà des entités. Il est tout de même possible de synchroniser une entité en mémoire avec les valeurs de la base de données. Comment faire ? Testez.

Correction

Les classes


Association M:N

Complétez les classes Projet et Participation et Test4 dont la méthode main crée 3 employés, 2 projets et répartit les 3 employés dans ces 2 projets. Attention, c'est pour cet exercice qu'il faut penser à ne pas écraser des éventuelles tables PROJET et PARTICIPATION ; les tables devront s'appeler PROJET2 et PARTICIPATION2.

Voici la méthode de la classe Projet qui servira à ajouter un employé dans un projet :
public void ajouterParticipant(Employe employe, String fonction)
Le concepteur de l'application souhaite que l'entité Participation ne soit pas visible pour l'utilisateur de la classe Projet. Que devrez-vous faire pour rendre persistantes les participations sans que ces participations ne soient visibles ?

Lancez l'exécution de Test4. Vérifier que tout s'est bien passé en allant voir directement dans les tables de la base de données.

Que faudrait-il modifier si un employé pouvait avoir plusieurs fonctions dans un même projet ?

Correction

Définition de l'unité de persistance
Participation invisible
Les classes
Plusieurs fonctions dans un projet


Requêtes polymorphe (ou non). Navigation dans les requêtes

Ecrivez une classe Test5 dont la méthode main

Correction

Classe Test5


Pour ceux qui ont déjà fini...

N + 1 select

Récupérez tous les employés en dehors d'une transaction avec la requête "select e from Employe as e".

Faites afficher les noms de tous les employés.

Sans relancer une requête, en utilisant seulement les associations des objets Employe récupérés, récupérez tous les projets auxquels participent les employés. Faites afficher la liste des noms des employés ; un nom par ligne, suivi de tous les projets auxquels l'employé participe. Les noms des employés qui ne participent à aucun projet devront être affichés.

En utilisant le logging de EclipseLink, regardez quelles sont les requêtes SQL lancées par EclipseLink et à quel moment. Modifiez le mode de récupération de l'association entre les employés et les participations pour voir l'impact sur les requêtes SQL.

Comment éviter le problème des N + 1 selects ?

Correction

Classe Test6
Ordres SQL


Transactions

Il est très important de comprendre les relations qui lient les contextes de persistance et les transactions. Dans cet exercice vous allez tester quelques situations particulières.

  1. Créez un département et ajoutez-le à un contexte de persistance. Ne commencez pas de transaction et modifiez ce département (changez son lieu par exemple). Est-ce les modifications ont été effectuées dans la base de données ?
  2. A la suite du code qui crée le département et le modifie, ouvrez une transaction et refermez-la tout de suite. Est-ce que les modifications effectuées sur le département sont enregistrées dans la base de données ?
  3. Après avoir fermé la transaction, modifiez à nouveau le département et lancez un flush (sans ouvrir de transaction). Que se passe-t-il ?

Avertissement pour ceux qui vont travailler avec un serveur d'applications (avec Java EE) : par défaut les contextes de persistance seront alors limités à une transaction et le comportement ne sera pas le même que dans cet exercice où le contexte de persistance et les transactions ne sont pas gérés par le container mais par l'application.

Correction

Classe Test7


Entité versionnée

Ajoutez un attribut de version (@Version) dans l'entité Departement.

  1. Modifiez le nom d'un département et vérifiez si le numéro de version a été incrémenté.
  2. Ajoutez un employé dans un département et vérifiez si le numéro de version a été incrémenté.
  3. Testez un lock (READ) et écrivez du code Java pour obtenir une lecture répétable sur ce département. Essayez de modifier ce département dans une autre transaction en parallèle pour voir ce qui se passe (ne passez pas le département en paramètre, récupérez-le dans la 2ème transaction lancée en parallèle comme dans la première transaction). Quelle est la différence de comportement avec du code qui n'aurait pas exécuté un lock en mode READ ? Vérifiez.
  4. Utilisez un lock (WRITE) et écrivez du code Java pour faire incrémenter le numéro de version, sans modifier le département. Vérifiez en faisant afficher le numéro de version.

Correction

Classe Test9


Génération du schéma de la base de données

Recommencez le premier exercice en vous arrangeant pour que le nom du département soit limité à la longueur 25 dans la table DEPARTEMENT générée automatiquement.

Correction

Réponse


Retour