Master e-services 2015
JPA (Java Persistance Api) est une norme Java EE permettant de lier des objets à des données contenues dans une base de données. Plus exactement, dans toute source de données possédant une implémentation correspondante ...
JPA est essentiellement utilisé pour la manipulation des bases de données relationnelles construites à partir du développement orienté objet en Java. Comme tout ORM, JPA fourni une abstraction d'accès à la base de données. Il n'est donc plus question d'écrire du code SQL "natif" de votre base. Les opérations de création, recherche, mise à jour et suppression des données sont complètement accessibles via des méthodes génériques.
En fonction de la persistance choisie, l'implémentation JPA pour votre base se chargera de traduire ces appels en SQL (ou autre) valide.
Essayons de mettre en oeuvre une persistance JPA avec l'implémentation Hibernate par dessus une base de données "H2".
Hibernate est un "gros" framework qui implémente les fonctionnalités de JPA. Il possède un nombre assez important de dépendances. Il devient assez vite compliqué de vouloir chercher toutes ces dépendances "à la main". Nous allons plutôt utiliser maven pour gérer tout cela.
Il faut faire de notre projet java, un projet "maven". Cela se fait simplement en ajoutant un fichier "pom.xml" à la racine.
Un fichier pom décrit notre projet en "artefact", dans un "group" et possédant une "version".
Le projet étant "packagé" selon un format défini.
<groupId>fr.eservices.xxVotreNomxx</groupId>
<artifactId>projet-jee</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
Le pom contiendra également le nom du projet et différentes informations nécessaires à la compilation ou au packaging de notre projet.
Le plus intéressant : cela contiendra des dépendances vers des librairies, qui elles-même peuvent tirer d'autres par dépendances transitives.
Les dépendances sont normalement présentes sur un serveur "central" maven. C'est un site regroupant toutes les librairies de projets qui souhaitent être diffusés/utilisés.
Il est aussi possible d'ajouter d'autres "repository" par de la configuration dans le pom, ou dans les settings de maven.
Les dépendances seront stockée sur votre ordinateur dans un repository local, par défaut dans ~/.m2/repository.
Il est possible d'ajouter manuellement des librairies dans ce repository. Ce qui peut être utile pour les librairies qui ne sont pas fourni au format maven (avec un pom ou sur le repo central ...).
La conception interne de maven est modulaire et chaque "plugin" est responsable d'une fonctionnalité. Ces mêmes plugins sont également des artefacts maven disponible sur le repo central et téléchargés au besoin.
Ainsi, c'est le plugin "install" qui va gérer les insersions dans le repo local.
Pour mettre un jar quelconque dans votre repo local on utilisera la commande :
mvn install:install-file -Dfile=fichier.jar -DgroupId=mon.groupe -DartifactId=nom-librairie -Dversion=version -Dpackaging=typeArtefactLa version est nécessairement numérique, séparée par des points.
Il faut distinguer différents ensembles de dépendances, nommées "scope" dans le vocabulaire maven.
» compile : (par défaut) indique que cette librairie est nécessaire pour la compilation et à l'exécution.
» runtime : uniquement nécessaire à l'exécution.
» provided : sera disponible à l'exécution mais n'est pas fourni par ce projet. Exemple : le "servlet-api" est fourni par tomcat et n'est pas amené par notre projet web. "servlet-api" est donc "provided". Cette librairie reste nécessaire à la compilation.
» test : librairie nécessaire uniquement dans l'exécution de tests unitaires. Exemple : junit.
Par convention, maven supposera une certaine structure de dossier de votre projet :
Vous trouverez dans ce fichier pom.xml une structure de base décrivant votre projet.
Vous pouvez ensuite executer mvn dependency:resolve
pour résoudre et télécharger toutes les dépendances.
Elles se rangeront dans votre repo local qui pourra vite devenir assez volumineux si vous travaillez régulièrement avec maven sur différents projets.
Rappel : Vous pouvez modifier votre .m2/settings.xml pour modifier l'emplacement où maven rangera toutes les dépendances.
Visualisez l'arbre des dépendances avec mvn dependency:tree
.
Un plugin permet de mettre à jour les dépendances d'éclipse en fonction de ce que contient le pom.xml
Je parle ici d'un plugin eclipse pour maven, à ne pas confondre avec l'inverse (ex: plugin m2e d'eclipse).
Ce plugin va en fait mettre à jour (ou créer) les fichiers .project et .classpath utilisés par eclipse.
Cela permettra d'ajouter les dépendances dans le classpath d'eclipse.
Pour cela utilisez mvn eclipse:eclipse
Et rafraîchissez votre projet avec F5, ou clic droit/refresh.
Il faudra également définir la variable "M2_REPO" dans votre workspace pour indiquer à eclipse où est rangé le repository maven.
Rendez-vous dans Outils (ou Eclipse) / Préférence / Java / Build Path / Classpath Variables.
Ajouter une variable "M2_REPO" qui pointe vers "home"/.m2/repository
Avec JPA, nous pouvons annoter nos classes JAVA pour indiquer que les données de ces classes seront persistées. De manière simple, il "suffit" de placer l'annotation @Entity
sur une classe pour que cela fonctionne ... ou presque. Il est nécessaire que les propriétés de l'objet à persister soient accessibles par le gestionnaire d'entité.
Comprenez :
- soit une propriété public ;
- soit un getter / setter avec le nom qui correspond getPropriété et setPropriété( valeur ).
- sinon l'implémentation JPA (hibernate) pourra essayer de modifier dynamiquement le bytecode pour rendre ces propriétés accessibles.
Pour certaines propriétés, il est utile de mettre d'autres annotations pour indiquer comment le gestionnaire ou la base se comportera. Par exemple l'identifiant de l'entité doit être annoté @Id
. Nous pouvons aussi indiquer que cette propriété sera générée par la base via une séquence (ou auto increment). Pour cela nous ajoutons la propriété @GeneratedValue
Pour marquer les relations entre nos entités il est possible d'utiliser les annotations :
» @OneToMany
: pour désigner une relation 1-n, soit une instance faisant référence à plusieurs autre. Permet d'accéder aux objets du cotés N à travers une liste. A utiliser avec vigilance selon la volumétrie des données associées.
» @ManyToOne
: pour désigner, une relation inverse de 1-n. Permet d'accéder facilement à l'objet du coté "1".
» @OneToOne
: pour désigner une relation 1-1 entre deux objets. Peut être utile pour séparer un ensemble de propriétés dont le sens est différent. Peut être utile pour concevoir une relation d'héritage également.
» @ManyToMany
: pour les relations n-n devenant une table associative.
Le "contexte de persistance" JPA se configure avec un fichier "persistence.xml" placé dans le classpath, dans un répertoire "META-INF/". Le fichier pom fourni est configuré pour placer toutes les "ressources" utiles (et copiées dans le classpath par maven) dans WEB-INF/resources. Donc, il vous faut créer un répertoire WEB-INF/resources/META-INF et y mettre ce fichier.
Jettez-y un oeil et modifiez le selon vos besoins.
Prétez notamment attention au paramètre "hbm2ddl.auto" et lisez cette page de stackOverflow.
Vous pouvez consulter la classe DemoJPA pour voir un exemple simple d'usage de l'entity manager et des transactions. Toutes les requêtes JPA doivent être exécutées dans une transaction, c'est ainsi. On "oblige" le développeur à se soucier de ce qui a du sens en terme d'état de base de données. Si une Exception survient durant la transaction, les modifications en base ne sont pas appliquées (ou défaites).
L'Entity Manager permet de sauvegarder (persist), charger (find), ou créer des requêtes exprimées en JPQL ... une sorte de SQL orienté objet. Notez qu'il est aussi possible de créer des requêtes à l'aide de l'API Criteria de JPA. Pour tout cela, il y a la doc. Consultez par exemple ce site ou encore LA doc officielle, un peu moins agréable.
Vous pouvez aussi vous plonger dans la javadoc, encore plus austère.
Reprenez le diagramme UML ci-dessous et implémentez le à l'aide d'annotation jpa.
L'ensemble des entitées seront situées dans le package "api.model".
N'implémentez pas la persistance de "Reservation" et "Secteur" dans un premier temps.
mvn install:install-file -Dfile=check.jar -DgroupId=fr.eservices -DartifactId=code-checker -Dversion=1.5 -Dpackaging=jar
mvn exec:java -Dexec.mainClass="tool.Check" -Dexec.classpathScope=test -Dexec.args="check-jpa"
( autonomie )
Créer une classe de test afin de vérifier le bon comportement de votre modèle et de l'entityManager lors des opérations de persistance, recherche par clé, modification et suppression.
Essayer d'effectuer une recherche selon un critère portant sur un attribut d'une classe persister. Exemple : Tous les pilotes de plus de 50 ans.
Ou encore les vols de plus de 5h.
Les associations N-N de notre modèle portent des informations. Dans ce cas de figure, il est préférable d'utiliser deux liens OneToMany vers une classe qui portera ces informations.
Pour gérer l'association correctement, il y a ensuite deux stratégie concernant la clé primaire :
1. Réaliser une clé composée à l'aide de EmbeddedId ; (Voir cette explication)
2. Ajouter une simple clé (colonne supplémentaire) et mettre une contrainte d'unicité sur les colonnes des deux clé étrangères. ; (Voir cette explication)
Essayez d'implémentez ces deux stratégies sur votre modèle, dans les Objets "Reservation" et "Secteur".
Sans y passer trop de temps vous pouvez regarder l'exercice d'initiation proposé en 2013.
Implémentez la partie JPA. Notez que cet exercice comporte des tests unitaires, c'est l'occasion de de voir comment cela fonctionne.
Une fois fini, vous pouvez aussi comparer votre travail avec la correction.