Ce site met a disposition le build journalier de la traduction francaise du Maven: The Definitive Guide
Consultez :
  • Les documents de reference sur le projet original
  • Les sources de la traduction fr sur GitHub
  • maven


    7.5. Le module simple-persist

    Ce module définit deux DAOs (Data Access Objects). Un DAO est un objet qui fournit une interface aux opérations de persistances. Dans une application qui utilise un framework de mapping ORM (Object-Relational Mapping) comme Hibernate, un DAO est défini autour d'un objet. Dans ce projet, nous définissons deux objets DAOs : WeatherDAO et LocationDAO. La classe WeatherDAO nous permet de sauvegarder un objet Weather dans la base de données, de le récupérer à partir de son id ou d'une Location spécifique. Le DAO LocationDAO contient une méthode qui permet la récupération d'un objet Location à partir d'un code postal. Commençons par regarder le POM du module simple-persist dans l'Exemple 7.8, « POM du module simple-persist ».

    Exemple 7.8. POM du module simple-persist

    <project xmlns="http://maven.apache.org/POM/4.0.0" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                          http://maven.apache.org/maven-v4_0_0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <parent>
        <groupId>org.sonatype.mavenbook.multispring</groupId>
        <artifactId>simple-parent</artifactId>
        <version>1.0</version>
      </parent>
      <artifactId>simple-persist</artifactId>
      <packaging>jar</packaging>
    
      <name>Simple Persistence API</name>
    
      <dependencies>
        <dependency>
          <groupId>org.sonatype.mavenbook.multispring</groupId>
          <artifactId>simple-model</artifactId>
          <version>1.0</version>
        </dependency>
        <dependency>
          <groupId>org.hibernate</groupId>
          <artifactId>hibernate</artifactId>
          <version>3.2.5.ga</version>
          <exclusions>
            <exclusion>
              <groupId>javax.transaction</groupId>
              <artifactId>jta</artifactId>
            </exclusion>
          </exclusions>
        </dependency>
        <dependency>
          <groupId>org.hibernate</groupId>
          <artifactId>hibernate-annotations</artifactId>
          <version>3.3.0.ga</version>
        </dependency>
        <dependency>
          <groupId>org.hibernate</groupId>
          <artifactId>hibernate-commons-annotations</artifactId>
          <version>3.3.0.ga</version>
        </dependency>
        <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>servlet-api</artifactId>
          <version>2.4</version>
          <scope>provided</scope>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring</artifactId>
          <version>2.0.7</version>
        </dependency>
      </dependencies>
    </project>
    

    Ce POM référence celui du module simple-parent en POM parent et définit quelques dépendances. Les dépendances listées dans le POM du module simple-persist sont :

    org.sonatype.mavenbook.multispring:simple-model:1.0

    Tout comme le module simple-weather, ce module de persistance référence les objets du modèle définis dans le projet simple-model.

    org.hibernate:hibernate:3.2.5.ga

    Nous définissions une dépendance sur la version 3.2.5.ga d'Hibernate. Notez l'exclusion d'une dépendance transitive d'Hibernate, nous effectuons cela à cause de la dépendance javax.transaction:jta qui n'est pas récupérable à partir du repository Maven public. Cette dépendance est l'une de ces dépendances Sun qui ne sont pas encore publiées dans le dépôt Maven libre 'central'. Afin d'éviter le message nous demandant d'aller télécharger cette dépendance, nous excluons simplement celle-ci d'Hibernate.

    javax.servlet:servlet-api:2.4

    Comme le projet contient une Servlet, nous devons inclure une version de Servlet API, ici la 2.4.

    org.springframework:spring:2.0.7

    Cette dépendance inclut l'intégration de Spring Framework.

    Note

    C'est généralement une bonne pratique de dépendre uniquement des composants Spring que vous utilisez. Spring dispose d'ailleurs d'artefacts spécifiques tels que spring-hibernate3.

    Pourquoi dépendre de Spring ? Lorsqu'il est utilisé avec son intégration Hibernate, Spring permet l'utilisation des classes Helpers comme HibernateDaoSupport facilitant l'utilisation d'Hibernate. Pour savoir ce qu'il est possible de faire avec la classe HibernateDaoSupport, regardez le code de la classe WeatherDAO de l'Exemple 7.9, « Classe WeatherDAO du module simple-persist ».

    Exemple 7.9. Classe WeatherDAO du module simple-persist

    package org.sonatype.mavenbook.weather.persist;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.hibernate.Query;
    import org.hibernate.Session;
    import org.springframework.orm.hibernate3.HibernateCallback;
    import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
    
    import org.sonatype.mavenbook.weather.model.Location;
    import org.sonatype.mavenbook.weather.model.Weather;
    
    public class WeatherDAO extends HibernateDaoSupport 1 {
    
        public WeatherDAO() {}
    
        public void save(Weather weather) {2
          getHibernateTemplate().save( weather );
        }
    
        public Weather load(Integer id) {3
          return (Weather) getHibernateTemplate().load( Weather.class, id);
        }
    
        @SuppressWarnings("unchecked")
        public List<Weather> recentForLocation( final Location location ) {
          return (List<Weather>) getHibernateTemplate().execute(
            new HibernateCallback() {4
            public Object doInHibernate(Session session) {
              Query query = getSession().getNamedQuery("Weather.byLocation");
              query.setParameter("location", location);
              return new ArrayList<Weather>( query.list() );
            }
          });
        }
    }

    C'est tout ? Pas vraiment, mais cela suffit pour obtenir une classe qui permet d'insérer de nouvelles lignes, en récupérer par leur clé primaire et récupérer toutes les lignes Weather qui référent à un id de la table Location. Nous ne pouvons clairement pas arrêter ce livre et insérer les cinq cents pages qu'il faudrait pour comprendre les subtilités d'Hibernate, contentons-nous de quelques rapides explications :

    1

    Cette classe étend HibernateDaoSupport. Ce qui signifie que cette classe est associée à une SessionFactory Hibernate qui permet la création d'objets Session Hibernate. Avec Hibenate, chaque opération s'effectue par l'intermédiaire d'un objet Session, celle-ci s'occupe des accès à la base de données et de la connexion via la DataSource JDBC. Étendre HibernateDaoSupport vous permet également d'accéder au template HibernateTemplate en utilisant la méthode getHibernateTemplate().

    2

    La méthode save() prend une instance de la classe Weather et appelle la méthode save() de la classe HibernateTemplate. L'HibernateTemplate simplifie les appels aux opérations standards d'Hibernate et convertit les exceptions typées des différentes bases de données en RuntimeException. Appelons la méthode save() qui insère une nouvelle ligne dans la table Weather. La mise à jour d'une entité déjà en base passe par la méthode update(). La méthode saveOrUpdate() crée une nouvelle ligne ou la modifie en fonction de la présence d'une valeur non-nulle dans la propriété id de la classe.

    3

    De la même manière, la méthode load() se contente d'appeler la méthode du même nom de l'instance HibernateTemplate. Cette méthode prend un objet Class et un Serializable en paramètres. Dans notre exemple, l'objet Serializable correspond à la valeur de id de l'objet Weather à charger.

    4

    Cette dernière méthode recentForLocation() appelle une NamedQuery définie dans l'objet modèle Weather. Il s'agit de la requête nommée "Weather.byLocation" dont le code est "from Weather w where w.location = :location". Nous chargeons cette NamedQuery en utilisant une référence de la Session Hibernate dans un HibernateCallback qui est lancé via un appel à la méthode execute() de l'HibernateTemplate. Dans cette méthode, nous remplissons le paramètre location avec le paramètre passé dans la méthode recentForLocation().

    C'est maintenant le bon moment de clarifier certains points. Les classes HibernateDaoSupport et HibernateTemplate proviennent du framework Spring. Elles ont été créées par Spring pour faciliter l'écriture de DAO. Pour cela, nous avons besoin de modifier la configuration de l'ApplicationContext Spring du module simple-persist. Le fichier XML de l'Exemple 7.10, « ApplicationContext Spring du module simple-persist » se trouve dans le dossier src/main/resources dans un fichier nommé applicationContext-persist.xml.

    Exemple 7.10. ApplicationContext Spring du module simple-persist

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
                 http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"
        default-lazy-init="true">
    
        <bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
            <property name="annotatedClasses">
                <list>
                    <value>org.sonatype.mavenbook.weather.model.Atmosphere</value>
                    <value>org.sonatype.mavenbook.weather.model.Condition</value>
                    <value>org.sonatype.mavenbook.weather.model.Location</value>
                    <value>org.sonatype.mavenbook.weather.model.Weather</value>
                    <value>org.sonatype.mavenbook.weather.model.Wind</value>
                </list>
            </property>
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.show_sql">false</prop>
                    <prop key="hibernate.format_sql">true</prop>
                    <prop key="hibernate.transaction.factory_class">
                      org.hibernate.transaction.JDBCTransactionFactory
                    </prop>
                    <prop key="hibernate.dialect">
                      org.hibernate.dialect.HSQLDialect
                    </prop>
                    <prop key="hibernate.connection.pool_size">0</prop>
                    <prop key="hibernate.connection.driver_class">
                      org.hsqldb.jdbcDriver
                    </prop>
                    <prop key="hibernate.connection.url">
                      jdbc:hsqldb:data/weather;shutdown=true
                    </prop>
                    <prop key="hibernate.connection.username">sa</prop>
                    <prop key="hibernate.connection.password"></prop>
                    <prop key="hibernate.connection.autocommit">true</prop>
                    <prop key="hibernate.jdbc.batch_size">0</prop>
                </props>
            </property>
        </bean>
    
        <bean id="locationDAO" 
                 class="org.sonatype.mavenbook.weather.persist.LocationDAO">
            <property name="sessionFactory" ref="sessionFactory"/>
        </bean>
        
        <bean id="weatherDAO" 
                 class="org.sonatype.mavenbook.weather.persist.WeatherDAO">
            <property name="sessionFactory" ref="sessionFactory"/>
        </bean>
     </beans>

    Cet ApplicationContext définit plusieurs choses. Le bean sessionFactory est le bean par lequel les DAOs pourront récupérer les Session Hibernate. Ce bean est une instance de la classe AnnotationSessionFactoryBean et fournit une liste d'annotatedClasses. Notez que cette liste de classes annotées est la liste des classes définies dans le module simple-model. Ensuite, la sessionFactory est configurée par l'intermédiaire d'une liste de propriétés de configuration Hibernate (hibernateProperties). Dans cet exemple, nous utilisons les propriétés Hibernate suivantes :

    hibernate.dialect

    Cette propriété permet de contrôler le SQL qui sera généré pour notre base de données. Comme nous utilisons HSQLDB, nous choisissons le dialecte org.hibernate.dialect.HSQLDialect. Hibernate possède nativement des dialectes pour la majorité des bases de données comme Oracle, MySQL, Postgres et SQL Server.

    hibernate.connection.*

    Dans cet exemple, nous configurons une connexion JDBC à partir de la configuration Spring. Nos applications sont configurées pour se lancer avec la base de données HSQLDB du répertoire ./data/weather. Dans une vraie application d'entreprise, vous utiliseriez plutôt quelque chose comme JNDI pour externaliser la configuration de la base de données de votre code applicatif.

    Dans la suite du fichier XML, sont définis les deux DAOs du module simple-persist. Chacun d'entre eux contient une référence vers la sessionFactory Hibernate que nous venons de définir. Comme l'ApplicationContext du module simple-weather, ce fichier applicationContext-persist.xml définit l'architecture du sous-module. Si vous travailliez avec un plus grand nombre de classes persistantes, vous pourriez trouver utile de les externaliser dans un ApplicationContext séparé.

    Il reste encore une dernière pièce du puzzle. Plus tard dans ce chapitre, nous verrons comment nous pouvons utiliser le plugin Maven Hibernate3 pour générer notre schéma de base de données à partir des objets annotés du modèle. Pour cela, le plugin Maven Hibernate3 a besoin de récupérer les paramètres de connexion JDBC, la liste des classes annotées et un autre fichier de configuration nommé hibernate.cfg.xml présent dans le répertoire src/main/resources. Le contenu de ce fichier (qui duplique une partie de la configuration du fichier applicationContext-persist.xml) nous permet l'utilisation du plugin Maven Hibernate3 pour générer le DDL (Data Definition Language) du schéma de la base de données. Consultez l'Exemple 7.11, « Fichier hibernate.cfg.xml du module simple-persist ».

    Exemple 7.11. Fichier hibernate.cfg.xml du module simple-persist

    <!DOCTYPE hibernate-configuration PUBLIC
            "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
            "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
            
    <hibernate-configuration>
      <session-factory>
            
        <!-- dialecte SQL -->
        <property name="dialect">org.hibernate.dialect.HSQLDialect</property>
        
        <!-- Configuration des connexions -->
        <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
        <property name="connection.url">jdbc:hsqldb:data/weather</property>
        <property name="connection.username">sa</property>
        <property name="connection.password"></property>
        <property name="connection.shutdown">true</property>
        
        <!-- Pool de connexion JDBC (utilisation du built-in) -->
        <property name="connection.pool_size">1</property>
        
        <!-- Définit le scope des context Hibernate -->
        <property name="current_session_context_class">thread</property>
        
        <!-- Désactivation du cache de second niveau  -->
        <property name="cache.provider_class">
          org.hibernate.cache.NoCacheProvider
        </property>
        
        <!-- Affiche les requêtes sur stdout -->
        <property name="show_sql">true</property>
        
        <!-- Désactivation du batching, permet à HSQLDB de propager ses erreurs correctement. -->
        <property name="jdbc.batch_size">0</property>
        
        <!-- Liste de toutes les classes annontées -->
        <mapping class="org.sonatype.mavenbook.weather.model.Atmosphere"/>
        <mapping class="org.sonatype.mavenbook.weather.model.Condition"/>
        <mapping class="org.sonatype.mavenbook.weather.model.Location"/>
        <mapping class="org.sonatype.mavenbook.weather.model.Weather"/>
        <mapping class="org.sonatype.mavenbook.weather.model.Wind"/>
            
      </session-factory>
    </hibernate-configuration>

    Les contenus de l'Exemple 7.10, « ApplicationContext Spring du module simple-persist » et de l'Exemple 7.1, « POM du projet simple-parent » sont redondants. Le XML de l'ApplicationContext Spring sera utilisé par l'application web et l'application en ligne de commande, le fichier hibernate.cfg.xml n'est présent que pour faire fonctionner le plugin Maven Hibernate3. Plus tard dans ce chapitre, nous verrons comment utiliser ce fichier hibernate.cfg.xml et le plugin Maven Hibernate3 pour générer le schéma de la base de données basé sur les objets annotés du projet simple-model. Ce fichier hibernate.cfg.xml configure les propriétés des connexions JDBC et énumère la liste des objets annotés.