Developpez.com

Une très vaste base de connaissances en informatique avec
plus de 100 FAQ et 10 000 réponses à vos questions

La persistance des données avec Hibernate 2.1.8

De nombreuses applications ont besoin de pouvoir enregistrer d'importantes quantités de données. Les développeurs optent généralement pour une base de données mais compliquent ainsi leur travail. Hibernate est conçu pour vous aider dans cette tâche.

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

Introduction

Hibernate est une bibliothèque Java diffusée sous licence LGPL. Nous allons nous intéresser à la version 2.1.8 datant de janvier 2005. Cette bibliothèque est un service de mapping objet/relationnel (ou mapping O/R) pour la plateforme Java. Son rôle est donc de vous permettre de travailler efficacement avec le contenu d'une base de données relationnelle depuis votre code source Java. Le monde des SGBDR est très différent de celui de Java, qui s'appuie sur le paradigme objet. Les compétences nécessaires sont donc également différentes. Lorsqu'une application Java doit manipuler d'importantes quantités d'informations et doit pour cela réaliser une traduction entre la représentation relationnelle et la représentation objet des données.
Ce travail est délicat puisqu'un développeur Java risquera de ne pas comprendre parfaitement la couche de données et inversement. Néanmoins, quelques outils existent pour simplifier la tâche des développeurs. C'est ainsi que tous les accès aux bases de données en Java sont réalisés à travers des pilotes Java DataBase Connectivity (JDBC) dont l'API masque le type de SGBDR utilisé. Le second outil très employé est le design pattern Data Access Object ou DAO qui pousse encore plus loin le niveau d'abstraction proposé par JDBC.
Bien que suffisants pour permettre aux développeurs d'effectuer tous leurs travaux de gestion des données, ces outils sont limités et nécessitent beaucoup de travail, particulièrement lorsque qu'une des structures, relationnelle ou objet, change. Hibernate vous permettra de transférer directement vos objets Java depuis vers une source de données. Cette approche est souvent opposée à celles proposée par les EJB (pour les versions 2.1 et inférieures). Ses défenseurs parlent d'ailleurs d'utiliser des Plain Old Java Object (POJO) en lieu et place des EJB. Le sujet des EJB et des POJO est très vaste mais sachez simplement que vous pouvez parfaitement recourir à Hibernate pour sauvegarder les données des EJB Session ou pour réaliser la persistance des EJB Entité BMP.

1. Mapping O/R

Hibernate, en tant qu'outil de mapping O/R, doit vous permettre de synchroniser facilement le contenu d'une base de données avec votre application Java. Vous pouvez pour ce faire demander à Hibernate d'analyser une base de données existante pour générer les classes Java correspondantes. L'inverse est également possible. Vous obtiendrez alors les requêtes SQL nécessaires pour créer une base de données capable d'accueillir vos classes Java. Nous allons pour notre part commencer sans base de données ni classes et nous verrons comment créer un document de mapping Hibernate.
Avant d'écrire la moindre ligne de code, nous devons installer Hibernate. Pour ce faire, ouvrez l'archive hibernate-2.1.8.tar.gz pour en extraire hibernate2.jar, que nous avons placé dans notre exemple dans un sous répertoire lib/. Puisque cette bibliothèque n'agit que comme un agent de communication entre Java et une base de données, nous devons en installer une. Les développeurs d'Hibernate assurent un support officiel pour les SGBDR les plus connus : Oracle, MySQL, PostgreSQL, SQP DB, etc. La plupart nécessitent l'installation d'un serveur sur votre machine, avec tous les ennuis qui peuvent en découler. C'est pourquoi nous avons choisi HSQLDB, un SGBDR écrit en Java et disponible sous forme d'une simple archive JAR à intégrer à votre projet. Il nous suffit donc de copier hsqldb.jar dans notre dossier lib pour l'installer. Nous devons enfin créer une base de données vide dans HSQLDB. La procédure est particulièrement simple puisqu'il nous suffit de nous connecter à une base inexistante avec l'utilisateur sa, pour system administrator. Depuis le dossier du projet, exécutez la commande suivante :

 
Sélectionnez

$ java -classpath lib/hsqldb.jar org.hsqldb.util.DatabaseManager -driver org.hsqldb.jdbcDriver 
  -url jdbc:hsqldb:db/books -user sa

Cette commande fera apparaître le HSQL Database Manager et créera automatiquement la base intitulée books/ dans le dossier db/ du projet. Notez que l'archive hsqldb.jar contient le pilote JDBC que vous pouvez utiliser avec toute autre application compatible JDBC pour manipuler votre base de données HSQLDB. L'étape suivante concerne la rédaction du document de mapping O/R pour Hibernate. Il s'agit d'un fichier XML dans lequel nous pouvons réaliser les associations entre les membres d'une classe Java et les colonnes d'une table de la base de données. Le listing 1 présente le document BooksShelf.hbm dont l'extension hbm signifie Hibernate Mapping. Un document HBM peut contenir autant de déclarations de classes que vous le désirez. Dans cet exemple, l'attribut package de l'élément <hibernate-mapping /> est utilisé pour indiquer que toutes les classes déclarées appartiennent au même paquetage Java. Pour mélanger des paquetages, omettez cet attribut et donnez à chaque classe son nom complet. La déclaration d'une classe est réalisée à l'aide de l'élément <class /> auquel nous passons comme attributs son nom et sa table dans le SGBDR. L'attribut table n'est pas indispensable si vous souhaitez utiliser le même nom pour les deux entités. L'élément suivant, <meta />, n'est pas indispensable lors de la rédaction d'un document HBM mais permet de le documenter. Les données inscrites dans ces éléments sont également ajoutées au code source Java que nous générerons à partir de ce fichier. Ainsi, un élément <meta /> de type class-description contient le commentaire JavaDoc qui sera inséré devant la déclaration de la classe Java. Les éléments restants définissent les membres de la classe et donc les colonnes de la table books. Le premier membre est un cas particulier faisant appel à l'élément <id />. Ce dernier sert à indiquer la clé primaire de la table. Nous l'utilisons ici pour déclarer le membre id correspondant à la colonne book_id. Son contenu présente une nouvelle utilisation intéressante de <meta />. Le type scope-set sert à indiquer la portée du mutateur, setId(), dans le code Java. Comme il est impensable de le laisser publique, qui est la portée par défaut, nous changeons ici sa portée en protected. La clé primaire dispose enfin d'un générateur permettant de définir la manière dont elle est calculée. Le générateur natif est celui du SGBDR utilisé. Vous pouvez néanmoins créer votre propre générateur avec l'API Hibernate. Vous pourrez par exemple générer des clés en fonction de la date. Tous les autres membres de la classe Book sont définis avec l'élément <property /> dont les attributs sont parfaitement clairs.
Pour générer le code Java correspondant à ces définitions, nous devons installer les extensions Hibernate. Pour cela, ouvrez hibernate-extensions-2.1.3.tar.gz et copiez les fichiers hibernate-tools.jar, jdom.jar et velocity-1.3.1.jar dans notre dossier lib. Vous devez également copier les archives commons-*.jar et log4j-1.2.8.jar du dossier lib/ d'Hibernate. Après vous être assuré d'avoir placé le chemin de toutes ces archives dans votre variable d'environnement CLASSPATH, exécutez la commande suivante :

 
Sélectionnez

java -classpath lib/hibernate2.jar:$CLASSPATH net.sf.hibernate.tool.hbm2java.CodeGenerator 
  -output=src BooksShelf.hbm

Si la génération de code réussit, vous découvrirez un dossier intitulé src/ contenant le fichier com/loginmag/hibernate/Book.java. Le listing 2 présente un extrait de ce dernier. Pour compiler les fichiers générés, exécutez les commandes suivantes à la racine du projet :

 
Sélectionnez

$ mkdir bin
$ javac -classpath lib/commons-lang-1.0.1.jar -sourcepath src -d bin src/com/loginmag/hibernate/*.java

Le répertoire bin/ contient maintenant notre code Java compilé. Ce répertoire est important car il doit également accueillir le fichier de configuration d'Hibernate. En effet, nous ne lui avons pas encore indiqué quel est le SGBDR à utiliser. Pour ce faire nous devons créer un fichier hibernate.properties à la racine de l'un des chemins du classpath. Le dossier bin/ contenant nos classes, le plus simple est d'y placer ce fichier dont le contenu se trouve dans le listing 3. Le fichier hibernate.properties définit le dialecte à utiliser, ici HSQLDialect. Un dialecte est une classe dont le rôle est de traduire les ordres SQL dans le langage propre à chaque SGBDR car certaines différences peuvent exister. Les autres propriétés définissent la connexion à notre base de données books.
Maintenant qu'Hibernate peut se connecter à HSQLDB, nous pouvons lui demander de générer les tables définies dans le document HBM avec l'outil SchemaExport :

 
Sélectionnez

$ java -classpath hibernate2.jar:$CLASSPATH:lib/hsqldb.jar:bin 
  net.sf.hibernate.tool.hbm2ddl.SchemaExport BooksShelf.hbm

Le classpath utilisé pointe vers l'archive d'HSQLDB qui contient le pilote JDBC ainsi que vers le répertoire bin/ dans lequel se trouvent hibernate.properties et Book.class. L'outil SchemaExport nécessite donc le document HBM et la classe Java compilée. Par défaut, la transaction SQL est directement émise au SGBDR. Vous pouvez également produire un fichier SQL contenant l'ensemble des directives utilisées pour créer les tables en ajoutant les options --text et --output=fichier.sql avant le nom du fichier HBM sur la ligne de commande. Lancez enfin le HSQL Database Manager pour vérifier que la table books a bien été créée.

2. Gestion de la persistance

Une fois Hibernate et HSQLDB configurés, il ne nous reste plus qu'à écrire le code Java exploitant les mappings que nous avons définis. Le listing 4 présente le programme PersistData qui crée deux instances de Book et les sauvegarde avec Hibernate. Pour ce faire, nous devons ouvrir une transaction avec la couche de persistance des données. Un objet Transaction est obtenu à partir d'une session, elle-même créée depuis une configuration. Un objet Configuration peut charger des fichiers mappings de plusieurs manières. Dans notre cas, nous demandons la lecture explicite de BookShelf.hbm dans le répertoire d'exécution courant. Il est également possible de charger des HBM spécifiques à des classes en invoquant la méthode addClass(MaClasse.class). Dans ce cas, le fichier HBM doit s'appeler MaClass.hbm.xml et se trouver dans le même paquetage. Les transactions fonctionnent comme en SQL classique, avec la possibilité de faire des commit et des rollback. La persistance des POJO est réalisée en invoquant la méthode save() de la transaction. Vous pouvez les supprimer avec la méthode delete(). Après avoir compilé et exécuté ce programme, vous devez pour cela copier toutes les archives JAR de hibernate-2.1.8.tar.gz dans lib/, exécutez le Database Manager pour vérifier la présence des nouveaux objets.
Récupérer des POJO depuis la couche de persistance est tout aussi simple. Vous devez au préalable ajouter une <query /> au document HBM ainsi que le montre le listing 5. Les requêtes sont écrites dans un langage proche de SQL et propre à Hibernate. Notre requête se nomme books.booksByAuthor et dispose d'un paramètre appelé author. Le listing 6 présente comment l'exécuter et afficher les résultats. Après avoir obtenu l'objet Query à l'aide de son nom dans la session, nous pouvons fixer la valeur de son paramètre. Enfin, la requête est exécutée en invoquant la méthode list() qui renvoie une List contenant les instances de Book trouvées. Hibernate est donc une bibliothèque puissante et accessible que vous pourrez utiliser avec succès dans toutes vos applications Java.

listing 1
Sélectionnez

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="com.loginmag.hibernate">
  <class name="Book" table="books">
    <meta attribute="class-description">
      Cette classe représente un livre appartenant à une collection.
    </meta>

    <id name="id" type="int" column="book_id">
      <meta attribute="scope-set">protected</meta>
      <meta attribute="field-description">
        L'ISBN n'est pas utilisé comme identificateur unique afin de pouvoir
        posséder plusieurs exemplaires d'un même ouvrage dans sa collection.
      </meta>
      <generator class="native" />
    </id>

    <property name="author" type="string" not-null="true" />

    <property name="title" type="string" not-null="true" />

    <property name="cover" type="string" not-null="true">
      <meta attribute="field-description">
        Contient le chemin vers une image de la couverture. Ce chemin peut
        être une URL ou un chemin local.
      </meta>
    </property>

    <property name="published" type="date">
      <meta attribute="field-description">Date de publication</meta>
    </property>
  </class>
</hibernate-mapping>
listing 2
Sélectionnez

package com.loginmag.hibernate;

import java.io.Serializable;
import java.util.Date;
import org.apache.commons.lang.builder.ToStringBuilder;

/** 
 * Cette classe représente un livre appartenant à une collection.
 */
public class Book implements Serializable {
  /** identifier field */
  private Integer id;

  /** persistent field */
  private String author;

  /** persistent field */
  private String title;

  /** persistent field */
  private String cover;

  /** nullable persistent field */
  private Date published;

  /** full constructor */
  public Book(String author, String title, String cover, Date published) {
    this.author = author;
    this.title = title;
    this.cover = cover;
    this.published = published;
  }
  // ...
}
listing 3
Sélectionnez

hibernate.dialect=net.sf.hibernate.dialect.HSQLDialect
hibernate.connection.driver_class=org.hsqldb.jdbcDriver
hibernate.connection.url=jdbc:hsqldb:db/books
hibernate.connection.username=sa
hibernate.connection.password=
listing 4
Sélectionnez

public class PersistData {
  public static void main(String args[]) throws Exception {
    Configuration config = new Configuration();
    config.addFile(new File("BooksShelf.hbm"));

    SessionFactory sessionFactory = config.buildSessionFactory();
    Session session = sessionFactory.openSession();
    Transaction transaction = null;

    try {
      transaction = session.beginTransaction();
      
      Book book = new Book("Robert CALVET", "Les Japonais", "japonais.jpg");
      session.save(book);

      book = new Book("Terry PRATCHETT", "The Truth", "thetruth.jpg");
      session.save(book);

      transaction.commit();
    } catch (Exception e) {
      if (transaction != null) {
        transaction.rollback();
      }
    } finally {
      session.close();
    }

    sessionFactory.close();
  }
}
listing 5
Sélectionnez

<query name="books.booksByAuthor">
    <![CDATA[from com.loginmag.hibernate.Book as book where book.author=:author]]>
</query>
listing 6
Sélectionnez

Query query = session.getNamedQuery("books.booksByAuthor");
query.setString("author", args[0]);
List results = query.list();
for (int i = 0; i < results.size(); i++)
  System.out.println(((Book) results.get(i)).getTitle());

3. Screenshots

Image non disponible
Le programme HSQL Database Manager est spartiate mais permet de vérifier rapidement le contenu d'une base HSQLDB.



Image non disponible
L'outil SchemaExport crée les tables dans la base de données à partir du document de mapping O/R.



Image non disponible
L'ajout de données dans la base de données se fait en sauvegardant simplement les POJO dans une transaction.



Image non disponible
HSQLDB est un SGBDR léger et performant distribué sous licence BSD sur le site hsqldb.sf.net.



Image non disponible
Le site hibernate.org propose une documentation très complète.



Image non disponible
Les JDO, une autre technologie de persistance des POJO, bénéficie du soutien de Sun Microsystems.



Image non disponible
O'Reilly publie un excellent ouvrage sur Hibernate.



Image non disponible
L'ouvrage Hibernate In Action est disponible sous forme d'eBook sur hibernate.org.



Image non disponible
Bruce Tate, dans Better, Faster, Lighter Java, explique comment créer des applications J2EE légères à l'aide d'Hibernate.



Image non disponible
JBoss met en avant les EJB 3.0 qui sont souvent opposés à des solutions comme Hibernate.



4. Liens

Articles et tutoriels Java
L'essentiel de Java en une heure
L'API java.nio du JDK 1.4
Inversion de contrôle en Java
L'introspection
Le Java Community Process
Conception de tests unitaires avec JUnit
Les Strings se déchaînent
Présentation de SWT
La programmation réseau en Java avec les sockets
Du bon usage de l'héritage et de la composition
Les références et la gestion de la mémoire en Java
Constructeurs et méthodes exportées en Java
Les membres statiques, finaux et non immuables en Java
Les classes et objets immuables en Java
Comprendre et optimiser le Garbage Collector
Les principes de la programmation d'une interface graphique
Les opérateurs binaires en Java
Prenez le contrôle du bureau avec JDIC
Les Java Data Objects (JDO version 1.0.1)
La persistance des données avec Hibernate 2.1.8
Journalisation avec l'API Log4j
Java 5.0 et les types paramétrés
Les annotations de Java 5
Java 1.5 et les types paramétrés
Créer un moteur de recherche avec Lucene
Articles et tutoriels Swing
Threads et performance avec Swing
Rechercher avec style en utilisant Swing
Splash Screen avec Swing et Java3D
Drag & Drop avec style en utilisant Swing
Attendre avec style en utilisant Swing
Mixer Java3D et Swing
Articles et tutoriels Java Web
Redécouvrez le web avec Wicket
Cette création est mise à disposition sous un contrat Creative Commons (Paternité - Partage des Conditions Initiales à l'Identique).