Poiché Hibernate è progettato per funzionare in molti ambienti differenti, ci sono un gran numero di parametri di configurazione. Fortunatamente, la maggior parte hanno dei valori predefiniti, e Hibernate viene distribuito con un file hibernate.properties di esempio che mostra le differenti opzioni possibili. Solitamente è sufficiente mettere quel file nel classpath e applicare le modifiche necessarie per il proprio ambiente.
Una istanza di net.sf.hibernate.cfg.Configuration rappresenta un insieme completo di mappaggi dei tipi java di una applicazione verso un database SQL. La Configuration viene usata per costruire un oggetto SessionFactory (immutabile). I mappaggi vengono compilati dai vari file di configurazione XML.
Potete ottenere una istanza di Configuration istanziandola direttamnete. Qui di seguito c'è un esempio di impostazione di un contenitore di dati a partire da dei mappaggi definiti in due file di configurazione XML (che si trovano sul classpath):
Configuration cfg = new Configuration() .addFile("Item.hbm.xml") .addFile("Bid.hbm.xml");
Una maniera alternativa (e in certi casi migliore), è di fare in modo che Hibernate carichi un file di mappaggio usando getResourceAsStream():
Configuration cfg = new Configuration() .addClass(org.hibernate.auction.Item.class) .addClass(org.hibernate.auction.Bid.class);
In questo caso Hibernate cercherà file di mappaggio che si chiamano /org/hibernate/autcion/Item.hbm.xml e /org/hibernate/autcion/Bid.hbm.xml sul classpath. Questo approccio elimina qualsiasi nome di file cablato nel codice.
Un oggetto Configuration specifica anche alcune proprietà opzionali:
Properties props = new Properties(); ... Configuration cfg = new Configuration() .addClass(org.hibernate.auction.Item.class) .addClass(org.hibernate.auction.Bid.class) .setProperties(props);
Una Configuration viene considerata un oggetto "da fase di configurazione", che va cioè scartato una volta che una SessionFactory sia stata costruita.
Quando tutti i mappaggi sono stati interpretati dalla Configuration, l'applicazione deve costruire una factory per le istanze di Session instances. Questa factory è fatta in modo tale da essere condivisa da tutti i flussi esecutivi (thread) dell'applicazione:
SessionFactory sessions = cfg.buildSessionFactory();
Comunque, Hibernate consente alla vostra applicazione di istanziare più di una SessionFactory. Questo è utile in particolare se state usando più di un database.
Una SessionFactory può aprire una Session su una connessione JDBC fornita dall'utente. Quesa scelta di design dà la libertà all'applicazione di ottenere le connessioni JDBC in qualunque modo preferisca:
java.sql.Connection conn = datasource.getConnection(); Session session = sessions.openSession(conn); // do some data access work
L'applicazione deve essere molto attenta a non aprire due Sessioni concorrenti sulla stessa connessione JDBC!
In alternativa potete fare in modo che la SessionFactory apra le connessioni per voi. La SessionFactory deve ricevere le proprietà per le connessioni JDBC in una delle maniere seguenti:
Passate una istanza di java.util.Properties al metodo Configuration.setProperties().
Mettete il file hibernate.properties in una directory che si trovi alla radice del classpath.
Impostate le proprietà System usando java -Dproperty=value all'avvio.
Includete elementi <property> nel file hibernate.cfg.xml.
Se seguite questo approccio, aprire una Session non è più difficile di così:
Session session = sessions.openSession(); // apre una nuova sessione // fate del lavoro di accesso ai dati, una connessione JDBC sarà usata se ce ne sarà bisogno
Tutti i nomi e le semantiche delle proprietà di Hibernate sono definiti nella classe net.sf.hibernate.cfg.Environment. Ora descriveremo le impostazioni più importanti per la configurazione delle connessioni JDBC.
Hibernate otterrà le connessioni usando java.sql.DriverManager (e le manterrà in un lotto) se impostate le proprietà seguenti:
Tabella 3.1. Proprietà JDBC di Hibernate
Nome della proprietà | Scopo |
---|---|
hibernate.connection.driver_class | classe del driver jdbc |
hibernate.connection.url | URL jdbc |
hibernate.connection.username | nome utente database |
hibernate.connection.password | chiave di accesso al database per l'utente |
hibernate.connection.pool_size | numero massimo di connessioni nel lotto |
L'algoritmo di "pooling" (mantenimento nel lotto) di Hibernate è abbastanza rudimentale. Ha lo scopo di aiutarvi a cominciare a lavorare, ma non è fatto per l'uso in un sistema in produzione, o anche solo per dei test di performance. Usate un'altra libreria di pooling per le migliori performance e la stabilità, ovvero sostituite la proprietà hibernate.connection.pool_size con le proprietà specifiche per il settaggio del pool.
C3P0 è una libreria open source di pooling per connessioni JDBC che viene distribuita insieme ad Hibernate nella directory lib. Hibernate userà il C3P0ConnectionProvider integrato per il pooling delle connessioni se settate le proprietà hibernate.c3p0.*. C'è anche un supporto integrato per Apache DBCP e per Proxool. Dovete in questo caso impostare le proprietà hibernate.dbcp.* per abilitare il DBCPConnectionProvider. Il caching dei prepared statement è abilitato se sono impostate le porprietà hibernate.dbcp.ps.* (caldamente consigliato). Riferitevi alla documentazione di Apache commons-pool per l'interpretazione di queste proprietà. Se invece volete usare Proxool, settate le proprietà hibernate.proxool.*.
Questo è un esempio usando 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.minPoolSize=5 hibernate.c3p0.maxPoolSize=20 hibernate.c3p0.timeout=1800 hibernate.c3p0.max_statement=50 hibernate.dialect = net.sf.hibernate.dialect.PostgreSQLDialect
Per l'uso all'interno di un application server, Hibernate può ottenere connessioni da un javax.sql.Datasource registrato nel JNDI. Impostate per questo le proprietà seguenti:
Tabella 3.2. Hibernate Datasource Properties
Nome della proprietà | Scopo |
---|---|
hibernate.connection.datasource | Nome JNDI del datasource |
hibernate.jndi.url | URL del provider JNDI (optional) |
hibernate.jndi.class | classe dell'InitialContextFactory JNDI (optional) |
hibernate.connection.username | utente del database (opzionale) |
hibernate.connection.password | chiave di accesso al database per l'utente (opzionale) |
Questo è un esempio usando un datasource fornito dal JNDI dell'application server:
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
Connessioni JDBC ottenute da un datasource JNDI parteciperanno automaticamete alle transazioni gestite dal container dell'application server.
Altre proprietà per la connessione possono venire impostate facendo precedere "hibernate.connnection" al nome della proprietà. Ad esempio, potete specificare un charSet usando hibernate.connnection.charSet.
Potete definire la vostra strategia "plugin" per ottenere le connessioni JDBC implementando l'interfaccia net.sf.hibernate.connection.ConnectionProvider. Potete selezionare una implementazionee custom impostando hibernate.connection.provider_class.
C'è un certo numero di altre proprietà che controllano il funzionamento in fase di esecuzione di Hibernate. Sono tutte opzionali, e hanno dei valori predefiniti ragionevoli.
Le proprietà a livello di sistema possono essere impostate eslusivamente tramite java -Dproperty=value o essere definite in hibernate.properties e non con una istanza di Properties passata alla classe Configuration.
Tabella 3.3. Proprietà per la configurazione di Hibernate
Nome della proprietà | Scopo |
---|---|
hibernate.dialect |
Il nome della classe di un Dialect di Hibernate -
attiva alcune funzionalità dipendenti dalla piattaforma di database.
e.g. nome.completo.del.Dialect |
hibernate.default_schema |
Specificate il nome dello schema/tablespace nei nomi delle tabelle
nell'SQL che viene generato.
e.g. NOME_DELLO_SCHEMA |
hibernate.session_factory_name |
Il SessionFactory verrà automaticamente pubblicato
sul JNDI sotto questo nome, se è stato specificato.
e.g. nome/composito/jndi |
hibernate.use_outer_join |
Attiva il reperimento via join esterno. Deprecato, usate max_fetch_depth.
e.g. true | false |
hibernate.max_fetch_depth |
Imposta una "profondità" massima per l'albero di join esterno
che risolve le associazioni ad una singola estremità (uno-a-uno,
molti-a-uno). Uno 0 disabilita la risoluzione
via join esterno (che invece è attivata per default).
e.g. i valori raccomandati sono tra 0 e 3 |
hibernate.jdbc.fetch_size | Un valore non nullo determina le dimensioni di raccolta del JDBC (chiama Statement.setFetchSize()). |
hibernate.jdbc.batch_size |
Un valore non nullo abilita l'uso dei "batch update" di JDBC 2
da parte di Hibernate (aggiornamenti in blocco).
e.g. I valori raccomandati sono tra 5 e 30 |
hibernate.jdbc.use_scrollable_resultset |
Consente l'uso di resultset scrollabili JDBC2 da parte di
Hibernate. Questa proprietà è necessaria solo quando
vengono usate connessioni JDBC fornite dall'utente: Hibernate
usa i metadati di connessione, altrimenti.
e.g. true | false |
hibernate.jdbc.use_streams_for_binary |
Se abilitata, Hibernate usa gli stream quando scrive/legge tipi binary
o serializable da/a JDBC
(proprietà di livello di sistema).
e.g. true | false |
hibernate.cglib.use_reflection_optimizer |
Abilita l'uso di CGLIB invece di "reflection" in fase di esecuzione
(è una proprietà a livello di sistema, il default è usare CGLIB
quando possibile). La "reflection" può a volte essere usata in
fase di risoluzione dei problemi.
e.g. true | false |
hibernate.jndi.<propertyName> | Passa la proprietà propertyName all' InitialContextFactory JNDI. |
hibernate.connection.isolation |
Setta il livello di isolamento transazionale JDBC. Consultate
la documentazione di java.sql.Connection per
ottenere i valori significativi, ma tenete presente che la maggior
parte dei database non supportano tutti i livelli di isolamento.
eg. 1, 2, 4, 8 |
hibernate.connection.<propertyName> | Passa il nome di proprietà JDBC propertyName a DriverManager.getConnection(). |
hibernate.connection.provider_class |
Il nome della classe di un ConnectionProvider
definito dall'utente.
e.g. nomediclasse.del.ConnectionProvider |
hibernate.cache.provider_class |
Il nome di classe di un CacheProvider
fornito dall'utente.
eg. nomediclasse.del.CacheProvider |
hibernate.cache.use_minimal_puts |
Ottimizza le operazioni della cache di secondo livello in modo
tale da minimizzare le scritture, al costo di letture più frequenti
(usato per cache in cluster).
e.g. true|false |
hibernate.cache.use_query_cache |
Attiva il caching delle interrogazioni, le query singole vanno comunque
impostate come "cacheabili".
e.g. true|false |
hibernate.cache.region_prefix |
Il prefisso da usare per i nomi delle regioni della cache
di secondo livello.
eg. prefisso |
hibernate.transaction.factory_class |
Il nome di classe di una TransactionFactory
da usare con l'API Transaction di Hibernate
(il valore predefinito è JDBCTransactionFactory).
eg. nomediclasse.della.TransactionFactory |
jta.UserTransaction |
Il nome di classe usato da JTATransactionFactory per
ottenere la UserTransaction JTA dall'application
server.
eg. nome/composito/jndi |
hibernate.transaction.manager_lookup_class |
Il nome di classe di un TransactionManagerLookup
- richiesto quando il caching a livello di JVM è abilitato in un contesto JTA.
eg. nomediclasse.del.TransactionManagerLookup |
hibernate.query.substitutions |
Mappaggio da alcune etichette nelle query di Hibernate
a dei valori di sostituzione per le query SQL
(potrebbero essere funzioni o letterali, ad esempio).
e.g. hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC |
hibernate.show_sql |
Scrive tutte le istruzioni SQL sulla console.
e.g. true | false |
hibernate.hbm2ddl.auto |
Esporta automaticamente lo schema DDL sul database quando
viene creata la SessionFactory. Con
create-drop, lo schema di database
verrà eliminato subito dopo che la SessionFactory
verrà chiusa esplicitamente.
e.g. update | create | create-drop |
Dovreste sempre impostare la proprietà hibernate.dialect al valore della sottoclasse di net.sf.hibernate.dialect.Dialect giusta per il vostro database. Questo non è strettamente necessario, a meno che desideriate usare la generazione di chiavi primaria native o sequence o il locking pessimistico (con, ad esempio, Session.lock() o Query.setLockMode()). Comunque, se specificate un dialetto, Hibernate imposterà dei default adatti per alcune delle proprietà elencate in precedenza, risparmiandovi lo sforzo di specificarle manualmente.
Tabella 3.4. Dialetti SQL di HIbernate (hibernate.dialect)
RDBMS | Dialetto |
---|---|
DB2 | net.sf.hibernate.dialect.DB2Dialect |
MySQL | net.sf.hibernate.dialect.MySQLDialect |
SAP DB | net.sf.hibernate.dialect.SAPDBDialect |
Oracle (any version) | net.sf.hibernate.dialect.OracleDialect |
Oracle 9 | net.sf.hibernate.dialect.Oracle9Dialect |
Sybase | net.sf.hibernate.dialect.SybaseDialect |
Sybase Anywhere | net.sf.hibernate.dialect.SybaseAnywhereDialect |
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 |
PostgreSQL | net.sf.hibernate.dialect.PostgreSQLDialect |
HypersonicSQL | net.sf.hibernate.dialect.HSQLDialect |
Microsoft SQL Server | net.sf.hibernate.dialect.SQLServerDialect |
Ingres | net.sf.hibernate.dialect.IngresDialect |
Informix | net.sf.hibernate.dialect.InformixDialect |
FrontBase | net.sf.hibernate.dialect.FrontbaseDialect |
Se il vostro database supporta i join esterni ANSI o Oracle, il reperimento via join esterno può aumentare le performance limitando il numero di accessi al database (al costo di un lavoro maggiore probabilmente effettuato dal database stesso). Il reperimento via join esterno consente ad un grafo di oggetti connessi da associazioni molti-a-uno, uno-a-molti o uno-a-uno di essere caricati in una singola SELECT SQL.
Per default, il grafo inizializzato quando si carica un oggetto termina agli oggetti foglia, alle collezioni, agli oggetti con mediatori (proxy) o dove ci siano delle circolarità.
Per una associazione particolare, il caricamento può essere abilitato o disabilitato (e quindi si può modificare il comportamento predefinito) impostando l'attributo outer-join nel mapping XML.
Il caricamento via join esterno può essere disabilitato globalmente impostando la proprietà hibernate.max_fetch_depth a 0. Un settaggio di 1 o più abilita il join esterno per tutte le associazioni uno-a-uno e molti-a-uno che sono, sempre come impostazione predefinita, impostate ad auto. In ogni caso, le associazioni uno-a-molti e le collezioni non vengono mai caricate con un join esterno, a meno che questo non venga esplicitamente dichiarato per ogni particolare associazione. Anche questo comportamento può essere modificato a runtime con delle query di Hibernate.
Oracle limita la dimensione degli array di byte che possono essere passati da o al suo driver JDBC. Se volete usare istanze di tipi binari o serializzabili di grandi dimensioni, dovete abilitare hibernate.jdbc.use_streams_for_binary. Questa impostazione viene settata esclusivamente a livello di JVM.
Potete integrare una cache di secondo livello che operi all'interno della virtual machine (JVM-level) o sull'intero cluster (clustered) implementando l'interfaccia net.sf.hibernate.cache.CacheProvider. Potete poi impostare l'implementazione personalizzata con l'opzione hibernate.cache.provider_class.
Se volete usare l'API Transaction di Hibernate, dovete specificare una classe factory ("fabbricatore" di istanze) per gli oggetti Transaction impostando la proprietà hibernate.transaction.factory_class. L'API Transaction maschera il meccanismo transazionale sottostante e consente al codice di Hibernate di eseguirsi in contesti gestiti dal container (managed) e non.
Ci sono due scelte possibili (pre-installate in Hibernate):
delega al meccanismo transazionale JDBC transactions (impostazione predefinita)
delega a JTA (se esiste una transazione attiva, la Session esegue il suo lavoro in quel contesto, altrimenti una nuova transazione viene attivata)
Potete anche definire le vostre strategie transazionali (per usare un servizio transazionale CORBA, ad esempio).
Se volete usare caching a livello di JVM per dati che siano modificabili in un contesto JTA, dovete specificare una strategia per ottenere il TransactionManager JTA, poiché non esiste un metodo standardizzato nei container J2EE per farlo:
Tabella 3.5. TransactionManager JTA
Transaction Factory | Application Server |
---|---|
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 |
Una SessionFactory di Hibernate pubblicata sul JNDI può semplificare il reperimento della factory stessa, e la creazione di nuove Sessioni.
Se volete che la SessionFactory venga pubblicata su uno spazio di nomi JNDI, specificate un nome (ad esempio. java:comp/env/hibernate/SessionFactory) usando la proprietà hibernate.session_factory_name. Se questa proprietà viene omessa, la SessionFactory non verrà pubblicata sul JNDI. (Questo è particolarmente utile in ambienti in cui l'implementazione standard del JNDI sia di sola lettura, come ad esempio in Tomcat)
Quando Hibernate pubblicherà la SessionFactory sul JNDI, userà i valori di hibernate.jndi.url e hibernate.jndi.class per istanziare il contesto iniziale JNDI (InitialContext). Se queste proprietà non vengono specificate, verrà usato l'InitialContext predefinito.
Se scegliete di usare il JNDI, un EJB o qualsiasi altra classe di utilità può ottenere la SessionFactory con una ricerca (lookup) sull'albero JNDI.
Potete definire nuove etichette per le interrogazioni di Hibernate usando la proprietà hibernate.query.substitutions. Ad esempio:
hibernate.query.substitutions true=1, false=0
farebbe sì che le etichette true e false venissero tradotti in letterali interi nell'SQL generato.
hibernate.query.substitutions toLowercase=LOWER
permetterebbe di rinominare la funzione LOWER dell'SQL.
Hibernate traccia ("logs") vari eventi utilizzando la libreria commons-logging di Apache.
Il servizio di commons-logging indirizzerà l'uscita a Apache Log4j (se includete log4j.jar nel vostro classpath) o al logging nativo di JDK1.4 (se l'applicazione sta funzionando sotto JDK1.4 o superiori). Potete scaricare Log4j da http://jakarta.apache.org. Per usare Log4j avrete bisogno di un file log4j.properties sul classpath: un file di proprietà di esempio viene distribuito con Hibernate nella directory src/.
Raccomandiamo vivamente che vi familiarizziate con i messaggi di log di Hibernate. È stato fatto un grande sforzo per far sì che le tracce lasciate da Hibernate siano il più dettagliate possibile senza per questo renderle illeggibili. È uno strumento di risoluzione dei problemi fondamentale. Non dimenticate anche di abilitare la traccia dell'SQL come descritto in precedenza (hibernate.show_sql), perché è il primo passo quando si lavori alla risoluzione di problemi di performance.
L'interfaccia net.sf.hibernate.cfg.NamingStrategy vi consente di specificare uno "standard di denominazione" per gli oggetti del database e gli elementi dello schema.
Potete fornire regole per generare automaticamente identificatori di database da identificatori java, o per ricavare nomi "fisici" di tabella e colonna dai nomi "logici" dati nel file di mappaggio. Questa funzionalità consente di ridurre la prolissità del documento di mappaggio, eliminando il rumore ripetitivo (come ad esempio i prefissi TBL_). La strategia base è abbastanza minimale.
Potete specificare una strategia differente chiamando Configuration.setNamingStrategy() prima di aggiungere i mappaggi alla configurazione:
SessionFactory sf = new Configuration() .setNamingStrategy(ImprovedNamingStrategy.INSTANCE) .addFile("Item.hbm.xml") .addFile("Bid.hbm.xml") .buildSessionFactory();
net.sf.hibernate.cfg.ImprovedNamingStrategy è una strategia che viene distribuita con Hibernate e che potrebbe essere un punto di partenza utile per alcune applicazioni.
Un approccio alternativo è specificare una configurazione completa in un file chiamato hibernate.cfg.xml. Questo file può essere usato come un'alternativa al file hibernate.properties o, se sono presenti entrambi, per ridefinirne le proprietà.
Il file di configurazione XML viene caricato da Hibernate dalla radice del CLASSPATH. Ecco un esempio:
<?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> <!-- una istanza di SessionFactory indicata con il suo /nome/jndi --> <session-factory name="java:comp/env/hibernate/SessionFactory"> <!-- proprietà --> <property name="connection.datasource">my/first/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>
La configurazione di Hibernate richiede solo di scrivere
SessionFactory sf = new Configuration().configure().buildSessionFactory();
È comunque possibile specificare un differente file di configurazione XML usando
SessionFactory sf = new Configuration() .configure("catdb.cfg.xml") .buildSessionFactory();