Une des tâches les plus chronophages dans l'écriture d'un programme est celle qui consiste à coder les accès aux tables des bases de données. C'est fastidieux et sans réelle plus value. Heureusement, des outils, appelés ORM (Object Relational Mapping) se chargent de cet aspect : les données sont alors encapsulées dans des classes, avec une instance par enregistrement.
Dans le monde Java, le plus connu est Hibernate, qui contient, de plus, un mécanisme de cache permettant de stocker localement des enregistrements des tables. Ce mécanisme de cache est surtout intéressant en développement web, mais il présente aussi des inconvénients (moins bonne maîtrise de ce qui est chargé, possible incohérence entre la base de données et le cache d'Hibernate dans certaines situations, sollicitation plus importante de la base de données dans certains cas de figure, etc.). Néanmoins, les avantages sont tels, notamment en vitesse d'écriture, qu'il serait dommage de s'en priver.
Hibernate travaille avec une description des tables, réalisée au format XML. Heureusement, un outil permet de générer automatiquement ces fichiers.
Pour plus d'informations concernant l'utilisation d'Hibernate, voici quelques documents que j'ai été amené à consulter :
Dans Maven (fichier pom.xml
), déclarez la dépendance suivante :
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.5.Final</version>
</dependency>
Créez le fichier src/main/resources/hibernate.cfg.xml
, avec les quelques lignes suivantes :
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory name="">
<property name="hibernate.bytecode.use_reflection_optimizer">false</property>
<property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="hibernate.connection.url">jdbc:hsqldb:mabase</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.default_schema">PUBLIC</property>
<property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
<property name="hibernate.search.autoregister_listeners">false</property>
<property name="hibernate.max_fetch_depth"/>
<property name="hibernate.show_sql">true</property>
</session-factory>
</hibernate-configuration>
Il s'agit d'un exemple avec une base de données hsqldb ; vous devrez adapter la configuration à votre contexte.
Voici un exemple qui fonctionne bien. Basculez dans la perspective Hibernate, puis :
Source : http://www.yuvalararat.com/2008/03/hibernate-auto-generate-hbm-and-java/
Créez la classe suivante :
@SuppressWarnings("deprecation")
public class HibernateUtil {
public static Session session;
static Configuration cfg;
private static final SessionFactory sessionFactory;
static {
try {
boolean debug = false;
if (Parametre.mode.equals("debug"))
debug = true;
if (debug)
System.out
.println("debut de la creation de la session Hibernate");
cfg = new Configuration()
.configure("hibernate.cfg.xml")
.setProperty("hibernate.connection.url",
Parametre.getStringValue("database", "url"))
.setProperty("hibernate.connection.driver_class",
Parametre.getStringValue("database", "DriverClass"))
.setProperty("hibernate.connection.username",
Parametre.getStringValue("database", "Username"))
.setProperty("hibernate.connection.password",
Parametre.getStringValue("database", "Password"))
.setProperty("hibernate.show_sql", String.valueOf(debug));
if (debug)
System.out.println("configuration Hibernate chargée");
sessionFactory = cfg.buildSessionFactory();
if (debug)
System.out.println("sessionFactory hibernate créée");
session = getSessionFactory().openSession();
session.beginTransaction();
if (debug)
System.out.println("Session hibernate ouverte");
} catch (Throwable ex) {
String nl = System.getProperty("line.separator");
String message = "La base de données n'est pas reconnue. Vérifiez qu'elle soit bien en cours de fonctionnement."
+ nl
+ "URL de connexion : "
+ Parametre.getStringValue("database", "url");
JOptionPane.showMessageDialog(Controleur.fenetre.getFenetre(),
message, "Erreur de connexion à la base de données",
JOptionPane.ERROR_MESSAGE);
Controleur.sessionError = true;
System.exit(1);
throw new ExceptionInInitializerError(ex);
}
}
public Session getSession() {
session = HibernateUtil.getSessionFactory().getCurrentSession();
if (!session.isOpen()) {
session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
}
if (!session.getTransaction().isActive())
session.beginTransaction();
return session;
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
Dans cet exemple, vous pouvez vous passer des lignes setProperty, qui ont été rajoutées pour gérer dynamiquement la connexion à la base de données. La classe contient aussi l'affichage de messages en mode "debug", mode debug qui est initialisé dans un fichier de paramètres.
Il s'agit de manipuler une table appelée Trait.
public abstract class Modele {
public Session getSession() {
return HibernateUtil.getSession();
}
}
public class Mtrait extends Modele {
Trait trait;
public Mtrait () {}
public List<Trait> getListe() {
return getSession().createQuery("select trait from Trait trait ").list();
}
/**
* Met a jour le trait considere
* @param trait
*/
public void update (Trait trait) {
getSession().saveOrUpdate(trait);
getSession().getTransaction().commit();
}
/**
* Supprime le trait considere
* @param trait
*/
public void delete(Trait trait) {
getSession().delete(trait);
getSession().getTransaction().commit();
}
}
Dans l'exemple précédent, nous avons simplement mis à jour la table trait, ou plutôt l'objet Trait. Si celui-ci contient plusieurs classes imbriquées, c'est à dire des tables en cascade permettant de décrire les différents composants de la table principale, et si vous souhaitez que toutes vos tables imbriquées soient mis à jour automatiquement quand vous enregistrez l'objet Trait, vous devrez probablement modifier manuellement le ou les fichiers hbm.xml
impliqués.
Il faut modifier tous les fichiers porteurs de relations, si vous avez plusieurs niveaux de tables.
Attention : après avoir modifié manuellement vos fichiers hbm.xml
, ne relancez plus de reverse-ingeneering : vous perdriez toutes vos modifications !
Les exemples sont issus des modifications affichées dans un lecteur GIT.
Voici un exemple portant sur une table fille, appelée IINDIVIDUS_EEL :
- <set name="individuEels" table="INDIVIDU_EEL" inverse="true" lazy="true" fetch="select">
+ <set name="individuEels" table="INDIVIDU_EEL" inverse="true" lazy="true" fetch="select" cascade="all,delete-orphan">
Nous avons rajouté l'attribut cascade="all,delete-orphan", qui va permettre d'assurer la mise à jour automatique.
- <one-to-one name="traitGeom" class="database.TraitGeom"></one-to-one>
+ <one-to-one name="traitGeom" class="database.TraitGeom" cascade="all,delete-orphan"></one-to-one>