Spring/JPA

Un article de JTips.

Aller à : Navigation, rechercher
Développement JPA avec Spring
Auteur : Olivier Hanny
Formation(s) sur le sujet :
Image:icodem.jpg

Sommaire

Introduction

Dans cet article, nous allons décrire le développement d'une couche DAO avec Spring et JPA.

Implémentation des DAO

JpaDaoSupport et JpaTemplate

Une première solution consiste à créer une classe DAO qui hérite de classe JpaDaoSupport de Spring. Par héritage, notre DAO dispose notamment d'un accès à une instance de JpaTemplate qui sera utilisée par délégation pour exécuter les requêtes JPA.

A noter que cette approche existe essentiellement pour des raisons historiques et elle ne permet pas aisément la configuration par annotation. Elle est de ce fait déconseillée au profit de l'approche "plain JPA".

La classe DAO :

public class ProduitDaoImpl extends JpaDaoSupport implements ProduitDao {

  public List<Produit> findProduit(String titre) {
    Map<String, String> params = new HashMap<String, String>();
    params.put("ptitre", titre);
    String query = "select p from Produit as p where p.titre like :ptitre";
    return getJpaTemplate().findByNamedParams(query, params);
  }
  ...
}

La déclaration du bean DAO est effectuée avec la syntaxe XML. Il suffit de lui injecter le bean "entity manager factory" sur la propriété "entityManagerFactory" disponible via la classe JpaDaoSupport.

<bean id="produitDao" class="fr.icodem.librairie.dao.ProduitDaoImpl">
  <property name="entityManagerFactory" ref="myEmf"/>
</bean>

Nous verrons comment déclarer le bean "myEmf" un peu plus loin.

Ecrire des DAO "Plain JPA"

La seconde solution consiste à écrire des DAO qui n'utilisent pas les classes Spring. Cette approche est intéressante car d'une part, elle semblera plus naturelle à un développeur JPA, et d'autre part, elle permet la configuration par annotation.

Avec cette solution, Spring injecte directement une instance "entity manager" dans notre DAO en exploitant l'annotation JPA @PersistenceContext.

@Repository("produitDao")
public class ProduitDaoImpl implements ProduitDao {
 
  @PersistenceContext
  private EntityManager em;

  public List<Produit> findProduit(String titre) {
    String jpql = "select p from Produit as p where p.titre like :ptitre"; 
    Query query = em.createQuery(jpql);
    query.setParameter("titre", titre);
    return query.getResultList(); 
  }
  ...
}

Remarques :

  • Bien que Spring injecte un "entity manager", nous n'aurons à déclarer du point de vue de Spring uniquement un bean de type EntityManagerFactory, pas EntityManager.
  • Spring permet d'injecter un EntityManagerFactory, notamment grâce à l'annotation @PersistenceUnit, mais les DAO doivent alors se charger de récupérer l'entity manager, ce qui alourdit le code

Déclarer l'entity manager factory

Pour déclarer l'entity manager factory, il faut d'abord considérer l'environnement d'exécution de notre code, à savoir :

  • Application standalone (cas d'une application Swing sans remoting, batch, test JUnit...)
  • Tomcat
  • Serveur JEE 5 complet (Glassfish, JBoss, Jonas, WebSphere, WebLogic...)

Cas d'une application standalone

Caractéristiques de l'environnement :

  • La connexion n'est pas gérée nativement par un pool : d'où l'utilisation du bean dataSource de type DriverManagerDataSource (on pourrait aussi utiliser un pool Apache DBCP avec un bean de type org.apache.commons.dbcp.BasicDataSource)
  • Il n'existe pas nativement un conteneur JPA, permettant entre autres d'instancier l'entity manager factory : d'où le bean de type LocalContainerEntityManagerFactoryBean

Fichier XML de configuration Spring :

<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:jee="http://www.springframework.org/schema/jee"
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                          http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
                          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  <context:annotation-config/>
  <context:component-scan base-package="fr.icodem.librairie"/>

  <bean id="librairieDS"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/librairie"/>
    <property name="username" value="root" />
    <property name="password" value="" />
  </bean>
 
  <bean id="emf"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
  </bean>

</beans>

Application Tomcat

Caractéristiques de l'environnement :

  • La connexion est gérée par un pool de connexion déclaré sous Tomcat : d'où la récupération du bean dataSource par lookup JNDI
  • Il n'existe pas nativement un conteneur JPA, permettant entre autres d'instancier l'entity manager factory : d'où le bean de type LocalContainerEntityManagerFactoryBean

Fichier XML de configuration Spring :

<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:jee="http://www.springframework.org/schema/jee"
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                          http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
                          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  <context:annotation-config/>
  <context:component-scan base-package="fr.icodem.librairie"/>

  <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/LibrairieDS"/>

  <bean id="emf"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
  </bean>

</beans>

Application JEE 5

Caractéristiques de l'environnement :

  • La connexion est gérée par un pool de connexion déclaré sous le serveur d'application : d'où la récupération du bean dataSource par lookup JNDI
  • Le serveur intègre nativement un conteneur JPA permettant l'instanciation de l'entity manager factory : on ne déclare donc pas l'entity manager factory avec Spring

Fichier XML de configuration Spring :

<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:jee="http://www.springframework.org/schema/jee"
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                          http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
                          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  <context:annotation-config/>
  <context:component-scan base-package="fr.icodem.librairie"/>

  <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/LibrairieDS"/>


</beans>

Configuration JPA

Il s'agit de renseigner le fichier persistence.xml placé dans le répertoire META-INF de l'archive de déploiement. Encore une fois, la configuration dépend de l'environnement d'exécution.

Cas d'une application standalone

Cet environnement d'exécution ne fournit pas de gestionnaire transationnel JTA (Java Transaction API). On positionne donc l'attribut transation-type à RESOURCE_LOCAL. La data source est pour sa part spécifiée dans la configuration Spring. Par ailleurs, comme on utilise un conteneur Spring / JPA (le bean LocalContainerEntityManagerFactoryBean), il faut préciser l'implémentation JPA à utiliser via l'élément provider.

Fichier persistence.xml :

<persistence>
  <persistence-unit name="librairie" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>

    <class>org.librairie.model.Categorie</class>
    <class>org.librairie.model.Produit</class>
    <exclude-unlisted-classes/>
  </persistence-unit>
</persistence>

Application Tomcat

La configuration est identique au cas d'une application standalone.

Fichier persistence.xml :

<persistence>
  <persistence-unit name="librairie" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>

    <class>org.librairie.model.Categorie</class>
    <class>org.librairie.model.Produit</class>
    <exclude-unlisted-classes/>
  </persistence-unit>
</persistence> 

Application JEE 5

Les serveurs d'application JEE fournissent un gestionnaire transationnel de type JTA. On positionne donc l'attribut transation-type à JTA et on utilise l'élément jta-data-source pour déclarer la data source. Par ailleurs, les serveurs JEE 5 sont livrés avec une implémentation JPA. Il n'est donc pas nécessaire de spécifier le provider JPA.

Fichier persistence.xml :

<persistence>
  <persistence-unit name="librairie" transaction-type="JTA">

    <jta-data-source>java:/jdbc/LibrairieDS</jta-data-source>

    <class>org.librairie.model.Categorie</class>
    <class>org.librairie.model.Produit</class>
    <exclude-unlisted-classes/>
  </persistence-unit>
</persistence>
Récupérée de « http://www.jtips.info/index.php?title=Spring/JPA »
Navigation