Dans le billet précédant [step15] nous avons créé les bases de données H2 et Derby constitué d’une table T_USER. Nous avons développé un petit code Java qui se connecte via JDBC à la base de données pour afficher la liste des User. Dans ce billet nous allons mettre en place JPA en mode « standalone » (sans utiliser un conteneur JPA) et développer des petits exemples qui récupère un User, une liste de User, qui insère un User dans la table T_USER des bases de données H2 et Derby avec les 2 implémentations JPA JPA/Hibernate et JPA/Eclipse Link :
Nous développerons les exemples décrits ci-dessus, avec les 2 implémentations JPA et les 2 bases de données :
- JPA/Hibernate – H2 : exemples qui utilise la base de données H2 avec l’implémentation JPA/Hibernate.
- JPA/Hibernate – Derby : exemples qui utilise la base de données Derby avec l’implémentation JPA/Hibernate.
- JPA/Eclipselink – H2 : exemples qui utilise la base de données H2 avec l’implémentation JPA/Eclipse Link.
- JPA/Eclipselink – Derby : exemples qui utilise la base de données Derby avec l’implémentation JPA/Eclipse Link.
Vous pouvez télécharger le zip org.dynaresume_step16.zip qui contient les projets expliqués ci dessous:
- org.dynaresume.test.jpa.hibernate : examples avec JPA/Hibernate.
- org.dynaresume.test.jpa.eclipselink : examples avec JPA/Eclipse Link.
- spring-target-platform-dao : librairies JARs récupérés via Maven dans le billet [step14].
- org.dynaresume.test.jpa : fusion des 2 projets org.dynaresume.test.jpa.hibernate et org.dynaresume.test.jpa.eclipselink pour montrer qu’il est possible d’avoir plusieurs implémentations JPA dans un même projet.
Pré-requis
Avant de démarrer ce billet vous devez avoir :
- créé la base de données H2 et copié la base H2 dynaresume.data.db dans C:/db/h2. Pour vérifier que la base H2 est bien installée, je vous conseille de tester la base H2.
- créé la base de données Derby et copié la base Derby (répertoire dynaresume ) dans le répertoire C:/db/derby. Pour vérifier que la base Derby est bien installée, je vous conseille de tester la base Derby.
Téléchargez le zip spring-target-platform-dao.zip qui contient les librairies JARs JPA, JPA/Hibernate,… récupérés via Maven dans le billet [step14] puis importez le projet spring-target-platform-dao dans votre workspace.
REMARQUE : nous avons créé la base de données via des scripts mais JPA est capable de générer la base de données en utilisant les informations de mapping.
JPA
ORM c’est quoi?
La plupart des projets s’appuient sur une base de données pour gérer leur données persistantes. La récupération/mise à jour de ces données s’effectuent via des requêtes SQL. En Java ceci s’effectue via l’API JDBC (Java DataBase Connectivity) ou la plupart des bases de données fournissent un driver JDBC pour exécuter des requêtes SQL en Java.
Avant l’arrivée de l’ORM, le développeur devait exécuter des requêtes SQL puis construire des POJO Java en récupérant les résultat des requêtes SQL Select. La mise à jour de la base de données s’effectuait via des requêtes SQL Insert/Update. Ce procédé était assez lourd à mettre en œuvre et des frameworks d’ORM ont fait leur apparition. Au lieu de manipuler directement la base de données, l’ORM permet de manipuler des instances POJO Java qui deviennent persistantes. Pour cela un mapping entre des classes POJO et les tables de la base doivent être effectués. Par exemple dans notre cas nous allons mapper les getter/setter login de la classe User avec la colonne USR_LOGIN_C de la table T_USER :
JPA c’est quoi?
L’un des premiers frameworks d’ORM en Java qui a vu le jour est Hibernate. Face au succès de ce framework, Sun a décidé de créer une spécification standard d’ORM qui s’appele JPA (Java Persistente API) et qui fait partie de la spécification EJB3. JPA est une API autrement dit elle ne fournit pas l’implémentation mais que des interfaces. Dans notre cas nous allons utiliser les implémentations JPA JPA/Hibernate et JPA/Eclipse Link.
Un conteneur JPA c’est quoi?
JPA peut être ensuite utilisé en « standalone » ou dans un conteneur JPA. Un conteneur JPA permet de
- gérer les connections (ouverture/fermeture). En JPA on parle de EntityManager.
- gérer les transactions (commit/rollback). En JPA on parle de EntityTransaction.
Le conteneur JPA décharge ainsi le développeur de ces problématiques et de ce travail fastidieux et évite de nombreux bugs d’oubli de fermeture de connexion par exemple.
EJB3 joue le rôle d’un conteneur JPA. Spring fournit aussi un support JPA et joue ainsi le rôle de conteneur JPA. Dans ce billet nous allons utiliser JPA en mode « standalone » et nous verrons dans le prochain billet comment utiliser Spring dans un contexte JPA et montrerons l’intérêt d’utiliser Spring avec JPA.
JPA – Principe
Voici grossièrement les différents concepts sur lequel s’appuie JPA :
- mapping qui permet de mapper les POJO avec les tables de la base de données.
- persistence.xml qui est le fichier de configuration JPA, qui permet de définir plusieurs unité de persistances (persistence-unit) qui décrivent l’implémentation JPA à utiliser, les informations de connection à la base de données, les classes persistantes du projet…
- EntityManagerFactory est chargé à partir d’une configuration persistence-unit et est utilisé pour obtenir un EntityManager qui est utilisé pour récupérer des POJO persistants, rendre persistent un POJO…
Mapping
JPA comme tout autre frameworks d’ORM nécéssite de définir un mapping entre les classes Java POJO (Domain) et les Tables de la base de données. En JPA ceci peut s’effectuer de 2 manières :
- via un fichier XML orm.xml.
- via des annotations Java.
Dans notre cas nous allons mettre en place le mapping via des Annotations.
persistence.xml
La configuration JPA s’effectue via un fichier nommé persistence.xml qui doit être stocké dans le répertoire META-INF. Voici un exemple de fichier XML persistence.xml que nous allons mettre en place :
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> <persistence-unit name="dynaresume-hibernate-h2" transaction-type="RESOURCE_LOCAL"> <provider> org.hibernate.ejb.HibernatePersistence </provider> <class>org.dynaresume.domain.User</class> <properties> <property name="hibernate.connection.driver_class" value="org.h2.Driver" /> <property name="hibernate.connection.url" value="jdbc:h2:C:/db/h2/dynaresume" /> <property name="hibernate.connection.username" value="sa" /> <property name="hibernate.connection.password" value="" /> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" /> <property name="hibernate.show_sql" value="true" /> </properties> </persistence-unit> </persistence>
REMARQUE : la déclaration provider/class/properties doit s’effectuer dans cet ordre si on souhaite être conforme au schema XML persistence_1_0.xsd. Si vous déclarez dans l’odre provider/properties/class ceci fonctionne très bien avec du JPA « standalone », mais plus avec Spring ORM.
Ce fichier permet de configurer plusieurs persistence-unit qui pour chacun d’eux définissent :
- l’implémentation JPA à utiliser via l’élement XML provider. Dans cet exemple cet élément provider est renseigné via org.hibernate.ejb.HibernatePersistence implémentation Hibernate de JPA.
- les classes persistentes du projet (qui sont mappés avec les tables de la base de données) via les éléments XML class.
- les informations de connection à la base de données via les éléments XML property.
EntityManagerFactory
La configuration JPA d’une unité de persistance persistence-unit (de nom dynaresume-hibernate-h2) décrite dans le fichier META-INF/persistence.xml est chargée dans une factory de gestionnaire d’entités EntityManagerFactory comme ceci :
String persistentUnitName = "dynaresume-hibernate-h2"; EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(persistentUnitName);
Elle permet de récupérer ensuite un gestionnaire d’entité EntityManager :
EntityManager entityManager = entityManagerFactory.createEntityManager();
A partir de ce gestionnaire d’entité, nous pouvons ensuite récupérer des entités persistentes (POJO Java), les mettre à jour… Par exemple voici le code qui permet de récupérer le User d’id 1 :
User user = entityManager.find(User.class, (long) 1);
Ce code engendre une requête Select SQL.
Voici le code qui permet de rendre persistent un POJO User (d’insérer un User) :
User user = new User("jpa-user", ""); entityManager.persist(user);
Ce code engendre une requête Insert SQL.
Dans mes billets traitant JPA, je ne vais PAS parler d’une utilisation avancée des mappings JPA, mais nous allons nous concentrer sur la factory de gestion d’entités JPA EntityManagerFactory. Dans ce billet une utilisation « standalone » de la factory EntityManagerFactory sera expliqué pour comprendre les principes de JPA. Nous verrons dans le billet suivant l’interêt d’utiliser JPA dans un conteneur géré par Spring. Si vous ne connaissez pas JPA, je vous conseille vivement de lire 43. JPA (Java Persistence API) qui décrit très bien tous les mappings possibles que l’on peut effectuer avec JPA.
Etapes pour mise en place JPA
Cette section liste les étapes à suivre pour mettre en place JPA dans un projet. Elle récapitule tout ce que nous allons effectuer en détail dans la section JPA/Hibernate – H2.
- Ajoutez les librairies JARs requises pour JPA :
- API JPA : com.springsource.javax.persistence-2.0.0.jar
- API Transaction : com.springsource.javax.transaction-1.1.0.jar
- Implémentation JPA. Dans le cas d’hibernate pensez à ajouter l’implémentation SFL4J (ex : avec log4j).
- Driver JDBC de la base que l’on souhaite utiliser.
- Définir le fichier persistence.xml dans le répertoire META-INF :
- en définissant un persistence-unit nommé.
- en indiquant l’implémentation JPA dans le persistence-unit via un provider.
- en configurant les paramètres de connections à la base de données (Driver JDBC, URL Base..). les properties
- Définir le mapping POJO-Table via des annotations ou un fichier orm.xml.
- Ouvrir/Fermer une connection avec l’EntityManager.
- Ouvrir/Fermer une Transaction EntityTransaction lors d’une MAJ.
JPA/Hibernate
Dans cette section nous allons mettre en place l’implémentation Hibernate de JPA et utiliser ensuite JPA pour afficher la liste des Users dans la console. Pour cette implémentation JPA nous allons écrire le code nécéssaire pour JPA pas à pas ce qui permettra de soulever toutes les erreurs que l’on peut avoir lorsque l’on met en place JPA.
Les JARs que nous allons utiliser sont ceux qui ont été récupéré via Maven dans le zip spring-target-platform-dao.zip. Ce zip contient le projet Eclipse spring-target-platform-dao que vous devez importer dans votre workspace.
Initialisation projet org.dynaresume.test.jpa.hibernate
Créez le projet Java org.dynaresume.test.jpa.hibernate.
Librairie JPA
Ajoutez les JARs :
JAR | Description |
---|---|
lib/javax/com.springsource.javax.persistence-2.0.0.jar | API JPA « OSGifiée ». |
User (sans annotations)
Créez la classe org.dynaresume.domain.User (sans annotations JPA) comme suit :
package org.dynaresume.domain; import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = 1L; private String login; private String password; public User(String login, String password) { setLogin(login); setPassword(password); } public String getLogin() { return login; } public void setLogin(String login) { this.login = login; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
REMARQUE : L’implémentation de l’interface java.io.Serializable n’est pas nécéssaire dans un contexte JPA.
JPA/Hibernate – H2
Dans cette section nous allons mettre en place JPA avec Hibernate en utilisant la base de donnée H2 :
- FindUserDynaresumeH2Hibernate récupère un User avec son ID en utilisant JPA/Hibernate dans la base de données H2.
- FindAllUserDynaresumeH2Hibernate récupère la liste des User en utilisant JPA/Hibernate dans la base de données H2.
- CreateUserDynaresumeH2Hibernate insère un User en utilisant JPA/Hibernate dans la base de données H2.
FindUserDynaresumeH2Hibernate (EntityManager#find avec H2)
Ici nous allons créer la classe FindUserDynaresumeH2Hibernate qui va afficher le User d’ID 1 de la base H2 via l’implémentation JPA Hibernate en utilisant
EntityManager#find(Class entityClass, Object primaryKey)
Créez la classe org.dynaresume.test.jpa.hibernate.h2.FindUserDynaresumeH2Hibernate comme suit :
package org.dynaresume.test.jpa.hibernate.h2; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import org.dynaresume.domain.User; public class FindUserDynaresumeH2Hibernate { public static void main(String[] args) { String persistentUnitName = "dynaresume-hibernate-h2"; EntityManagerFactory entityManagerFactory = null; EntityManager entityManager = null; try { entityManagerFactory = Persistence .createEntityManagerFactory(persistentUnitName); entityManager = entityManagerFactory.createEntityManager(); User user = entityManager.find(User.class, (long) 1); if (user == null) { System.out.println("User NOT founded."); } else { System.out.println("User founded login=" + user.getLogin()); } } finally { if (entityManager != null) { entityManager.close(); } if (entityManagerFactory != null) { entityManagerFactory.close(); } f } } }
Voici l’explication de ce code :
- Récupération du gestionnaire d’entité JPA EntityManager via sa factory EntityManager Factory chargée par la configuration JPA persistence-unit de nom dynaresume-hibernate-h2 :
String persistentUnitName = "dynaresume-hibernate-h2"; EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(persistentUnitName); EntityManager entityManager = entityManagerFactory.createEntityManager();
REMARQUE : a ce stade nous n’avons pas encore défini la configuration JPA persistence-unit.
- Récupération de l’entité persistente User d’id 1 via le gestionnaire d’entité JPA EntityManager, qui effectuera un Select SQL dans la table T_USER :
User user = entityManager.find(User.class, (long) 1);
- Fermeture du gestionnaire d’entité JPA EntityManager et de sa factory EntityManagerFactory:
entityManager.close(); entityManagerFactory.close();
Lancez la classe FindUserDynaresumeH2Hibernate et vous verrez cette erreur dans la console :
Exception in thread "main" javax.persistence.PersistenceException: No Persistence provider for EntityManager named dynaresume-hibernate-h2
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:84)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54)
at org.dynaresume.test.jpa.hibernate.h2.FindUserDynaresumeH2Hibernate.main(FindUserDynaresumeH2Hibernate.java:16)
Cette erreur est normal car nous n’avons pas encore défini de fichier persistence.xml dans le répertoire META-INF de notre projet.
persistence.xml
Ici nous allons créer le fichier persistence.xml dans le répertoire META-INF que vous devez créer dans le répertoire src (et pas à la racine du projet comme les bundles OSGi). En effet ce fichier doit se retrouver après compilation du projet dans le répertoire bin. Créez le fichier persistence.xml avec le contenu suivant :
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> <persistence-unit name="dynaresume-hibernate-h2" transaction-type="RESOURCE_LOCAL"> </persistence-unit> </persistence>
Après compilation du projet, vérifiez que ce fichier est bien contenu dans le répertoire bin du projet Eclipse org.dynaresume.test.jpa.hibernate.
Relancez et vous pourrez constater la même erreur :
Exception in thread "main" javax.persistence.PersistenceException: No Persistence provider for EntityManager named dynaresume-hibernate-h2
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:84)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54)
at org.dynaresume.test.jpa.hibernate.h2.FindUserDynaresumeH2Hibernate.main(FindUserDynaresumeH2Hibernate.java:16)
Je trouve cette erreur assez perturbante car elle ne nous permet pas de distinguer le cas où le persistence-unit est définit au cas où il n’est pas définit. Cette erreur s’explique par le fait que nous n’avons pas indiqué l’implémentation JPA que nous souhaitons utiliser.
provider
L’implémentation JPA que nous souhaitons utiliser renseigné dans l’élement XML provider. Cet élement provider doit être renseigné par une classe qui implémente l’interface JPA javax.persistence.spi.PersistenceProvider. Dans notre cas nous souhaitons utiliser l’implémentation JPA Hibernate. Pour cela ajoutez l’élement provider comme suit :
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> <persistence-unit name="dynaresume-hibernate-h2" transaction-type="RESOURCE_LOCAL"> <provider> org.hibernate.ejb.HibernatePersistence </provider> </persistence-unit> </persistence>
Relancez et vous pourrez constater la même erreur :
Exception in thread "main" javax.persistence.PersistenceException: No Persistence provider for EntityManager named dynaresume-hibernate-h2
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:84)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54)
at org.dynaresume.test.jpa.hibernate.h2.FindUserDynaresumeH2Hibernate.main(FindUserDynaresumeH2Hibernate.java:16)
Cette erreur s’explique par le fait que la classe org.hibernate.ejb.HibernatePersistence n’est pas trouvée dans le ClassLoader ce qui est normal car nous n’avons pas encore ajouté les JARs JPA/Hibernate.
Librairies Implémentation JPA
Ajoutez les librairies JPA Hibernate lib/jpa-hibernate/*.jar à votre projet.
Relancez et vous aurrez l’erreur suivante :
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/impl/StaticLoggerBinder
at org.slf4j.LoggerFactory.getSingleton(LoggerFactory.java:189)
Cette erreur s’explique par le fait que aucune implémentation de SL4J n’est trouvée. SFL4J (Simple Logging Facade for Java) est une facade de logs comme ce que propose l’API commons-logging. Cependant SFL4J est utilisé dans plus en plus de projets (Spring, Hibernate…) car il permet entre autres de régler les problèmes de ClassLoading. Je vous conseille de lire l’article Commons-logging VS SLF4J pour plus d’information à ce sujet.
Ajoutez les lib d’implémentation de SFL4J (log4j) lib/slf4j-log4j/*.jar à votre projet.
Relancez et la console affiche un warning (log4j) et une erreur :
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.annotations.Version).
log4j:WARN Please initialize the log4j system properly.
Exception in thread "main" javax.persistence.PersistenceException: [PersistenceUnit: dynaresume-hibernate-h2] Unable to build EntityManagerFactory
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:677)
at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:126)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:78)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54)
at org.dynaresume.test.jpa.hibernate.h2.FindUserDynaresumeH2Hibernate.main(FindUserDynaresumeH2Hibernate.java:16)
Caused by: org.hibernate.HibernateException: 'hibernate.dialect' must be set when no Connection avalable
at org.hibernate.dialect.resolver.DialectFactory.buildDialect(DialectFactory.java:107)
Cette erreur s’explique par le fait que le dialect Hibernate (dialect de la base de donnée à utiliser) doit être configuré. Mais d’autres propriétés doivent être aussi configurés (Driver JDBC, URL de la base….). Ces configurations s’effectuent via l’élement XML properties.
properties
Modifiez le fichier persistence.xml avec les propriétés suivantes :
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> <persistence-unit name="dynaresume-hibernate-h2" transaction-type="RESOURCE_LOCAL"> <provider> org.hibernate.ejb.HibernatePersistence </provider> <properties> <property name="hibernate.connection.driver_class" value="org.h2.Driver" /> <property name="hibernate.connection.url" value="jdbc:h2:C:/db/h2/dynaresume" /> <property name="hibernate.connection.username" value="sa" /> <property name="hibernate.connection.password" value="" /> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" /> <property name="hibernate.show_sql" value="true" /> </properties> </persistence-unit> </persistence>
Voici l’explication de ces propriétés :
Nom propriété | Valeur propriété | Description propriété |
---|---|---|
hibernate.connection.driver_class | org.h2.Driver | Configuration du driver JDBC de la base de données H2. |
hibernate.connection.url | jdbc:h2:C:/db/h2/dynaresume | Configuration de l’URL de la base de données H2 de dynaresume. |
hibernate.connection.username | sa | Configuration de l’utilisateur de la base de données H2 de dynaresume. |
hibernate.connection.password | Configuration du mot de passe de la base de données H2 de dynaresume. | |
hibernate.dialect | org.hibernate.dialect.H2Dialect | Dialect à utiliser pour générer les requêtes SQL pour la base de données H2. |
hibernate.show_sql | true | Affiche les requêtes SQL. |
Ici on peut constater que le nom des propriétés est lié à l’implémentation JPA. En JPA 2.0 les propriétés commune (driver JDBC….) sont standardisées.
Relancez et la console affiche un warning (log4j) et une erreur :
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.annotations.Version).
log4j:WARN Please initialize the log4j system properly.
Exception in thread "main" javax.persistence.PersistenceException: [PersistenceUnit: dynaresume-hibernate-h2] Unable to build EntityManagerFactory
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:677)
at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:126)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:78)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54)
at org.dynaresume.test.jpa.hibernate.h2.FindUserDynaresumeH2Hibernate.main(FindUserDynaresumeH2Hibernate.java:16)
Caused by: org.hibernate.HibernateException: JDBC Driver class not found: org.h2.Driver
at org.hibernate.connection.DriverManagerConnectionProvider.configure(DriverManagerConnectionProvider.java:89)
Cette erreur s’explique par le fait que nous n’avons pas encore mis le JAR du Driver JDBC de H2.
Ajoutez la librairie H2 lib/h2/com.springsource.org.h2-1.0.71.jar à votre projet :
JAR | Description |
---|---|
lib/h2/com.springsource.org.h2-1.0.71.jar | Base H2 « OSGifiée ». |
Relancez et la console affiche un warning (log4j) et une erreur :
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.annotations.Version).
log4j:WARN Please initialize the log4j system properly.
Exception in thread "main" java.lang.NoClassDefFoundError: javax/transaction/SystemException
at org.hibernate.ejb.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:39)
at org.hibernate.ejb.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:34)
at org.dynaresume.test.jpa.hibernate.h2.FindUserDynaresumeH2Hibernate.main(FindUserDynaresumeH2Hibernate.java:17)
Caused by: java.lang.ClassNotFoundException: javax.transaction.SystemException
Cette erreur s’explique par le fait qu’il nous manque la librairie de transaction.
Ajoutez la librairie de Transaction lib/javax/com.springsource.javax.transaction-1.1.0.jar à votre projet.
JAR | Description |
---|---|
lib/javax/com.springsource.javax.transaction-1.1.0.jar | API Transaction « OSGifiée ». |
Relancez et la console affiche un warning (log4j) et une erreur :
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.annotations.Version).
log4j:WARN Please initialize the log4j system properly.
Exception in thread "main" java.lang.IllegalArgumentException: Unknown entity: org.dynaresume.domain.User
at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:193)
at org.dynaresume.test.jpa.hibernate.h2.FindUserDynaresumeH2Hibernate.main(FindUserDynaresumeH2Hibernate.java:19)
Caused by: org.hibernate.MappingException: Unknown entity: org.dynaresume.domain.User
Cette erreur s’explique par le fait que la classe User est inconnu de JPA.
class
La configuration JPA doit lister toutes les classes persistantes via l’élement XML class :
<class>org.dynaresume.domain.User</class>
Modifiez le fichier persistent.xml en ajoutant la classe User en tant que classe persistante :
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> <persistence-unit name="dynaresume-hibernate-h2" transaction-type="RESOURCE_LOCAL"> <provider> org.hibernate.ejb.HibernatePersistence </provider> <class>org.dynaresume.domain.User</class> <properties> <property name="hibernate.connection.driver_class" value="org.h2.Driver" /> <property name="hibernate.connection.url" value="jdbc:h2:C:/db/h2/dynaresume" /> <property name="hibernate.connection.username" value="sa" /> <property name="hibernate.connection.password" value="" /> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" /> <property name="hibernate.show_sql" value="true" /> </properties> </persistence-unit> </persistence>
Relancez et la même erreur s’affiche ce qui est normal car nous n’avons pas encore défini le mapping.
User – Mapping
Modifiez la classe User pour définir le mapping entre cette classe et la table T_USER comme suit :
package org.dynaresume.domain; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "T_USER") public class User { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "USR_ID_N") private long id; @Column(name = "USR_LOGIN_C") private String login; @Column(name = "USR_PASSWORD_C") private String password; public User() { } public User(String login, String password) { setLogin(login); setPassword(password); } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getLogin() { return login; } public void setLogin(String login) { this.login = login; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
Le champs id de la classe User est mappé comme ceci :
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "USR_ID_N") private long id;
Voici les explications de ce mapping :
- @Column(name = « USR_ID_N ») indique que le champs id de la classe User est mappé avec la colonne USR_ID_N de la table T_User.
- @Id indique que ce champs définit un identifiant (clé primaire).
- @GeneratedValue(strategy = GenerationType.IDENTITY) indique que ce champs est mis à jour automatiquement lors d’une insertion en utilisant la stratégie IDENTITY. Cette stratégie permet d’utiliser le type IDENTITY qui est supporté à la fois par H2 et Derby. Ici nous avons la chance que ces 2 bases supportent le même type. Dans le cas ou on souhaiterait utiliser Oracle (support des sequences) et H2 (support Identity) avec JPA, on ne peut pas mettre de stratégie en commun (excepté celle de TABLE). Je parle un petit peu de cette problématique dans la section @GeneratedValue.
Relancez et vous devez voir dans la console la requête SQL de Select et le User d’ID 1 de la table T_USER de la base H2 :
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.annotations.Version).
log4j:WARN Please initialize the log4j system properly.
Hibernate: select user0_.USR_ID_N as USR1_0_0_, user0_.USR_LOGIN_C as USR2_0_0_, user0_.USR_PASSWORD_C as USR3_0_0_ from T_USER user0_ where user0_.USR_ID_N=?
User founded login=angelo (H2)
log4j
Pour éviter le warn log4j, il suffit d’ajouter le fichier log4J.properties dans le répertoire src :
log4j.rootLogger=info, con log4j.appender.con=org.apache.log4j.ConsoleAppender log4j.appender.con.layout=org.apache.log4j.PatternLayout log4j.appender.con.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
Relancez et vous verrez dans la console des traces Hibernate :
0 [main] INFO org.hibernate.cfg.annotations.Version - Hibernate Annotations 3.4.0.GA
15 [main] INFO org.hibernate.cfg.Environment - Hibernate 3.3.2.GA
...
390 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - using driver: org.h2.Driver at URL: jdbc:h2:C:/db/h2/dynaresume
390 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - connection properties: {user=sa, password=****, autocommit=true, release_mode=auto}
656 [main] INFO org.hibernate.cfg.SettingsFactory - RDBMS: H2, version: 1.0.71 (2008-04-25)
656 [main] INFO org.hibernate.cfg.SettingsFactory - JDBC driver: H2 JDBC Driver, version: 1.0.71 (2008-04-25)
672 [main] INFO org.hibernate.dialect.Dialect - Using dialect: org.hibernate.dialect.H2Dialect
...
734 [main] INFO org.hibernate.impl.SessionFactoryImpl - building session factory
890 [main] INFO org.hibernate.impl.SessionFactoryObjectFactory - Not binding factory to JNDI, no JNDI name configured
Hibernate: select user0_.USR_ID_N as USR1_0_0_, user0_.USR_LOGIN_C as USR2_0_0_, user0_.USR_PASSWORD_C as USR3_0_0_ from T_USER user0_ where user0_.USR_ID_N=?
User founded login=angelo (H2)
984 [main] INFO org.hibernate.impl.SessionFactoryImpl - closing
984 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - cleaning up connection pool: jdbc:h2:C:/db/h2/dynaresume
FindAllUserDynaresumeH2Hibernate (EntityManager#createQuery avec H2)
Ici nous allons créer la classe FindAllUserDynaresumeH2Hibernate qui va afficher la liste des Users de la base H2 via l’implémentation JPA Hibernate en utilisant :
EntityManager#createQuery(String qlString)
Créez la classe org.dynaresume.test.jpa.hibernate.h2.FindAllUserDynaresumeH2Hibernate comme suit :
package org.dynaresume.test.jpa.hibernate.h2; import java.util.Collection; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.Query; import org.dynaresume.domain.User; public class FindAllUserDynaresumeH2Hibernate { public static void main(String[] args) { String persistentUnitName = "dynaresume-hibernate-h2"; EntityManagerFactory entityManagerFactory = null; EntityManager entityManager = null; try { entityManagerFactory = Persistence .createEntityManagerFactory(persistentUnitName); entityManager = entityManagerFactory.createEntityManager(); Query query = entityManager.createQuery("select u from " + User.class.getSimpleName() + " u"); Collection<User> users = query.getResultList(); for (User user : users) { System.out.println("User [id=" + user.getId() + " login=" + user.getLogin() + ", password=" + user.getPassword() + "]"); } } finally { if (entityManager != null) { entityManager.close(); } if (entityManagerFactory != null) { entityManagerFactory.close(); } } } }
Ici nous avons utilisé la query JPA :
Query query = entityManager.createQuery("select u from " + User.class.getSimpleName() + " u");
Nous aurrions pu écrire aussi
Query query = entityManager.createQuery("from " + User.class.getSimpleName());
mais cette syntaxe ne fonctionne pas avec Eclipselink.
Lancez FindAllUserDynaresumeH2Hibernate et la console affiche la liste des User de la table T_USER de la base H2 :
...
Hibernate: select user0_.USR_ID_N as USR1_0_, user0_.USR_LOGIN_C as USR2_0_, user0_.USR_PASSWORD_C as USR3_0_ from T_USER user0_
User [id=1 login=angelo (H2), password=]
User [id=2 login=djo (H2), password=]
User [id=3 login=keulkeul (H2), password=]
User [id=4 login=pascal (H2), password=]
...
CreateUserDynaresumeH2Hibernate (EntityManager#persist avec H2)
Ici nous allons créer la classe CreateUserDynaresumeH2Hibernate qui va insérer un User dans la table T_USER de la base H2 via l’implémentation JPA Hibernate en utilisant :
EntityManager#persist(Object entity)
Créez la classe org.dynaresume.test.jpa.hibernate.h2.CreateUserDynaresumeH2Hibernate comme suit :
package org.dynaresume.test.jpa.hibernate.h2; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import org.dynaresume.domain.User; public class CreateUserDynaresumeH2Hibernate { public static void main(String[] args) { String persistentUnitName = "dynaresume-hibernate-h2"; EntityManagerFactory entityManagerFactory = null; EntityManager entityManager = null; try { entityManagerFactory = Persistence .createEntityManagerFactory(persistentUnitName); entityManager = entityManagerFactory.createEntityManager(); User user = new User("jpa-user (Hibernate-H2)", ""); entityManager.persist(user); } finally { if (entityManager != null) { entityManager.close(); } if (entityManagerFactory != null) { entityManagerFactory.close(); } } } }
Lancez la classe CreateUserDynaresumeH2Hibernate, mais ceci n’insère pas le User dans la base.
Ajoutez l’appel de la méthode EntityManager#flush() après l’appel de EntityManager#persist(Object o) pour forcer l’exécution de la requête SQL :
User user = new User("jpa-user (Hibernate-H2)", ""); entityManager.persist(user); entityManager.flush();
Relancez CreateUserDynaresumeH2Hibernate et la console affiche l’erreur suivante :
Exception in thread "main" javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:301)
at org.dynaresume.test.jpa.hibernate.h2.CreateUserDynaresumeH2Hibernate.main(CreateUserDynaresumeH2Hibernate.java:26)
Cette erreur s’explique par le fait que aucune transaction n’a été ouverte. Pour effectuer cela nous devons récupérer uen transaction javax.persistence.EntityTransaction et l’ourvir puis la commiter après l’insertion (EntityManager#persist(Object entity)) du User. Modifiez la classe CreateUserDynaresumeH2Hibernate comme suit :
package org.dynaresume.test.jpa.hibernate; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import org.dynaresume.domain.User; public class CreateUserDynaresumeH2Hibernate { public static void main(String[] args) { String persistentUnitName = "dynaresume-hibernate-h2"; EntityManagerFactory entityManagerFactory = null; EntityManager entityManager = null; EntityTransaction transaction = null; try { entityManagerFactory = Persistence .createEntityManagerFactory(persistentUnitName); entityManager = entityManagerFactory.createEntityManager(); transaction = entityManager.getTransaction(); transaction.begin(); User user = new User("jpa-user (Hibernate-H2)", ""); entityManager.persist(user); transaction.commit(); } catch(Exception e) { if (transaction != null) { transaction.rollback(); } }finally { if (entityManager != null) { entityManager.close(); } if (entityManagerFactory != null) { entityManagerFactory.close(); } } } }
Vous pouvez remarquer que l’appel du flush a été omis car l’appel de EntityTransaction#commit() force l’éxécution des requêtes SQL.
Lancez CreateUserDynaresumeH2Hibernate et la console affiche :
Hibernate: insert into T_USER (USR_ID_N, USR_LOGIN_C, USR_PASSWORD_C) values (null, ?, ?)
Ce log montre que la requête SQL d’Insert dans la table T_USER a été effectué. La valeur null passé à la colonne USR_ID_N peut être déroutante, mais la valeur de cette colonne sera mis a jour en incrémentatnt le dernier ID de la table T_User (type IDENTITY). Relancez FindAllUserDynaresumeH2Hibernate et la console affiche la liste des User de la table T_USER de la base H2 avec l’utilisateur jpa-user (Hibernate-H2) :
...
Hibernate: select user0_.USR_ID_N as USR1_0_, user0_.USR_LOGIN_C as USR2_0_, user0_.USR_PASSWORD_C as USR3_0_ from T_USER user0_
User [id=1 login=angelo (H2), password=]
User [id=2 login=djo (H2), password=]
User [id=3 login=keulkeul (H2), password=]
User [id=4 login=pascal (H2), password=]
User [id=5 login=jpa-user (Hibernate-H2), password=]
...
JPA/Hibernate – Derby
Dans cette section nous allons mettre en place JPA avec Hibernate en utilisant la base de donnée Derby :
- FindUserDynaresumeDerbyHibernate récupère un User avec son ID en utilisant JPA/Hibernate dans la base de données Derby.
- FindAllUserDynaresumeDerbyHibernate récupère la liste des User en utilisant JPA/Hibernate dans la base de données Derby.
- CreateUserDynaresumeDerbyHibernate insère un User en utilisant JPA/Hibernate dans la base de données Derby.
Initialisation JPA/Hibernate – Derby
JAR
Ajoutez les libraires de derby lib/derby/*.jar à votre projet.
JAR | Description |
---|---|
lib/derby/com.springsource.org.apache.derby-10.5.1000001.764942.jar | Base Derby « OSGifiée ». |
lib/derby/com.springsource.org.apache.derby.client-10.5.1000001.764942.jar | Client de la Base Derby « OSGifiée ». |
persistence.xml
Ajoutez dans le fichier persistent.xml, la déclaration persistence-unit de derby comme suit :
<persistence-unit name="dynaresume-hibernate-derby" transaction-type="RESOURCE_LOCAL"> <provider> org.hibernate.ejb.HibernatePersistence </provider> <class>org.dynaresume.domain.User</class> <properties> <property name="hibernate.connection.driver_class" value="org.apache.derby.jdbc.EmbeddedDriver" /> <property name="hibernate.connection.url" value="jdbc:derby:C:/db/derby/dynaresume" /> <property name="hibernate.connection.username" value="" /> <property name="hibernate.connection.password" value="" /> <property name="hibernate.dialect" value="org.hibernate.dialect.DerbyDialect" /> <property name="hibernate.show_sql" value="true" /> </properties> </persistence-unit>
Voici l’explication de ces propriétés :
Nom propriété | Valeur propriété | Description propriété |
---|---|---|
hibernate.connection.driver_class | org.apache.derby.jdbc.EmbeddedDriver | Configuration du driver JDBC de la base de données Derby en mode enmbarqué. |
hibernate.connection.url | jdbc:derby:C:/db/derby/dynaresume | Configuration de l’URL de la base de données Derby de dynaresume. |
hibernate.connection.username | Configuration de l’utilisateur de la base de données Derby de dynaresume. | |
hibernate.connection.password | Configuration du mot de passe de la base de données Derby de dynaresume. | |
hibernate.dialect | org.hibernate.dialect.DerbyDialect | Dialect à utiliser pour générer les requêtes SQL pour la base de données Derby. |
hibernate.show_sql | true | Affiche les requêtes SQL. |
FindUserDynaresumeDerbyHibernate (EntityManager#find avec Derby)
Ici nous allons créer la classe FindUserDynaresumeDerbyHibernate qui va afficher le User d’ID 1 de la base Derby via l’implémentation JPA Hibernate.
Créez la classe org.dynaresume.test.jpa.hibernate.h2.FindUserDynaresumeDerbyHibernate comme suit :
package org.dynaresume.test.jpa.hibernate.h2; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import org.dynaresume.domain.User; public class FindUserDynaresumeDerbyHibernate { public static void main(String[] args) { String persistentUnitName = "dynaresume-hibernate-derby"; EntityManagerFactory entityManagerFactory = null; EntityManager entityManager = null; try { entityManagerFactory = Persistence .createEntityManagerFactory(persistentUnitName); entityManager = entityManagerFactory.createEntityManager(); User user = entityManager.find(User.class, (long) 1); if (user == null) { System.out.println("User NOT founded."); } else { System.out.println("User founded login=" + user.getLogin()); } } finally { if (entityManager != null) { entityManager.close(); } if (entityManagerFactory != null) { entityManagerFactory.close(); } } } }
Lancez FindUserDynaresumeDerbyHibernate et la console affiche le login du User d’ID 1 d’ID 1 de la table T_USER de la base Derby :
Hibernate: select user0_.USR_ID_N as USR1_0_0_, user0_.USR_LOGIN_C as USR2_0_0_, user0_.USR_PASSWORD_C as USR3_0_0_ from T_USER user0_ where user0_.USR_ID_N=?
User founded login=angelo (Derby)
FindAllUserDynaresumeDerbyHibernate (EntityManager#createQuery avec Derby)
Créez la classe org.dynaresume.test.jpa.hibernate.FindAllUserDynaresumeDerbyHibernate comme suit :
package org.dynaresume.test.jpa.hibernate.derby; import java.util.Collection; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.Query; import org.dynaresume.domain.User; public class FindAllUserDynaresumeDerbyHibernate { public static void main(String[] args) { String persistentUnitName = "dynaresume-hibernate-derby"; EntityManagerFactory entityManagerFactory = null; EntityManager entityManager = null; try { entityManagerFactory = Persistence .createEntityManagerFactory(persistentUnitName); entityManager = entityManagerFactory.createEntityManager(); Query query = entityManager.createQuery("select u from " + User.class.getSimpleName() + " u"); Collection<User> users = query.getResultList(); for (User user : users) { System.out.println("User [id=" + user.getId() + " login=" + user.getLogin() + ", password=" + user.getPassword() + "]"); } } finally { if (entityManager != null) { entityManager.close(); } if (entityManagerFactory != null) { entityManagerFactory.close(); } } } }
Lancez FindAllUserDynaresumeDerbyHibernate et la console affiche la liste des User de la table T_USER de la base Derby :
...
Hibernate: select user0_.USR_ID_N as USR1_0_, user0_.USR_LOGIN_C as USR2_0_, user0_.USR_PASSWORD_C as USR3_0_ from T_USER user0_
User [id=1 login=angelo (Derby), password=]
User [id=2 login=djo (Derby), password=]
User [id=3 login=keulkeul (Derby), password=]
User [id=4 login=pascal (Derby), password=]
...
CreateUserDynaresumeDerbyHibernate (EntityManager#persist avec Derby)
Ici nous allons créer la classe CreateUserDynaresumeDerbyHibernate qui va insérer un User dans la table T_USER de la base Derby via l’implémentation JPA Hibernate.
Créez la classe org.dynaresume.test.jpa.hibernate.derby.CreateUserDynaresumeDerbyHibernate comme suit :
package org.dynaresume.test.jpa.hibernate.derby; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import org.dynaresume.domain.User; public class CreateUserDynaresumeDerbyHibernate { public static void main(String[] args) { String persistentUnitName = "dynaresume-hibernate-derby"; EntityManagerFactory entityManagerFactory = null; EntityManager entityManager = null; EntityTransaction transaction = null; try { entityManagerFactory = Persistence .createEntityManagerFactory(persistentUnitName); entityManager = entityManagerFactory.createEntityManager(); transaction = entityManager.getTransaction(); transaction.begin(); User user = new User("jpa-user (Hibernate-Derby)", ""); entityManager.persist(user); transaction.commit(); } catch(Exception e) { if (transaction != null) { transaction.rollback(); } }finally { if (entityManager != null) { entityManager.close(); } if (entityManagerFactory != null) { entityManagerFactory.close(); } } } }
Lancez CreateUserDynaresumeDerbyHibernate et la console affiche :
Hibernate: insert into T_USER (USR_ID_N, USR_LOGIN_C, USR_PASSWORD_C) values (default, ?, ?)
Hibernate: values identity_val_local()
Relancez FindAllUserDynaresumeDerbyHibernate et la console affiche la liste des User de la table T_USER de la base Derby avec l’utilisateur jpa-user (Hibernate-Derby) :
User [id=1 login=angelo (Derby), password=]
User [id=2 login=djo (Derby), password=]
User [id=3 login=keulkeul (Derby), password=]
User [id=4 login=pascal (Derby), password=]
User [id=5 login=jpa-user (Hibernate-Derby), password=]
JPA/Eclipse Link
EclipseLink est une implémentation JPA avec EclipseLink. Cette implémentation qui est basé sur les sources de TopLink est une version adaptée au contexte OSGi, ce qui le rend facilement utilisable dans un contexte Eclipse RCP. L aversion EclipseLink 2.0.0 que nous utilisons a pour objectif d’implémenter JPA 2.0 (JSR 317).
Initialisation projet org.dynaresume.test.jpa.eclipselink
Créez le projet Java org.dynaresume.test.jpa.eclipselink.
JAR
Ajoutez les JAR API JPA suivants :
JAR | Description |
---|---|
lib/javax/com.springsource.javax.persistence-2.0.0.jar | JPA API |
lib/javax/com.springsource.javax.transaction-1.1.0.jar | Transaction API |
Ajoutez les JAR Implémentation Eclipselink suivants :
JAR | Description |
---|---|
lib/jpa-eclipselink/org.eclipse.persistence.antlr-2.0.0.jar | |
lib/jpa-eclipselink/org.eclipse.persistence.asm-2.0.0.jar | |
lib/jpa-eclipselink/org.eclipse.persistence.core-2.0.0.jar | |
lib/jpa-eclipselink/org.eclipse.persistence.jpa-2.0.0.jar |
Ajoutez les JAR JDBC H2
JAR | Description |
---|---|
lib/h2/com.springsource.org.h2-1.0.71.jar | Base H2 « OSGifiée ». |
et JDBC Derby :
JAR | Description |
---|---|
lib/derby/com.springsource.org.apache.derby-10.5.1000001.764942.jar | Base Derby « OSGifiée ». |
lib/derby/com.springsource.org.apache.derby.client-10.5.1000001.764942.jar | Client de la Base Derby « OSGifiée ». |
persistence.xml
Créer le fichier persistence.xml dans le répertoire src/META-INF comme suit :
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> <persistence-unit name="dynaresume-eclipselink-h2" transaction-type="RESOURCE_LOCAL"> <provider> org.eclipse.persistence.jpa.PersistenceProvider </provider> <class>org.dynaresume.domain.User</class> <properties> <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" /> <property name="javax.persistence.jdbc.url" value="jdbc:h2:C:/db/h2/dynaresume" /> <property name="javax.persistence.jdbc.user" value="sa" /> <property name="javax.persistence.jdbc.password" value="" /> <property name="eclipselink.target-database" value="org.eclipse.persistence.platform.database.H2Platform" /> </properties> </persistence-unit> <persistence-unit name="dynaresume-eclipselink-derby" transaction-type="RESOURCE_LOCAL"> <provider> org.eclipse.persistence.jpa.PersistenceProvider </provider> <class>org.dynaresume.domain.User</class> <properties> <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" /> <property name="javax.persistence.jdbc.url" value="jdbc:derby:C:/db/derby/dynaresume" /> <property name="javax.persistence.jdbc.user" value="" /> <property name="javax.persistence.jdbc.password" value="" /> <property name="eclipselink.target-database" value="org.eclipse.persistence.platform.database.DerbyPlatform" /> <property name="show-sql" value="true" /> </properties> </persistence-unit> </persistence>
Les propriétés utilisés pour le driver JDBC, l’URL… nommées javax.persistence.* sont des noms standards utilisés dans la spécification JPA 2.0 qui est implémenté par EclipseLink 2.0.0. Les anciennes versions de EclipseLink utilisaient le nommage eclipselink.*. Voici comment déclarer les propriétés avec une ancienne version de EclipseLink :
<properties> <property name="eclipselink.jdbc.driver" value="org.h2.Driver" /> <property name="eclipselink.jdbc.url" value="jdbc:h2:C:/db/h2/dynaresume" /> <property name="eclipselink.jdbc.user" value="sa" /> <property name="eclipselink.jdbc.password" value="" /> </properties>
REMARQUE :
- le dialect de la base H2 org.eclipse.persistence.platform.database.H2Platform (renseigné avec la propriété eclipselink.target-database) est intégré dans la version EclipseLink 2.0.0 mais pas dans la version 1.0.1 de celle de Spring repository.
- Je n’ai pas réussi à trouver la propriété qui permet de montrer les traces SQL (théoriquement ca devrait être show-sql, mais ca ne marche pas?).
JPA/EclipseLink – H2
Dans cette section nous allons mettre en place JPA avec Eclipselink en utilisant la base de donnée H2 :
- FindUserDynaresumeH2EclipseLink récupère un User avec son ID en utilisant JPA/Eclipselink dans la base de données H2.
- FindAllUserDynaresumeH2EclipseLink récupère la liste des User en utilisant JPA/Eclipselink dans la base de données H2.
- CreateUserDynaresumeH2EclipseLink insère un User en utilisant JPA/Eclipselink dans la base de données H2.
FindUserDynaresumeH2EclipseLink (EntityManager#find avec H2)
Ici nous allons créer la classe FindUserDynaresumeH2EclipseLink qui va afficher le User d’ID 1 de la base H2 via l’implémentation JPA Eclipselink.
Créez la classe org.dynaresume.test.jpa.eclipselink.h2.FindUserDynaresumeH2EclipseLink comme suit :
package org.dynaresume.test.jpa.eclipselink.h2; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import org.dynaresume.domain.User; public class FindUserDynaresumeH2Eclipselink { public static void main(String[] args) { String persistentUnitName = "dynaresume-eclipselink-h2"; EntityManagerFactory entityManagerFactory = null; EntityManager entityManager = null; try { entityManagerFactory = Persistence .createEntityManagerFactory(persistentUnitName); entityManager = entityManagerFactory.createEntityManager(); User user = entityManager.find(User.class, (long) 1); if (user == null) { System.out.println("User NOT founded."); } else { System.out.println("User founded login=" + user.getLogin()); } } finally { if (entityManager != null) { entityManager.close(); } if (entityManagerFactory != null) { entityManagerFactory.close(); } } } }
Lancez FindUserDynaresumeH2EclipseLink et la console affiche le login du User d’ID 1 d’ID 1 de la table T_USER de la base H2 :
[EL Info]: 2010-02-18 17:33:18.875--ServerSession(11587215)--EclipseLink, version: Eclipse Persistence Services - 1.1.0.r3634
[EL Info]: 2010-02-18 17:33:19.109--Not able to detect platform for vendor name [H2]. Defaulting to [org.eclipse.persistence.platform.database.DatabasePlatform]. The database dialect used may not match with the database you are using. Please explicitly provide a platform using property eclipselink.platform.class.name.
[EL Info]: 2010-02-18 17:33:19.234--ServerSession(11587215)--file:/D:/_Projets/Personal/workspace-gestcv-osgi-wordpress/step16/org.dynaresume.test.jpa.eclipselink/bin/-dynaresume-eclipselink-h2 login successful
User founded login=angelo (H2)
[EL Info]: 2010-02-18 17:33:19.328--ServerSession(11587215)--file:/D:/_Projets/Personal/workspace-gestcv-osgi-wordpress/step16/org.dynaresume.test.jpa.eclipselink/bin/-dynaresume-eclipselink-h2 logout successful
FindAllUserDynaresumeH2EclipseLink (EntityManager#createQuery avec H2)
Ici nous allons créer la classe FindAllUserDynaresumeH2EclipseLink qui va afficher la liste des Users de la base H2 via l’implémentation JPA Eclipselink.
Créez la classe org.dynaresume.test.jpa.eclipselink.h2.FindAllUserDynaresumeH2EclipseLink comme suit :
package org.dynaresume.test.jpa.eclipselink.h2; import java.util.Collection; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.Query; import org.dynaresume.domain.User; public class FindAllUserDynaresumeH2EclipseLink { public static void main(String[] args) { String persistentUnitName = "dynaresume-eclipselink-h2"; EntityManagerFactory entityManagerFactory = null; EntityManager entityManager = null; try { entityManagerFactory = Persistence .createEntityManagerFactory(persistentUnitName); entityManager = entityManagerFactory.createEntityManager(); Query query = entityManager.createQuery("select u from " + User.class.getSimpleName() + " u"); Collection<User> users = query.getResultList(); for (User user : users) { System.out.println("User [id=" + user.getId() + " login=" + user.getLogin() + ", password=" + user.getPassword() + "]"); } } finally { if (entityManager != null) { entityManager.close(); } if (entityManagerFactory != null) { entityManagerFactory.close(); } } } }
Lancez FindAllUserDynaresumeH2EclipseLink et la console affiche la liste des User de la table T_USER de la base H2 :
[EL Info]: 2010-02-22 17:40:15.453--ServerSession(23414511)--EclipseLink, version: Eclipse Persistence Services - 2.0.0.v20091127-r5931
[EL Info]: 2010-02-22 17:40:15.765--ServerSession(23414511)--file:/D:/_Projets/Personal/workspace-gestcv-osgi-wordpress/step16/org.dynaresume.test.jpa/bin/_dynaresume-eclipselink-h2 login successful
User [id=1 login=angelo (H2), password=]
User [id=2 login=djo (H2), password=]
User [id=3 login=keulkeul (H2), password=]
User [id=4 login=pascal (H2), password=]
User [id=5 login=jpa-user (Hibernate-H2), password=]
[EL Info]: 2010-02-22 17:40:16.14--ServerSession(23414511)--file:/D:/_Projets/Personal/workspace-gestcv-osgi-wordpress/step16/org.dynaresume.test.jpa/bin/_dynaresume-eclipselink-h2 logout successful
CreateUserDynaresumeH2EclipseLink (EntityManager#persist avec H2)
Ici nous allons créer la classe CreateUserDynaresumeH2EclipseLink qui va insérer un User dans la table T_USER de la base H2 via l’implémentation JPA Eclipselink.
Créez la classe org.dynaresume.test.jpa.eclipselink.h2.CreateUserDynaresumeH2EclipseLink comme suit :
package org.dynaresume.test.jpa.eclipselink.h2; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import org.dynaresume.domain.User; public class CreateUserDynaresumeH2EclipseLink { public static void main(String[] args) { String persistentUnitName = "dynaresume-eclipselink-h2"; EntityManagerFactory entityManagerFactory = null; EntityManager entityManager = null; EntityTransaction transaction = null; try { entityManagerFactory = Persistence .createEntityManagerFactory(persistentUnitName); entityManager = entityManagerFactory.createEntityManager(); transaction = entityManager.getTransaction(); transaction.begin(); User user = new User("jpa-user (EclipseLink-H2)", ""); entityManager.persist(user); transaction.commit(); } catch(Exception e) { if (transaction != null) { transaction.rollback(); } }finally { if (entityManager != null) { entityManager.close(); } if (entityManagerFactory != null) { entityManagerFactory.close(); } } } }
Lancez CreateUserDynaresumeH2EclipseLink et la console affiche :
[EL Info]: 2010-02-22 17:43:17.125--ServerSession(1321194)--EclipseLink, version: Eclipse Persistence Services - 2.0.0.v20091127-r5931
[EL Info]: 2010-02-22 17:43:17.453--ServerSession(1321194)--file:/D:/_Projets/Personal/workspace-gestcv-osgi-wordpress/step16/org.dynaresume.test.jpa/bin/_dynaresume-eclipselink-h2 login successful
[EL Info]: 2010-02-22 17:43:17.546--ServerSession(1321194)--file:/D:/_Projets/Personal/workspace-gestcv-osgi-wordpress/step16/org.dynaresume.test.jpa/bin/_dynaresume-eclipselink-h2 logout successful
Relancez FindAllUserDynaresumeH2EclipseLink et la console affiche la liste des User de la table T_USER de la base H2 avec l’utilisateur jpa-user (EclipseLink-H2) :
User [id=1 login=angelo (H2), password=]
User [id=2 login=djo (H2), password=]
User [id=3 login=keulkeul (H2), password=]
User [id=4 login=pascal (H2), password=]
User [id=5 login=jpa-user (Hibernate-H2), password=]
User [id=6 login=jpa-user (EclipseLink-H2), password=]
JPA/EclipseLink – Derby
Dans cette section nous allons mettre en place JPA avec EclipseLink en utilisant la base de donnée Derby :
- FindUserDynaresumeDerbyEclipseLink récupère un User avec son ID en utilisant JPA/EclipseLink dans la base de données Derby.
- FindAllUserDynaresumeDerbyEclipseLink récupère la liste des User en utilisant JPA/EclipseLink dans la base de données Derby.
- CreateUserDynaresumeDerbyEclipseLink insère un User en utilisant JPA/EclipseLink dans la base de données Derby.
FindUserDynaresumeDerbyEclipseLink (EntityManager#find avec Derby)
Ici nous allons créer la classe FindUserDynaresumeDerbyEclipseLink qui va afficher le User d’ID 1 de la base H2 via l’implémentation JPA Eclipselink.
Créez la classe org.dynaresume.test.jpa.eclipselink.derby.FindUserDynaresumeDerbyEclipseLink comme suit :
package org.dynaresume.test.jpa.eclipselink.derby; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import org.dynaresume.domain.User; public class FindUserDynaresumeDerbyEclipseLink { public static void main(String[] args) { String persistentUnitName = "dynaresume-eclipselink-derby"; EntityManagerFactory entityManagerFactory = null; EntityManager entityManager = null; try { entityManagerFactory = Persistence .createEntityManagerFactory(persistentUnitName); entityManager = entityManagerFactory.createEntityManager(); User user = entityManager.find(User.class, (long) 1); if (user == null) { System.out.println("User NOT founded."); } else { System.out.println("User founded login=" + user.getLogin()); } } finally { if (entityManager != null) { entityManager.close(); } if (entityManagerFactory != null) { entityManagerFactory.close(); } } } }
Lancez FindUserDynaresumeDerbyEclipseLink et la console affiche le login du User d’ID 1 d’ID 1 de la table T_USER de la base Derby :
[EL Info]: 2010-02-18 17:42:48.5--ServerSession(19731881)--EclipseLink, version: Eclipse Persistence Services - 1.1.0.r3634
[EL Info]: 2010-02-18 17:42:49.062--ServerSession(19731881)--file:/D:/_Projets/Personal/workspace-gestcv-osgi-wordpress/step16/org.dynaresume.test.jpa.eclipselink/bin/-dynaresume-eclipselink-derby login successful
User founded login=angelo (Derby)
[EL Info]: 2010-02-18 17:42:49.39--ServerSession(19731881)--file:/D:/_Projets/Personal/workspace-gestcv-osgi-wordpress/step16/org.dynaresume.test.jpa.eclipselink/bin/-dynaresume-eclipselink-derby logout successful
FindAllUserDynaresumeDerbyEclipseLink (EntityManager#createQuery avec Derby)
Ici nous allons créer la classe FindAllUserDynaresumeDerbyEclipseLink qui va afficher la liste des Users de la base Derby via l’implémentation JPA Eclipselink.
Créez la classe org.dynaresume.test.jpa.eclipselink.derby.FindAllUserDynaresumeDerbyEclipseLink comme suit :
package org.dynaresume.test.jpa.eclipselink.derby; import java.util.Collection; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.Query; import org.dynaresume.domain.User; public class FindAllUserDynaresumeDerbyEclipseLink { public static void main(String[] args) { String persistentUnitName = "dynaresume-eclipselink-derby"; EntityManagerFactory entityManagerFactory = null; EntityManager entityManager = null; try { entityManagerFactory = Persistence .createEntityManagerFactory(persistentUnitName); entityManager = entityManagerFactory.createEntityManager(); Query query = entityManager.createQuery("select u from " + User.class.getSimpleName() + " u"); Collection<User> users = query.getResultList(); for (User user : users) { System.out.println("User [id=" + user.getId() + " login=" + user.getLogin() + ", password=" + user.getPassword() + "]"); } } finally { if (entityManager != null) { entityManager.close(); } if (entityManagerFactory != null) { entityManagerFactory.close(); } } } }
Lancez FindAllUserDynaresumeDerbyEclipseLink et la console affiche la liste des User de la table T_USER de la base Derby :
[EL Info]: 2010-02-22 17:53:28.281--ServerSession(22543186)--EclipseLink, version: Eclipse Persistence Services - 2.0.0.v20091127-r5931
[EL Info]: 2010-02-22 17:53:28.906--ServerSession(22543186)--file:/D:/_Projets/Personal/workspace-gestcv-osgi-wordpress/step16/org.dynaresume.test.jpa.eclipselink/bin/_dynaresume-eclipselink-derby login successful
User [id=1 login=angelo (Derby), password=]
User [id=2 login=djo (Derby), password=]
User [id=3 login=keulkeul (Derby), password=]
User [id=4 login=pascal (Derby), password=]
User [id=5 login=jpa-user (Hibernate-Derby), password=]
[EL Info]: 2010-02-22 17:53:29.421--ServerSession(22543186)--file:/D:/_Projets/Personal/workspace-gestcv-osgi-wordpress/step16/org.dynaresume.test.jpa.eclipselink/bin/_dynaresume-eclipselink-derby logout successful
CreateUserDynaresumeDerbyEclipseLink (EntityManager#persist avec Derby)
Ici nous allons créer la classe CreateUserDynaresumeDerbyEclipseLink qui va insérer un User dans la table T_USER de la base Derby via l’implémentation JPA Eclipselink.
Créez la classe org.dynaresume.test.jpa.eclipselink.derby.CreateUserDynaresumeDerbyEclipseLink comme suit :
package org.dynaresume.test.jpa.eclipselink.derby; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import org.dynaresume.domain.User; public class CreateUserDynaresumeDerbyEclipseLink { public static void main(String[] args) { String persistentUnitName = "dynaresume-eclipselink-derby"; EntityManagerFactory entityManagerFactory = null; EntityManager entityManager = null; EntityTransaction transaction = null; try { entityManagerFactory = Persistence .createEntityManagerFactory(persistentUnitName); entityManager = entityManagerFactory.createEntityManager(); transaction = entityManager.getTransaction(); transaction.begin(); User user = new User("jpa-user (EclipseLink-Derby)", ""); entityManager.persist(user); transaction.commit(); } catch(Exception e) { if (transaction != null) { transaction.rollback(); } }finally { if (entityManager != null) { entityManager.close(); } if (entityManagerFactory != null) { entityManagerFactory.close(); } } } }
Lancez CreateUserDynaresumeDerbyEclipseLink et la console affiche :
[EL Info]: 2010-02-18 17:46:58.578--ServerSession(17447716)--EclipseLink, version: Eclipse Persistence Services - 1.1.0.r3634
[EL Info]: 2010-02-18 17:46:59.296--ServerSession(17447716)--file:/D:/_Projets/Personal/workspace-gestcv-osgi-wordpress/step16/org.dynaresume.test.jpa.eclipselink/bin/-dynaresume-eclipselink-derby login successful
[EL Info]: 2010-02-18 17:46:59.625--ServerSession(17447716)--file:/D:/_Projets/Personal/workspace-gestcv-osgi-wordpress/step16/org.dynaresume.test.jpa.eclipselink/bin/-dynaresume-eclipselink-derby logout successful
Relancez FindAllUserDynaresumeDerbyEclipseLink et la console affiche la liste des User de la table T_USER de la base Derby avec l’utilisateur jpa-user (EclipseLink-Derby) :
User [id=1 login=angelo (Derby), password=]
User [id=2 login=djo (Derby), password=]
User [id=3 login=keulkeul (Derby), password=]
User [id=4 login=pascal (Derby), password=]
User [id=5 login=jpa-user (Hibernate-Derby), password=]
User [id=6 login=jpa-user (EclipseLink-Derby), password=]
@GeneratedValue
L’annotation JPA @GeneratedValue permet de définir la strategie d’alimentation d’un champs ID d’une table à l’aide de l’enum javax.persistence.GenerationType. Je vous conseille de lire Need a Primary Key? Let Me Show You the Ways pour en savoir plus sur cette annotation.
Il existe 2 type de stragegies pour mettre à jour un identifiant d’une table :
- stratégie native à la base de donnée :
- stratégie GenerationType.SEQUENCE : cette stratégie permet d’utiliser les sequences qui sont supportés par des bases de données comme comme Oracle, H2..
- stratégie GenerationType.IDENTITY : cette stratégie permet d’utiliser le type IDENTITY (auto-incrément) qui sont supportés par des bases de données comme comme Derby, H2, SQL Server..
- stratégie commune à toute base de donnée :
- stratégie GenerationType.TABLE : cette stratégie consiste à créer une table qui gère les compteurs de toutes les tables de l abase de données. Cette stratégie a l’avantage de fonctionner avec n’importe quelle base mais demande 2 requête SQL (sélection+update) sur cette table lorsqu’un d’un enregistrement doit être inséré.
De ce que j’ai pu constater, la stratégie GenerationType.AUTO se comporte de la même manière que celle de GenerationType.TABLE. Dans notre cas nous avons utilisé la stratégie GenerationType.IDENTITY car (par chance) les bases de données H2 et Derby supportent le type IDENTITY. la classe User à un champs id qui est son identifiant :
[/code]
EN JPA, il n’est pas possible de pouvoir définir plusieurs stratégies dans le mapping et de « switcher » de stratégie en fonction de la base utilisé. La seule strategie commune aux 2 bases est GenerationType.TABLE qui engendre des requêtes SQL en plus.
Je pense que l’idéal est de pouvoir utiliser les solutions natives de chacune des bases (Sequence pour Oracle, Identity pour Derby) avec le MEME mapping JPA (ce qui n’est pas possible avec JPA). Je n’ai pas beaucoup étudié le sujet mais pour réaliser ceci il faudrait (peut être) pour chaque implémentation développer une classe qui permette d’effectuer un swtich de strategie selon le type de la base de donnée.
Pour Hibernate j’ai trouvé ce lien et pour EclipseLink ce lien.
Conclusion
Dans ce billet nous avons vu comment mettre en place JPA en mode « standalone » avec 2 bases de données et 2 implémentations JPA Hibernate et EclipseLink. Dans ce billet nous pouvons retenir que :
- l’EntityManager doit être récupéré de la factory EntityManagerFactory et doit être fermé lorsqu’il n’est plus utilisé. Le code qui permet cela est redondant et l’oubli d’une fermeture d’un EntityManager peut être source de bug.
- l’EntityTransaction qui permet de gérer les transactions doit être ouvert et fermé lorsque l’on souhaite utiliser la méthode EntityManager#persist(Object entity). Le code qui permet cela est redondant et l’oubli d’une fermeture d’un EntityTransaction peut être source de bug.
- la définition d’un persistence-unit dans le fichier persistence.xml décrit les classes persistentes, les implémentation JPA et les informations de connections à la base. Ceci est problématique lorsque l’on souhaite définir plusieurs bases de données ou plusieurs implémentation JPA car on est obligé de dupliquer les informations. Par exemple l’élement XML class sera dupliqué dans les 4 définitions des persistance-unit (2 base * 2 implémentation JPA).
Dans le prochain billet nous verrons comment utiliser le support Spring pour JPA pour utiliser JPA dans un conteneur JPA et verrons comment Spring règle les 3 problèmes décrits ci-dessus.
Vous pouvez lire le billet suivant [step17].