Hibernate

Introduction

Hibernate est un framework open source gérant la persistance des objets en base de données relationnelle qu’on peut définir comme un outil de mapping Objet/Relationnel. Ce framework apporte une solution aux problèmes d’adaptation entre le paradigme objet et les SGBD en remplaçant les accès à la base de données par des appels à des méthodes objet de haut niveau.

Il permet de manipuler facilement les données et d’assurer leur persistance. L’objectif est de réduire le temps de développement de l’application en éliminant une grande partie du code SQL à écrire pour interagir avec la base de données et en encapsulant le code SQL résiduel. Les développeurs manipulent les classes dont les données doivent être persistantes comme des classes Java normales. Seule une initialisation correcte d’Hibernate doit être effectuée, et quelques règles respectées lors de l’écriture et la manipulation des classes persistantes. Hibernate se compose de différents modules développés par plusieurs équipes :

  • Core : Le module principal d’Hibernate contient les fonctionnalités clés telles que les sessions, les transactions, le cache d’objet ou le langage SQL.
  • Annotations : Apporte le support des Annotations tel que décrit dans JSR 175. Cette approche permet d’éviter la description de la correspondance entre les champs d’une table et les champs du POJO en XML.
  • Entity Manager : Permet le support de JSR 220 JPA par le module Core.
  • Shards : Ce module permet la partition horizontale du Core Hibernate.
  • Validator : Module de validation des contraintes d’entité de la base de données implantée sous forme d’annotations tels que les plages de valeurs autorisées, les formats de chaîne de caractère, la détection des valeurs nulles, etc…
  • Search : Le dernier module apporte une couche d’abstraction pour la recherche de Lucene appliquée sur les entités persistantes maintenues par Hibernate.
  • Tools : Ensemble d’outils pour Ant ou Eclipse facilitant le développement avec Hibernate.


Site officiel

Vous trouverez le site officiel de Hibernate à l’adresse suivante : http://www.hibernate.org/

Wikipédia

Je vous invite à regarder les articles français et anglais de la célèbre encyclopédie Wikipédia sur Hibernate.

Tutoriel

Avant d’entamer ce tutoriel, vérifiez que vous avez bien les pré requis :

  • Le JDK doit être installé sur votre machine.
  • Eclipse doit être installé sur votre machine (il est conseillé d’utiliser la version Eclipse IDE for Java EE Developers pour ce tutoriel).

Ce tutoriel a pour but de prendre en main les modules Hibernate Core et Annotations. Nous allons choisir comme source de données pour ce tutoriel HSQLDB car il a été présenté précédemment. Il est évidemment possible d’en choisir une autre comme PostGreSQL, Oracle, Mysql ou même un simple fichier XML pourvu que vous disposiez des drivers JDBC adaptés.

Création du projet dans Eclipse

  • Lancer Eclipse.
  • On crée un nouveau projet. Pour cela on ouvre le menu File → New → Java Project.
  • La fenêtre de wizard apparaît alors. Nous allons simplement indiquer ici le nom du projet dans le champ Project name : sf-tutorial-hibernate
  • On peut ensuite cliquer sur Finish. Le projet apparait alors dans l’onglet Package qui répertorie les différents projets du workspace.


Ajout des librairies

  • Il faut télécharger les librairies des modules Hibernate Core et Annotations aux adresses suivantes : Hibernate Core et Hibernate Annotations
  • On va ensuite extraire le contenu des archive zip dans notre projet. Pour ce faire, on ouvre un explorateur de fichiers, pour accéder au workspace (sous Ubuntu il sera situé par défaut dans /home/user/workspace), on va dans le répertoire du projet, dans lequel on crée un nouveau répertoire lib qui contiendra les librairies. On extrait ensuite les archives dedans.

Si le répertoire lib n’apparait pas dans votre projet sous Eclipse, sélectionnez votre projet et appuyez sur F5 pour rafraîchir le projet.

  • Il faut ensuite spécifier à Eclipse dans notre projet qu’on a ajouté une librairie pour pouvoir l’utiliser. On fait un clic droit sur le projet dans le package explorer qui ouvre un menu contextuel : on ouvre Build Path → Configure Build Path. On clic sur l’onglet Libraries, puis sur le bouton add JARS…. Il suffit ensuite d’ajouter toutes les bibliothèques du module Core (hibernate3.jar, etc…) et toutes celles du module Annotations (hibernate-annotations.jar, etc…) situées dans les sous répertoires de lib du projet sf-tutorial-hibernate. Il ne faut pas oublier d’ajouter aussi la bibliothèque associée au SGBD qu’on souhaite utiliser. Dans notre cas on prend hsqldb.jar que l’on a téléchargé auparavant dans le tutoriel HSQLDB. On valide les modifications sur le projet en cliquant sur OK. Nous sommes maintenant prêts à débuter le code!


Configuration d’Hibernate

Création du fichier de configuration xml

Il faut tout d’abord créer un fichier XML nommé hibernate.cfg.xml à la racine du répertoire src du projet qui va permettre d’indiquer au moteur d’Hibernate les paramètres de connexion à la base de données et les drivers à utiliser. Si vous préférez utiliser par exemple PostGreSQL au lieu d’HSQLBD, il suffit de modifier le fichier de configuration ci dessous.

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration
	PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
	"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
 
<hibernate-configuration>
	<session-factory>
		<!-- local connection properties -->
		<property name="hibernate.connection.url">jdbc:hsqldb:mem:database</property>
		<property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
		<property name="hibernate.connection.username">sa</property>
		<property name="hibernate.connection.password"></property>
		<!-- <property name="hibernate.connection.pool_size"></property> -->
 
		<!-- dialect for HypersonicSQL -->
		<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
 
		<property name="hibernate.show_sql">false</property>
		<property name="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
		<property name="hibernate.hbm2ddl.auto">update</property>
 
		<mapping package="com.scub.foundation.tutorial.hibernate"/>
		<mapping class="com.scub.foundation.tutorial.hibernate.Utilisateur"/>
 
	</session-factory>
</hibernate-configuration>


Création du fichier de mapping xml

Ce fichier est un élément majeur puisqu’il va permettre à Hibernate de faire le pont entre les classes de persistance (nos classes Java) et la source de données (la base de données HSQLBD). Nous n’aurons ici pas besoin de créer ce fichier monfichier.hbm.xml dans la mesure où nous allons utiliser le module Annotations d’Hibernate.

Il suffit de déclarer les packages et les classes annotées dans le fichier de configuration XML habituel(hibernate.cfg.xml). Afin de mieux comprendre, nous allons dans un premier temps créer une classe annotée et ensuite nous rajouterons les lignes nécessaires dans le fichier hibernate.cfg.xml.

Initialisation d’Hibernate

Pour initialiser Hibernate nous allons créer un petit wrapper dans une classe statique d’initialisation, connue en tant que HibernateUtil. Voici le code que doit contenir cette classe :

package com.scub.foundation.tutorial.hibernate;
 
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
 
/**
 * Wrapper pour démarrer Hibernate dans un bloc statique d'initialisation.
 * @author Scub-Foundation
 */
public class HibernateUtil {
 
	private static final SessionFactory sessionFactory;
 
	static {
		try {
			// On créé une factory de type Annotation afin de les prendre en charge
			sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
		} catch (Throwable ex) {
			// Log exception!
			throw new ExceptionInInitializerError(ex);
		}
	}
 
	/**
	 * Accesseur.
	 * @return la session existante
	 * @throws HibernateException
	 */
	public static Session getSession() throws HibernateException {
		return sessionFactory.openSession();
	}
 
	/**
	 * Ferme la session Hibernate.
	 * @throws HibernateException
	 */
	public static void closeSession() throws HibernateException {
		sessionFactory.close();
	}
}


Création des classes

Nous sommes désormais prêts pour créer une classe java tout ce qu’il y a de plus simple à laquelle nous allons ajouter des annotations. C’est un exemple volontairement simpliste afin de se concentrer sur Hibernate. Vous allez voir c’est un jeu d’enfant ! Nous allons reprendre l’exemple utilisé dans le tutoriel précédent, et voir comment on peut simplement avec des annotations mapper notre classe pour qu’elle représente une table dans une base de données.

  • On se place sur le répertoire src du projet sf-tutorial-hibernate, puis on fait un clic droit, New → Class.
  • La fenêtre de wizard New Java Class est normalement apparue. On indique dans le champ Package com.scub.foundation.tutorial.hibernate et on entre dans Name le nom Utilisateur puis on clique sur Finish.
  • Voici le code de la classe avec des explications ci-dessous :
package com.scub.foundation.tutorial.hibernate;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
 
/**
 * Classe modélisant un utilisateur.
 * @author Scub-Foundation
 */
@Entity
@Table(name = "users")
public class Utilisateur {
 
	/** identifiant de l'utilisant. */
	@Id
	@GeneratedValue
	@Column(name = "idUser")
	private int idUser;
 
	/** login de l'utilisateur. */
	@Column(name = "login")
	private String login;
 
	/** mot de passe de l'utilisateur. */
	@Column(name = "password")
	private String password;
 
	/**
	 * Constructeur.
	 * @param login login de l'utilisateur
	 * @param password mot de passe de l'utilisateur
	 */
	public Utilisateur(String login, String password) {
		this.login = login;
		this.password = password;
	}
 
	/**
	 * Accesseur.
	 * @return le mot de passe
	 */
	public String getPassword() {
		return password;
	}
 
	/**
	 * Accesseur.
	 * @param password
	 */
	public void setPassword(String password) {
		this.password = password;
	}
 
	/**
	 * Accesseur.
	 * @return l'utilisateur
	 */
	public int getIdUser() {
		return idUser;
	}
 
	/**
	 * Accesseur.
	 * @return l'identifiant
	 */
	public String getLogin() {
		return login;
	}
 
	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		String utilisateur = this.idUser + "\t" + this.login + "\t" + this.password;
		return utilisateur;
	}
}

On a une classe Utilisateur avec trois attributs idUser, login et password. Nous avons ajouté des annotations au dessus de la déclaration de la classe et des attributs. Une petite explication s’impose, chaque classe POJO persistante liée est un entity bean et est déclarée en utilisant l’annotation @Entity (au niveau de la classe)

  • @Entity déclare la classe comme un entity bean (ie une classe POJO persistante).
  • @Id déclare la propriété identifiante de cet entity bean. Cela permet de mapper une clé primaire.

On a défini ensuite la Table :

  • @Table est positionnée au niveau de la classe ; cela vous permet de définir le nom de la table, du catalogue et du schéma pour le mapping de votre entity bean. Si aucune @Table n’est définie les valeurs par défaut sont utilisées : le nom de la classe de l’entité (sans le nom de package).

Puis on va mapper les propriétés : Chaque propriété (attribut ou méthode) non statique non transient d’un entity bean est considérée persistante, à moins que vous l’annotiez comme @Transient. L’exemple est simple : nous avons une clé primaire idUser, et deux autres colonnes dans la table login et password.

  • @Column déclare l’attribut comme une colonne de la table. le paramètre name permet de spécifier le nom de la colonne.
  • @GeneratedValue permet de spécifier que le contenu de la colonne sera généré automatiquement (correspond à l’auto_increment en SQL). On utilise cette annotation avec l’annotation @Id. Il est possible de passer des paramètres complémentaires à cette annotation pour spécifier comment générer la clé.

On peut maintenant indiquer dans le fichier de configuration hibernate.cfg.xml que la classe Utilisateur est mappée. Pour cela on ajoute les lignes suivantes dans le fichier de xml entre les balises <session-factory> … </session-factory> :
<mapping package= »com.scub.foundation.tutorial.hibernate »/>
<mapping/>
Ces lignes indiquent que le package com.scub.foundation.tutorial.hibernate contient des classes mappées dont la classe Utilisateur.

Test du code produit

Il s’agit maintenant de tester le code produit. Pour cela nous allons créer la classe de test HibernateTest suivante :

package com.scub.foundation.tutorial.hibernate;
 
import java.util.List;
 
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Order;
 
/**
 * Classe de test du tutoriel sur Hibernate.
 * @author Scub-Foundation
 * 
 */
public class HibernateTest {
 
	/**
	 * Méthode de test.
	 * @param args
	 */
	public static void main(String[] args) {
		// On récupère la session Hibernate créée dans HibernateUtil
		Session session = HibernateUtil.getSession();
 
		// On créé une transaction pour pallier à d'éventuel problèmes lors de l'insertion des tuples dans la table
		Transaction transaction = session.beginTransaction();
 
		// On créé nos objets
		Utilisateur user1 = new Utilisateur("straumat", "straumat16");
		Utilisateur user2 = new Utilisateur("jgoncalves", "jgoncalves16");
		Utilisateur user3 = new Utilisateur("sgoumard", "sgoumard16");
 
		// On les enregistre dans la base HSQLDB
		session.save(user1);
		session.save(user2);
		session.save(user3);
 
		// On valide la transaction
		transaction.commit();
 
		// On fait ensuite une recherche sur la table users
		// On selectionne tous les enregistrements, qu'on récupère dans une liste en les triant par ordre alphabétique de la colonne login
		List resultats = session.createCriteria(Utilisateur.class).addOrder(Order.asc("login")).list();
 
		// On affiche le résultat de la requête
		System.out.println("contenu de la table users : ");
		System.out.println("id\tlogin\t\tpassword");
		for(Utilisateur utilisateurParcouru : resultats) {
			System.out.println(utilisateurParcouru);
		}
 
		// On clos la session
		HibernateUtil.closeSession();
	}
}

On sélectionne la classe HibernateTest dans le package explorer, on fait un clic droit Run As → Java Application. On obtient alors normalement le résultat suivant :

Voici une petite description du test qui a été réalisé :

  • Lors de l’exécution de la classe HibernateTest le bloc statique de la classe HibernateUtil va être exécuté ce qui va créer une session Hibernate qui va prendre en paramètre les éléments du fichier de configuration hibernate.cfg.xml

On commence dans la classe de test par récupérer cette session, puis on instancie une Transaction et trois Utilisateur, que l’on va ensuite insérer dans la base de données grâce à la méthode save() après la validation de la Transaction avec la méthode commit(). L’utilisation des Transactions sert pour pallier à d’éventuels problèmes lors de l’insertion.

  • Pour vérifier ensuite que nos trois utilisateurs ont bien été insérés dans la base, nous effectuons une requête de sélection à l’aide des Criteria. Cela permet d’effectuer des requêtes sans avoir à écrire en SQL, c’est un outil très pratique dès lors qu’on doit réaliser des requêtes assez complexes. Ici nous avons une requête basique, qui sélectionne l’ensemble de la table users et la trie par ordre alphabétique de la colonne login. Il est important de noter que l’on récupère directement une collection d’objets de type Utilisateur. On affiche ensuite le contenu de la collection pour voir le contenu de la requête.
  • On ferme ensuite la session Hibernate.


Pourquoi utiliser Hibernate?


Un outil indispensable pour le développement en couche

Le développement en couche est indispensable pour être capable d’obtenir des applications capables de prendre en compte les changements. Hibernate offre la possibilité de créer une couche d’abstraction d’accès aux bases de données. Son utilisation rend l’application indépendante du SGBD auquel elle fait appel. Un simple changement des paramètres de configuration d’Hibernate permet de passer par exemple d’une base de données HSQLBD, utilisée souvent durant les tests à PostGreSQL qui sera utilisé lors du passage en production. C’est la garantie d’avoir des applications plus homogènes, plus facilement migrables.

Optimisation du temps de développement

Grâce à Hibernate, une grande partie du SQL est supprimée du code, ce qui permet d’avoir des objets métiers plus faciles à manipuler. Lorsqu’on effectue une requête sur la base de données, on récupère directement des objets métiers. C’est un gain de temps pour les développeurs.

Télécharger les sources

Les sources du projet Eclipse pour ce tutoriel sont téléchargeables sur Source Forge à l’adresse suivante.