Parce qu'Hibernate est conçu pour fonctionner dans différents environnements, il existe beaucoup de paramètres de configuration. Heureusement, la plupart ont des valeurs par défaut appropriées et la distribution d'Hibernate contient un exemple de fichier hibernate.properties qui montre les différentes options. Généralement, vous n'avez qu'à placer ce fichier dans votre classpath et à l'adapter.
Une instance de net.sf.hibernate.cfg.Configuration représente un ensemble de mappings des classes Java d'une application vers la base de données SQL. La Configuration est utilisée pour construire un objet (immuable) SessionFactory. Les mappings sont constitués d'un ensemble de fichiers de mapping XML.
Vous pouvez obtenir une instance de Configuration en l'instanciant directement. Voici un exemple de configuration d'une source de données et d'un mapping composé de deux fichiers de configuration XML (qui se trouvent dans le classpath) :
Configuration cfg = new Configuration() .addFile("Item.hbm.xml") .addFile("Bid.hbm.xml");
Une alternative (parfois meilleure) est de laisser Hibernate charger le fichier de mapping en utilisant getResourceAsStream() :
Configuration cfg = new Configuration() .addClass(org.hibernate.auction.Item.class) .addClass(org.hibernate.auction.Bid.class);
Hibernate va rechercher les fichiers de mappings /org/hibernate/auction/Item.hbm.xml et /org/hibernate/auction/Bid.hbm.xml dans le classpath. Cette approche élimine les noms de fichiers en dur.
Une Configuration permet également plusieurs valeurs optionnelles :
Properties props = new Properties(); ... Configuration cfg = new Configuration() .addClass(org.hibernate.auction.Item.class) .addClass(org.hibernate.auction.Bid.class) .setProperties(props);
Une Configuration est sensée être un objet nécessaire pendant la phase de configuration et être libérée une fois la SessionFactory construite.
Quand tous les mappings ont été parsés par la Configuration, l'application doit obtenir une fabrique d'instances de Session. Cette fabrique est supposée être partagée par tous les threads de l'application :
SessionFactory sessions = cfg.buildSessionFactory();
Cependant, Hibernate permet à votre application d'instancier plus d'une SessionFactory. C'est utile si vous utilisez plus d'une base de données.
Une SessionFactory peut ouvrir une Session en utilisant une connexion JDBC fournie par l'utilisateur. Ce choix de design permet à l'application d'obtenir les connexions JDBC de la façon qu'il lui plait :
java.sql.Connection conn = datasource.getConnection(); Session session = sessions.openSession(conn); // do some data access work
L'application doit faire attention à ne pas ouvrir deux Sessions concurrentes en utilisant la même connexion !
Alternativement, vous pouvez laisser la SessionFactory ouvrir les connexions pour vous. La SessionFactory doit recevoir les propriétés de connexions JDBC de l'une des manières suivantes :
Passer une instance de java.util.Properties à Configuration.setProperties().
Placer hibernate.properties dans un répertoire racine du classpath
Positionner les propriétés System en utilisant java -Dproperty=value.
Inclure des éléments <property> dans le fichier hibernate.cfg.xml (voir plus loin).
Si vous suivez cette approche, ouvrir une Session est aussi simple que :
Session session = sessions.openSession(); // ouvre une nouvelle session // faire quelques accès aux données, une connexion JDBC sera utilisée à la demande
Tous les noms et sémantiques des propriétés d'Hibernate sont définies dans la javadoc de la classe net.sf.hibernate.cfg.Environment. Nous allons décrire les paramètres les plus importants pour une connexion JDBC.
Hibernate obtiendra des connexions (et les mettra dans un pool) en utilisant java.sql.DriverManager si vous positionner les paramètres de la manière suivante :
Tableau 3.1. Propriétés JDBC d'Hibernate
Nom de la propriété | Fonction |
---|---|
hibernate.connection.driver_class | Classe du driver jdbc |
hibernate.connection.url | URL jdbc |
hibernate.connection.username | utilisateur de la base de données |
hibernate.connection.password | mot de passe de la base de données |
hibernate.connection.pool_size | nombre maximum de connexions dans le pool |
L'algorithme natif de pool de connexions d'Hibernate est plutôt rudimentaire. Il a été fait dans le but de vous aider à démarrer et n'est pas prévu pour un système en production ou même pour un test de peformance. Utiliser un pool tiers pour de meilleures performances et une meilleure stabilité : remplacer la propriété hibernate.connection.pool_size avec les propriétés spécifique au pool de connexions que vous avez choisi.
C3P0 est un pool de connexions JDBC open source distribué avec Hibernate dans le répertoire lib. Hibernate utilisera le provider intégré C3P0ConnectionProvider pour le pool de connexions si vous positionnez les propriétés hibernate.c3p0.*. Il y a également un support intégré pour Apache DBCP et Proxool. Vous devez positionner les propriétés hibernate.dbcp.* (propriétés du pool de connexions DBCP) pour activer le DBCPConnectionProvider. Le cache des Prepared Statement est activé (fortement recommandé) si hibernate.dbcp.ps.* (propriétés du cache de statement de DBCP) sont positionnées. Merci de vous référer à la documentation de apache commons-pool pour l'utilisation et la compréhension de ces propriétés. Vous devez positionner les propriétés hibernate.proxool.* si vous voulez utiliser Proxool.
Voici un exemple utilisant C3P0:
hibernate.connection.driver_class = org.postgresql.Driver hibernate.connection.url = jdbc:postgresql://localhost/mydatabase hibernate.connection.username = myuser hibernate.connection.password = secret hibernate.c3p0.min_size=5 hibernate.c3p0.max_size=20 hibernate.c3p0.timeout=1800 hibernate.c3p0.max_statement=50 hibernate.dialect = net.sf.hibernate.dialect.PostgreSQLDialect
Dans le cadre de l'utilisation au sein d'un serveur d'applications, Hibernate peut obtenir les connexions à partir d'une javax.sql.Datasource enregistrée dans le JNDI. Positionner les propriétés suivantes :
Tableau 3.2. Propriété d'une Datasource Hibernate
Nom d'une propriété | fonction |
---|---|
hibernate.connection.datasource | Nom JNDI de la datasource |
hibernate.jndi.url | URL du fournisseur JNDI (optionnelle) |
hibernate.jndi.class | Classe de l'InitialContextFactory du JNDI (optionnelle) |
hibernate.connection.username | utilisateur de la base de données (optionnelle) |
hibernate.connection.password | mot de passe de la base de données (optionnelle) |
voici un exemple utilisant les datasources JNDI fournies par un serveur d'applications :
hibernate.connection.datasource = java:/comp/env/jdbc/MyDB hibernate.transaction.factory_class = \ net.sf.hibernate.transaction.JTATransactionFactory hibernate.transaction.manager_lookup_class = \ net.sf.hibernate.transaction.JBossTransactionManagerLookup hibernate.dialect = \ net.sf.hibernate.dialect.PostgreSQLDialect
Les connexions JDBC obtenues à partir d'une datasource JNDI participeront automatiquement aux transactions gérées par le conteneur du serveur d'applications.
Des propriétés supplémentaires de connexion peuvent être passées en préfixant le nom de la propriété par "hibernate.connnection". Par exemple, vous pouvez spécifier un jeu de caractères en utilisant hibernate.connnection.charSet.
Vous pouvez fournir votre propre stratégie d'obtention des connexions JDBC en implémentant l'interface net.sf.hibernate.connection.ConnectionProvider. Vous pouvez sélectionner une implémentation spécifique en positionnant hibernate.connection.provider_class.
Il y a un certain nombre d'autres propriétés qui contrôlent le fonctionnement d'Hibernate à l'exécution. Toutes sont optionnelles et ont comme valeurs par défaut des valeurs "raisonnables" pour un fonctionnement nominal.
Les propriétés de niveau System ne peuvent être positionnées que via la ligne de commande (java -Dproperty=value) ou être définies dans hibernate.properties. Elle ne peuvent l'être dans une instance de Properties passée à la Configuration.
Tableau 3.3. Propriétés de configuration d'Hibernate
Nom de la propriété | Fonction |
---|---|
hibernate.dialect |
Le nom de la classe du Dialect Hibernate - active
l'utilisation de certaines fonctionalités spécifiques à la plateforme.
ex. nom.complet.de.ma.classe.de.Dialect |
hibernate.default_schema |
Positionne dans le SQL généré un schéma/tablespace par défaut pour les noms de
table ne l'ayant pas surchargé.
ex. MON_SCHEMA |
hibernate.session_factory_name |
La SessionFactory sera automatiquement
liée à ce nom dans le JNDI après sa création.
ex. jndi/nom/hierarchique |
hibernate.use_outer_join |
Active le chargement via les jointures ouvertes. Dépréciée, utiliser max_fetch_depth.
ex. true | false |
hibernate.max_fetch_depth |
Définit la profondeur maximale d'un arbre de chargement par
jointures ouvertes pour les associations à cardinalité unitaire
(un-à-un, plusieurs-à-un).
Un 0 désactive le chargement par jointure
ouverte.
ex. valeurs recommandées entre 0 et 3 |
hibernate.jdbc.fetch_size | Une valeur non nulle détermine la taille de chargement des statements JDBC (appelle Statement.setFetchSize()). |
hibernate.jdbc.batch_size |
Une valeur non nulle active l'utilisation par Hibernate des mises
à jour par batch de JDBC2.
ex. les valeurs recommandées entre 5 et 30 |
hibernate.jdbc.batch_versioned_data |
Paramétrez cette propriété à true si votre pilote JDBC
retourne des row counts corrects depuis executeBatch() (il est
souvent approprié d'activer cette option). Hibernate utilisera alors le "batched DML" pour
versionner automatiquement les données. Par défaut = false.
eg. true | false |
hibernate.jdbc.use_scrollable_resultset |
Active l'utilisation par Hibernate des resultsets scrollables
de JDBC2. Cette propriété est seulement nécessaire lorsque l'on
utilise une connexion JDBC fournie par l'utilisateur. Autrement,
Hibernate utilise les métadonnées de la connexion.
ex. true | false |
hibernate.jdbc.use_streams_for_binary |
Utilise des flux lorsque l'on écrit/lit des types
binary ou serializable
vers et à partir de JDBC (propriété de niveau système).
ex. true | false |
hibernate.jdbc.use_get_generated_keys |
Active l'utilisation de PreparedStatement.getGeneratedKeys() de JDBC3
pour récupérer nativement les clés générées après insertion. Nécessite un pilote
JDBC3+, le mettre à false si votre pilote a des problèmes avec les générateurs
d'identifiant Hibernate. Par défaut, essaie de déterminer les possibilités du
pilote en utilisant les meta données de connexion.
eg. true|false |
hibernate.cglib.use_reflection_optimizer |
Active l'utilisation de CGLIB à la place de la réflexion à l'exécution
(Propriété de niveau système, la valeur par défaut étant d'utiliser CGLIB
lorsque c'est possible). La réflexion est parfois utile en cas de problème.
ex. true | false |
hibernate.jndi.<propertyName> | Passe la propriété propertyName au JNDI InitialContextFactory. |
hibernate.connection.isolation |
Positionne le niveau de transaction JDBC. Merci de vous référer
à java.sql.Connection pour le détail des valeurs
mais sachez que toutes les bases de données ne supportent pas
tous les niveaux d'isolation.
ex. 1, 2, 4, 8 |
hibernate.connection.<propertyName> | Passe la propriété JDBC propertyName au DriverManager.getConnection(). |
hibernate.connection.provider_class |
Le nom de classe d'un ConnectionProvider
spécifique.
ex. nom.de.classe.du.ConnectionProvider |
hibernate.cache.provider_class |
Le nom de classe d'un CacheProvider
spécifique.
ex. nom.de.classe.du.CacheProvider |
hibernate.cache.use_minimal_puts |
Optimise le cache de second niveau en minimisant les écritures,
au prix de plus de lectures (utile pour les caches en cluster).
ex. true|false |
hibernate.cache.use_query_cache |
Activer le cache de requête, les requêtes individuelles doivent tout
de même être déclarées comme mettable en cache.
ex. true|false |
hibernate.cache.query_cache_factory |
Le nom de classe d'une interface QueryCache ,
par défaut = built-in StandardQueryCache.
eg. nom.de.la.classe.de.QueryCache |
hibernate.cache.region_prefix |
Un préfixe à utiliser pour le nom des régions du
cache de second niveau.
ex. prefix |
hibernate.transaction.factory_class |
Le nom de classe d'une TransactionFactory
qui sera utilisée par l'API Transaction
d'Hibernate (la valeur par défaut est
JDBCTransactionFactory).
ex. nom.de.classe.d.une.TransactionFactory |
jta.UserTransaction |
Le nom JNDI utilisé par la JTATransactionFactory
pour obtenir la UserTransaction JTA du serveur
d'applications.
eg. jndi/nom/compose |
hibernate.transaction.manager_lookup_class |
Le nom de la classe du TransactionManagerLookup
- requis lorsque le cache de niveau JVM est activé dans un
environnement JTA.
ex. nom.de.classe.du.TransactionManagerLookup |
hibernate.query.substitutions |
Lien entre les tokens de requêtes Hibernate et les
tokens SQL (les tokens peuvent être des fonctions ou des
noms littéraux par exemple).
ex. hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC |
hibernate.show_sql |
Ecrit les ordres SQL dans la console.
ex. true | false |
hibernate.hbm2ddl.auto |
Exporte le schéma DDL vers la base de données automatiquement
lorsque la SessionFactory est créée.
La valeur create-drop permet de supprimer
le schéma de base de données lorsque la SessionFactory
est fermée explicitement.
ex. update | create | create-drop |
Vous devriez toujours positionner la propriété hibernate.dialect à la sous-classe appropriée à votre base de données. Ce n'est pas strictement obligatoire à moins de vouloir utiliser la génération de clé primaire native ou par sequence ou de vouloir utiliser le mécanisme de lock pessimiste (ex. via Session.lock() ou Query.setLockMode()). Cependant, si vous spécifiez un dialecte, Hibernate utilisera des valeurs adaptées pour certaines autres propriétés listées ci-dessus, vous évitant l'effort de le faire à la main.
Tableau 3.4. Dialectes SQL d'Hibernate (hibernate.dialect)
SGBD | Dialecte |
---|---|
DB2 | net.sf.hibernate.dialect.DB2Dialect |
DB2 AS/400 | net.sf.hibernate.dialect.DB2400Dialect |
DB2 OS390 | net.sf.hibernate.dialect.DB2390Dialect |
PostgreSQL | net.sf.hibernate.dialect.PostgreSQLDialect |
MySQL | net.sf.hibernate.dialect.MySQLDialect |
SAP DB | net.sf.hibernate.dialect.SAPDBDialect |
Oracle (toutes versions) | net.sf.hibernate.dialect.OracleDialect |
Oracle 9/10g | net.sf.hibernate.dialect.Oracle9Dialect |
Sybase | net.sf.hibernate.dialect.SybaseDialect |
Sybase Anywhere | net.sf.hibernate.dialect.SybaseAnywhereDialect |
Microsoft SQL Server | net.sf.hibernate.dialect.SQLServerDialect |
SAP DB | net.sf.hibernate.dialect.SAPDBDialect |
Informix | net.sf.hibernate.dialect.InformixDialect |
HypersonicSQL | net.sf.hibernate.dialect.HSQLDialect |
Ingres | net.sf.hibernate.dialect.IngresDialect |
Progress | net.sf.hibernate.dialect.ProgressDialect |
Mckoi SQL | net.sf.hibernate.dialect.MckoiDialect |
Interbase | net.sf.hibernate.dialect.InterbaseDialect |
Pointbase | net.sf.hibernate.dialect.PointbaseDialect |
FrontBase | net.sf.hibernate.dialect.FrontbaseDialect |
Firebird | net.sf.hibernate.dialect.FirebirdDialect |
Si votre base de données supporte les outer joins de type ANSI ou Oracle, le chargement par jointure ouverte devrait améliorer les performances en limitant le nombre d'aller-retour avec la base de données (la base de données effectuant donc potentiellement plus de travail). Le chargement par jointure ouverte permet à un graphe connecté d'objets par une relation plusieurs-à-un, un-à-plusieurs ou un-à-un d'être chargé en un seul SELECT SQL.
Par défaut, le graphe chargé lorsqu'un objet est demandé, finit aux objets feuilles, aux collections, aux objets avec proxy ou lorsqu'une circularité apparaît.
Le chargement peut être activé ou désactivé (valeur par défaut) pour une association particulière, en positionant l'attribut outer-join dans le mapping XML.
Le chargement par jointure ouverte peut être désactivé de manière globale en positionant la propriété hibernate.max_fetch_depth à 0. Une valeur de 1 ou plus permet les jointures ouvertes pour toutes les associations un-à-un et plusieurs-à-un qui sont, par défaut, positionnées à la valeur de jointure outerte auto. Cependant, les associations un-à-plusieurs et les collections ne sont jamais chargées en utilisant une jonture ouverte, à moins de le déclarer de façon explicite pour chaque association. Cette fonctionalité peut être surchargée à l'exécution dans les requêtes Hibernate.
Oracle limite la taille d'un tableau de byte qui peuvent être passées à et vers son pilote JDBC. Si vous souhaitez utiliser des instances larges de type binary ou serializable, vous devez activer la propriété hibernate.jdbc.use_streams_for_binary. C'est une fonctionalité de niveau JVM uniquement.
Vous pouvez intégrer un cache de second niveau de type JVM (ou cluster) en implémentant l'interface net.sf.hibernate.cache.CacheProvider. Vous pouvez sélectionner l'implémentation spécifique en positionnant hibernate.cache.provider_class.
Si vous souhaitez utiliser l'API d'Hibernate Transaction, vous devez spécifier une classe factory d'instances de Transaction en positionnant la propriété hibernate.transaction.factory_class. L'API Transaction masque le mécanisme de transaction sous-jacent et permet au code utilisant Hibernate de tourner dans des environnements managés et non-managés sans le moindre changement.
Il existe deux choix standards (fournis) :
délègue aux transactions de la base de données (JDBC). Valeur par défaut.
délègue à JTA (si une transaction existant est en cours, la Session exécute son travail dans ce contexte ; sinon, une nouvelle transaction est démarrée).
Vous pouvez également définir votre propre stratégie transactionnelle (pour un service de transaction CORBA par exemple).
Si vous voulez utiliser un cache de niveau JVM pour des données muables dans un environnement JTA, vous devez spécifier une stratégie d'obtention du TransactionManager JTA. En effet, cet accès n'est pas standardisé par la norme J2EE :
Tableau 3.5. TransactionManagers JTA
Factory de Transaction | Serveur d'application |
---|---|
net.sf.hibernate.transaction.JBossTransactionManagerLookup | JBoss |
net.sf.hibernate.transaction.WeblogicTransactionManagerLookup | Weblogic |
net.sf.hibernate.transaction.WebSphereTransactionManagerLookup | WebSphere |
net.sf.hibernate.transaction.OrionTransactionManagerLookup | Orion |
net.sf.hibernate.transaction.ResinTransactionManagerLookup | Resin |
net.sf.hibernate.transaction.JOTMTransactionManagerLookup | JOTM |
net.sf.hibernate.transaction.JOnASTransactionManagerLookup | JOnAS |
net.sf.hibernate.transaction.JRun4TransactionManagerLookup | JRun4 |
net.sf.hibernate.transaction.BESTransactionManagerLookup | Borland ES |
Une SessionFactory Hibernate associée au JNDI peut simplifier l'accès à la fabrique et donc la création de nouvelles Sessions.
Si vous désirez associer la SessionFactory à un nom JNDI, spécifiez un nom (ex. java:comp/env/hibernate/SessionFactory) en utilisant la propriété hibernate.session_factory_name. Si cette propriété est omise, la SessionFactory ne sera pas associée au JNDI (c'est particulièrement pratique dans les environnements ayant une implémentation de JNDI en lecture seule, comme c'est le cas pour Tomcat).
Lorsqu'il associe la SessionFactory au JNDI, Hibernate utilisera les valeurs de hibernate.jndi.url, hibernate.jndi.class pour instancier un contexte d'initialisation. S'ils ne sont pas spécifiés, l'InitialContext par défaut sera utilisé.
Si vous décidez d'utiliser JNDI, un EJB ou toute autre classe utilitaire pourra obtenir la SessionFactory en faisant un accès au JNDI.
Vous pouvez définir de nouveaux tokens dans les requêtes Hibernate en utilisant la propriété hibernate.query.substitutions. Par exemple :
hibernate.query.substitutions vrai=1, faux=0
remplacerait les tokens vrai et faux par des entiers dans le SQL généré.
hibernate.query.substitutions toLowercase=LOWER
permettrait de renommer la fonction SQL LOWER en toLowercase
Hibernate loggue divers évènements en utilisant Apache commons-logging.
Le service commons-logging délèguera directement à Apache Log4j (si vous incluez log4j.jar dans votre classpath) ou le système de log du JDK 1.4 (si vous tournez sous le JDK 1.4 et supérieur). Vous pouvez télécharger Log4j à partir de http://jakarta.apache.org. Pour utiliser Log4j, vous devrez placer dans votre classpath un fichier log4j.properties. Un exemple de fichier est distribué avec Hibernate dans le répertoire src/.
Nous vous recommandons fortement de vous familiariser avec les messages de logs d'Hibernate. Beaucoup de soins a été apporté pour donner le plus de détails possibles sans les rendre illisibles. C'est un outil essentiel en cas de soucis. De même, n'oubliez pas d'activer les logs SQL comme décrit précédemment hibernate.show_sql, c'est la première étape pour regarder les problèmes de performance.
L'interface net.sf.hibernate.cfg.NamingStrategy vous permet de spécifier une "stratégie de nommage" des objets et éléments de la base de données.
Vous pouvez fournir des règles pour automatiquement générer les identifiants de base de données à partir des identifiants Java, ou transformer une colonne ou table "logique" donnée dans le fichier de mapping en une colonne ou table "physique". Cette fonctionnalité aide à réduire la verbosité de documents de mapping, en éliminant le bruit répétitif (les préfixes TBL_ par exemple). La stratégie par défaut utilisée par Hibernate est minimale.
Vous pouvez définir une stratégie différente en appelant Configuration.setNamingStrategy() avant d'ajouter des mappings :
SessionFactory sf = new Configuration() .setNamingStrategy(ImprovedNamingStrategy.INSTANCE) .addFile("Item.hbm.xml") .addFile("Bid.hbm.xml") .buildSessionFactory();
net.sf.hibernate.cfg.ImprovedNamingStrategy est une stratégie fournie qui peut être utile comme point de départ de quelques applications.
Une approche alternative est de spécifier toute la configuration dans un fichier nommé hibernate.cfg.xml. Ce fichier peut être utilisé à la place du fichier hibernate.properties, voire même peut servir à surcharger les propriétés si les deux fichiers sont présents.
Le fichier de configuration XML doit par défaut se placer à la racine du CLASSPATH. En voici un exemple :
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd"> <hibernate-configuration> <!-- une instance de SessionFactory accessible par son nom jndi --> <session-factory name="java:comp/env/hibernate/SessionFactory"> <!-- propriétés --> <property name="connection.datasource">ma/premiere/datasource</property> <property name="dialect">net.sf.hibernate.dialect.MySQLDialect</property> <property name="show_sql">false</property> <property name="use_outer_join">true</property> <property name="transaction.factory_class"> net.sf.hibernate.transaction.JTATransactionFactory </property> <property name="jta.UserTransaction">java:comp/UserTransaction</property> <!-- mapping files --> <mapping resource="org/hibernate/auction/Item.hbm.xml"/> <mapping resource="org/hibernate/auction/Bid.hbm.xml"/> </session-factory> </hibernate-configuration>
Configurer Hibernate devient donc aussi simple que ceci :
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Vous pouvez utiliser une fichier de configuration XML de nom différent en utilisant
SessionFactory sf = new Configuration() .configure("/my/package/catdb.cfg.xml") .buildSessionFactory();