Master e-services 2016
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 fournit 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.
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 autres. 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.
Certaines associations N-N du modèle peuvent comporter 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égies 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és étrangères. (Voir cette explication)
Si vous utilisez JPA sans autre framework (type spring), le "contexte de persistance" se configure avec un fichier "persistence.xml" placé dans le classpath, dans un répertoire "META-INF/". Dans un projet maven, ce fichier est généralement placé dans les "resources" principales. Donc, il vous faut créer un répertoire WEB-INF/resources/META-INF et y mettre ce contenu :
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" version="2.1"> <persistence-unit name="myApp"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <properties> <property name="connection.driver_class" value="org.h2.Driver"/> <property name="hibernate.connection.url" value="jdbc:h2:./db"/> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <property name="hibernate.hbm2ddl.auto" value="create-drop"/> <property name="hibernate.show_sql" value="true"/> </properties> </persistence-unit> </persistence>
Ce fichier est évidemment à adapter selon vos besoins.
Prétez notamment attention au paramètre "hbm2ddl.auto" et lisez cette page de stackOverflow.
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.
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).
Bien que ce ne soit pas recommandé, il reste possible d'effectuer des requête SQL à travers l'API JPA.
Le mapping peut rester "automatique" si toutefois votre résultat de requête reste compatible avec l'objet à affecter.
String sql = "SELECT * FROM USER WHERE ID = ?"; Query query = entityManager.createNativeQuery(sql, User.class); query.setParameter(1, id); User user = (User) query.getSingleResult();