Utiliser Maven 2 pour générer mon schéma SQL depuis un mapping Hibernate

Créer le modèle objet avec Hibernate, et générer le schémas automatiquement est une pratique courante. Mais lorsqu'on souhaite garder la main sur le schéma généré, en production par exemple ? Voici comment résoudre ce problème en insérant une étape de génération du schémas au sein d'un build piloté par Maven2.

Une utilisation très pratique d'Hibernate est de créer son modèle objet, puis d'utiliser les outils Hibernate pour générer le schéma de sa base de données. Ainsi, on peut se concentrer sur le modèle métier et automatiser la partie base de données (bien que certaines fois, quelques coups de tournevis sont utiles).

D'un autre côté, Maven2 est un outil très puissant permettant d'automatiser toute la construction de son application. Et lorsqu'on se trouve dans le cas précédent, une bonne pratique lors des phases de développement et d'intégration est de générer le schéma de la base de données à chaque modification du code. En développement, on peut configurer Hibernate pour qu'il modifie lui-même la base de donnée lors du démarrage de l'application. Dans d'autres environnements et notamment en production, cette pratique est généralement proscrite et à juste titre, on passe donc par des scripts SQL qui peuvent être validées par les équipes d'exploitation et/ou des DBAs.

Le but est donc de continuer à utiliser les outils Hibernate pour générer le schéma de la base de données, mais de le générer dans un fichier. Puis d'insérer cette phase au sein d'un build piloté par Maven2.

Pour ceux qui ne croient que ce qu'ils voient, le code complet est sur la forge publique Octo :

http://forge.octo.com/svn/java/trunk/octo-samples/generate-hibernate-schema-by-maven/

Dans les exemples, le code est allégé pour être plus lisible.

Partons d'un projet simple :

  • Une seule classe métier nommée "Person"
public class Person {
    private Date dateOfBirth;
    private String firstname;
    private Long id;
    private String lastname;
}
  • L'application est configurée via Spring qui initialise Hibernate :
<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="annotatedClasses">
      <list>
        <value>com.octo.samples.Person</value>
      </list>
    </property>
    <property name="hibernateProperties">
      <props>
        <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
        <prop key="hibernate.jdbc.batch_size">20</prop>
        <prop key="hibernate.hbm2ddl.auto">create-drop</prop>
        <prop key="hibernate.show_sql">true</prop>
      </props>
    </property>
  </bean>
  • Le build du projet est piloté par Maven 2 avec une configuration standard.

Les bases sont maintenant posées, revenons au sujet initial : utiliser Maven pour générer mon schéma SQL depuis mon mapping Hibernate.

1) Rajouter le plugin hibernate dans la configuration Maven

en rajoutant dans son fichier de configuration Maven "pom.xml" la configuration suivante :

<build>
  <plugins>
    <plugin>
      <groupid>org.codehaus.mojo</groupid>
      <artifactid>hibernate3-maven-plugin</artifactid>
      <configuration>
        <componentproperties>
          <export>false</export>
          <update>false</update>
          <outputfilename>create-schema.sql</outputfilename>
        </componentproperties>
      </configuration>
    </plugin>
  </plugins>
</build>

2) Utiliser un fichier hibernate.cfg.xml

En effet, le plugin maven va utiliser configuration hibernate définie dans un fichier "hibernate.cfg.xml", le nom est paramétrable mais il n'est pas encore possible d'utiliser la configuration Spring. Ainsi, nous sommes obligés d'avoir 2 fichiers de configurations, le fichier de configuration spring et le fichier de configuration hibernate. Afin de limiter cet impact on peut faire pointer la configuration spring sur le fichier hibernate afin d'éviter une redondance, souvent source d'erreurs. On limitera aussi l'utilisation du fichier hibernate.cfg.xml à la seule liste des classes annotées/mappées.

Dans la configuration spring, on remplace le tag

<property name="annotatedClasses"/>

par :

<property name="configLocation" value="classpath:hibernate.cfg.xml" />

Configuration Hibernate dans le fichier hibernate.cfg.xml :

<hibernate -configuration>
  <session -factory name="mySessionFactory">
    <property name="hibernate.dialect">org.hibernate.dialect.OracleDialect</property>
    <mapping class="com.octo.samples.Person" />
  </session>
</hibernate>

3) Lancer la commande Maven

La commande à lancer est "mvn hibernate3:hbm2ddl"

Cette commande va alors créer un fichier target/hibernate3/sql/create-schema.sql contenant les instructions de création de la base de données CQFD

Pour plus de détails sur le plugin maven : http://mojo.codehaus.org/maven-hibernate3/hibernate3-maven-plugin/

L'exemple complet sur la forge publique Octo : http://forge.octo.com/svn/java/trunk/octo-samples/generate-hibernate-schema-by-maven/

ps : Il m'arrive souvent d'avoir une base de données comme Oracle en production mais qu'en développement on utilise une base de données mémoire comme HSQLDB afin d'être indépendant de la base de données et la génération des schéma doit donc en tenir compte de l'environnement. Un petit "trick" pour résoudre ce problème :dans notre exemple, la configuration spring pour hibernate utilise le dialect HSQLDialect, alors que la configuration hibernate utilise le dialect OracleDialect.

Dans ce cas, la commande Maven, n'ayant pas connaissance de la configuration Spring, utilise le dialect Oracle alors que lors de l'exécution, Spring utilise en priorité sa configuration et donc le dialect HSQLDB.