JPA : Utilisation en dehors du module EJB
Récemment, j'ai eu à utiliser JPA pour un site Web. Pour plus de simplicité, je me suis tourné vers un conteneur EJB. Seulement, j'ai eu aussi besoin de créer une multitude de Stateful / Stateless Session Beans, alors que je n'avais vraiment pas besoin de ça. Une application Web toute simple me suffisait largement dans ce cas là.
J'ai alors entrepris de convertir mon application d'entreprise en application Web. Seulement, un problème est apparu assez rapidement : la gestion des transactions de JPA ! En effet, les transactions dont JPA à besoin étaient jusqu'à lors gérées par le conteneur EJB. QUe je n'utilisais plus maintenant.
Je vais donc entreprendre dans cet article un rapide "howto" de l'utilisation de JPA en dehors du module EJB.
On commence avec une entité :
Puis, on passe à la création de notre unité de persistance (dans notre fichier
La différence par rapport à une unité de persistance gérée par le conteneur EJB est que nous n'allons par utiliser JTA pour la gestion des transactions. Les changements sont :
À noter que j'utilise dans cet exemple Toplink (le gestionnaire de persistence par défaut de GlassFish). Les valeurs de la balise
Autre étape très importante : tous les
Inconvénient propre à cette technique : une fois tous les
3.
Avant de créer une
Le code de cette classe est assez simple :
Nous allons maintenant créer une
Suivie d'une déclaration très simple dans
Deux choses sont notables :
4. Utilisation de notre
Et enfin, dernière étape, utilisation de notre
Un rapide exemple, pour stocket un
On récupère ici nos
Voilà rapidement les étapes à suivre pour utiliser JPA dans un contexte non-géré par le conteneur. Les même étapes peuvent être suivies pour utiliser JPA dans une application Java SE, en modifiant la Servlet par un autre mécanisme permettant de charger notre EntityManager au lancement et de le libérer à la fermeture de l'application.
J'ai alors entrepris de convertir mon application d'entreprise en application Web. Seulement, un problème est apparu assez rapidement : la gestion des transactions de JPA ! En effet, les transactions dont JPA à besoin étaient jusqu'à lors gérées par le conteneur EJB. QUe je n'utilisais plus maintenant.
Je vais donc entreprendre dans cet article un rapide "howto" de l'utilisation de JPA en dehors du module EJB.
1. Mise en place des entités
La première partie est commune à nos deux modes de fonctionnement. Il s'agit de créer nos entités JPA et de créer notre unité de persistance.On commence avec une entité :
package com.aperigeek.jpa.entity; @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; // Getters and setters }
Puis, on passe à la création de notre unité de persistance (dans notre fichier
persistence.xml) :
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> <persistence-unit name="AperigeekPU" transaction-type="RESOURCE_LOCAL"> <provider>oracle.toplink.essentials.PersistenceProvider</provider> <non-jta-data-source>jdbc/aperigeek</non-jta-data-source> <class>com.aperigeek.jpa.entity.User</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> <properties> <property name="toplink.ddl-generation" value="create-tables"/> </properties> </persistence-unit> </persistence>
La différence par rapport à une unité de persistance gérée par le conteneur EJB est que nous n'allons par utiliser JTA pour la gestion des transactions. Les changements sont :
- Le
transaction-typede notre unité de persistance devient"RESOURCE_LOCAL", pour indiquer que nous allons gérer nous même nos transactions. - La
DataSourceest déclarée grâce aux balises<non-jta-data-source />,. LesDataSources que nous allons utiliser ne seront pas gérées par JTA.
À noter que j'utilise dans cet exemple Toplink (le gestionnaire de persistence par défaut de GlassFish). Les valeurs de la balise
<provider> et de la propriété toplink.ddl-generation sont donc spécifique à Toplink.2. Récupération d'un EntityManager
Étant donné que nous ne sommes plus dans un contexte géré par le conteneur, nous allons devoir récupérer nosEntityManagers à la main. Le code pour récupérer un EntityManager est assez simple. Il suffit de passer par un EntityManagerFactory, qui nous est fourni directement par une méthode statique de la classe Persistence.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("AperigeekPU");
EntityManager em = emf.createEntityManager();
Autre étape très importante : tous les
EntityManagerFactory et EntityManager doivent être fermés ! Cette étape de fermeture des ressources est d'habitude laissée au conteneur. Dans notre cas, nous allons devoir le faire nous même :
em.close(); emf.close();
Inconvénient propre à cette technique : une fois tous les
EntityManagerFactory et EntityManager fermés, le gestionnaire de persistance s'arrête, pour redémarrer à la prochaine création d'un EntityManagerFactory. Je suis donc parti dans la création d'une Servlet, qui aura pour mission de créer un EntityManager au démarrage de l'application, et de le libérer à l'arrêt de l'application.3. Servlet de chargement, et classe PersistenceManager
Avant de créer une Servlet, j'ai d'abord créé une classe, PersistenceManager, chargée de créer des EntityManagerFactory et EntityManager, et de les fermer ensuite.Le code de cette classe est assez simple :
public class PersistenceManager { private static EntityManagerFactory emf; private static EntityManager em; public static EntityManager getEntityManager() { return (em); } public static void loadEntityManager() { emf = Persistence.createEntityManagerFactory("AperigeekPU"); em = emf.createEntityManager(); } public static void releaseEntityManager() { em.close(); emf.close(); } }
Nous allons maintenant créer une
Serlvet, qui devra appeler les méthodes PersistenceManager.loadEntityManager() au chargement de l'application et PersistenceManager.releaseEntityManager() au déchargement :
public class PersistenceLoaderServlet extends GenericServlet { @Override public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException { throw new UnsupportedOperationException("Not supported yet."); } @Override public void init() throws ServletException { PersistenceManager.loadEntityManager(); } @Override public void destroy() { PersistenceManager.releaseEntityManager(); } }
Suivie d'une déclaration très simple dans
web.xml :
<servlet> <servlet-name>PersistenceLoaderServlet</servlet-name> <servlet-class>com.aperigeek.jpa.servlet.PersistenceLoaderServlet</servlet-class> <load-on-startup>2</load-on-startup> </servlet>
Deux choses sont notables :
- L'attribut
load-on-startupest à 2, pour permettre à laServletd'être chargée au déploiement de l'application. - Il n'y a aucun mapping pour cette
Servlet. Elle n'est en effet pas vouée à être appelée pour répondre à une requête. C'est pour cette même raison que la méthodeservice()de notre servlet lève une exception.
4. Utilisation de notre EntityManager
Et enfin, dernière étape, utilisation de notre EntityManager. Nous allons devoir :
- Récupérer nos
EntityManagers à la main. N'étant plus gérés par le conteneur, nous n'allons pas pouvoir les injecter en tant que dépendance dans nos beans. - Gérer les transactions. Pour la même raison que ci-dessus, les transactions devront être gérées à la main.
Un rapide exemple, pour stocket un
User en base de données :
public void addUser(User user) {
EntityManager em = PersistenceManager.getEntityManager();
em.getTransaction().begin();
em.persist(user);
em.getTransaction().commit();
}
On récupère ici nos
EntityManagers grâce à la méthode PersistenceManager.getEntityManager(). Ensuite, avant chaque opération en base de données, nous devons démarrer une transaction (em.getTransaction().begin();), puis commiter les modifications (em.getTransaction().commit();).Voilà rapidement les étapes à suivre pour utiliser JPA dans un contexte non-géré par le conteneur. Les même étapes peuvent être suivies pour utiliser JPA dans une application Java SE, en modifiant la Servlet par un autre mécanisme permettant de charger notre EntityManager au lancement et de le libérer à la fermeture de l'application.