Vous vous en doutez, je vais pas parler photographie ou botanique. Je vais capitaliser sur un problème qui m'a tenu en haleine une bonne partie de l'après-midi.

Le symptôme: certaines tables ne sont pas créées automatiquement par Hibernate. Pourtant j'ai bien mis la bonne valeur update pour la propriété hibernate.hbm2ddl.auto, mais rien n'y faisait.

J'ai réussi à activer les logs pour Hibernate (au niveau WARN ça suffit), et j'ai repéré les lignes suivantes :

15:49:25,073 - ERROR - org.hibernate.tool.hbm2ddl.SchemaUpdate - Unsuccessful: create table XXX (key varchar(255) not null, description longtext, multi bit not null, type integer, primary key (key))
15:49:25,074 - ERROR - org.hibernate.tool.hbm2ddl.SchemaUpdate - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'varchar(255) not null, description longtext, multi bit not null, type integer, p' at line 1

Et là, c'est évident: j'ai en effet un champ key, qui est un mot-clé réservé en SQL.

Je peste: mais comment se fait-ce ? Pourquoi Hibernate ne sait-il pas échapper ça tout seul comme un grand ?

De là, il y a plusieurs solutions. La plus simple, c'est de changer le nom du champ. Mais je ne suis pas homme à se laisser aller à tant de facilité, ce n'est pas un ORM qui va faire sa loi ! On peut aussi changer uniquement le nom du mapping, voir préciser dans le mapping qu'il faut échapper. Mais ça ne me plait pas trop non plus. J'aimerais une solution plus universelle.

C'est une question sur StackOverflow qui m'a mis sur la voie. On peut donc configurer une propriété spécifique Hibernate (à partir de 3.5) hibernate.globally_quoted_identifiers=true.

Mais ils pointent aussi vers une manière JPA 2, je continue donc à fouiller dans la spécification officielle bien pénible à lire, notamment la partie 2.13, et je trouve la solution: il suffit d'ajouter un simple élément delimited-identifiers au bon endroit dans le bon fichier.

Ah oui, d'accord. Donc quel fichier ? Quel endroit ? Et c'est grâce à Google (loué soit-il) que je trouve la solution, que je vous livre ici.

Dans un fichier META-INF/orm.xml, mettez le contenu suivant :

<?xml version="1.0" encoding="UTF-8"?>
 
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
 http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
	version="2.0">
	<persistence-unit-metadata>
		<persistence-unit-defaults>
			<delimited-identifiers />
		</persistence-unit-defaults>
	</persistence-unit-metadata>
</entity-mappings>

Si le fichier existe déjà, débrouillez vous pour fusionner, ça devrait pas être trop compliqué.

Il faut savoir que META-INF/orm.xml permet normalement de configurer des informations de mapping. Il est utilisé dans le cas où on serait assez idiot pour ne pas vouloir utiliser les annotations Java 5. Donc la plupart du temps, on n'a pas besoin de l'utiliser. Sauf là :-)

Et si vous faites des tests unitaires avec DbUnit, vous voudrez peut-être aussi lire mon billet sur l'échappement des noms de tables et de champs avec DbUnit, qui traite d'un problème que j'ai eu lorsque j'ai mis en place cette configuration.