Primi Passi con Hibernate

Con il termine Object relational Mapping (ORM) si intende una tecnica di programmazione utilizzata per l’integrazione di sistemi software che si basano sul paradigma della programmazione ad oggetti  con sistemi per la gestione di basi dati relazionali (RDBMS). In altri termini offrono un modello per il mapping (standard secondo la specifica JPA) tra oggetti di un dominio Object Oriented (OO) e entità di un dominio relazionale (tipicamente tabelle e relazioni). Esistono diversi framework open source che supportano il paradigma ORM, tra i quali iBatis (confluito poi in myBatis) ed il più famoso Hibernate.

In questo articolo muoviamo i primi passi con Hibernate e vediamo come creare una semplice applicazione java che lo utilizza.

Setup dell’Applicazione

Per la configurazione dell’applicazione di esempio utilizziamo maven e creiamo un nuovo progetto java senza specificare l’archetype. Quindi apriamo il file pom.xml ed inseriamo le dipendenze necessaire, che sono Hibernate (ovviamente) e il connettore per il database utilizzato, che nel nostro caso sarà MySql.

Proseguiamo creiamo la tabella UTENTI che sarà utilizzata nell’esempio. Si noti che per personale convenzione utilizzo il plurale per i nomi delle tabelle ed il singolare per i nomi degli oggetti associati. Lo script di generazione della tabella sulla schema javaboss è il seguente:

Configurazione di Hibernate

Per il corretto utilizzo di Hibernate è necessario fornire alcune informazioni in anticipo, come ad esempio: il database cui connettersi (e le relative credenziale), ma anche il mapping da operare tra le tabelle e gli oggetti del dominio.

Tali informazioni possono essere definite o attraverso un file di properties, denominato hibernate.properties, o mediante un file XML, denominato hibernate.cfg.xml. In entrambe i casi il file deve essere disponibile le classpath dell’applicazione, quindi, essendo il nostro un progetto maven, possiamo inserirli nella cartella src/main/resources.

Nell’esempio utilizzeremo il file XML, che dovrà essere conforme al DTD http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd (l’elenco di tutti i DTD utilizzati dal framework è disponibile al link http://hibernate.org/dtd/). Il file è il seguente:

Ispezionando il file troviamo una serie di proprietà di configurazione ed (in fondo) il tag <mapping>, di cui parleremo in seguito.

Connessione

Alcune proprietà sono direttamente riconducibili alle informazioni di configurazione presenti nella specifica JDBC, che è utilizzata da Hibernate, descritta nel post Accesso ai Database in Java con JDBC. Tra queste troviamo la url di connessione al db hibernate.connection.url, e le credenziali di accesso connection.username e connection.password. Il driver di connessione allo specifico RDBMS è invece definita dalla proprietà hibernate.connection.driver_class.

Ciascun RDBMS supporta il linguaggio SQL standard ma aggiunge a questo alcune caratteristiche distintive. La specifica del dialetto, attraverso la proprietà dialect, consente ad Hibernate di personalizzare gli statement SQL generati per lo specifico RDBMS. Nel nostro esempio il dialetto utilizzato è quello per MySQL con InnoDB, che è identificato dalla stringa org.hibernate.dialect.MySQL5InnoDBDialect.

Connection Pool

Per la gestione del connection pool Hibernate supporta diversi meccanismi. In una Web Application, ad esempio, è possibile utilizzare il connection pool fornito dal container ed ottenuto utilizzando JNDI. Il nostro esempio è però una applicazione java standard, quindi dobbiamo utilizzare uno dei connection pool supportati: c3p0Apache DBCPProxool, etc.

Le stringhe di configurazione del connection pool sono ovviamente dipendenti dalla specifica implementazione. Nel nostro esempio utilizziamo c3p0 e le proprietà sono:

  • hibernate.c3p0.min_size: Numero minimo di connessioni JDBC nel pool (default 1);
  • hibernate.c3p0.max_size: Numero massimo di connessioni JDBC nel pool (default 100);
  • hibernate.c3p0.timeout: Indica in secondi quando tempo una connessione può rimanere non attiva prima di essere rimossa dal pool (default 0, ovvero mai);
  • hibernate.c3p0.max_statements: Numero di statement SQL conservati in cache al fine di migliorare le performace dell’applicazione (default: 0, ovvero chaching disabilitato).

Mapping

Tra le proprietà di configurazione di Hibernate il mapping delle classi nelle entità del database è sicuramente quella più onerosa. Nelle prime versioni l’operazione era eseguita attraverso uno specifico file XML, mentre nelle versioni più recenti il mapping può essere realizzato anche attraverso l’utilizzo delle annotation. Nell’esempio è utilizzato il file Utente.hbm.xml, il quale è dichiarato nel tag <mapping> del file di configurazione hibernate.cfg.xml. Naturalmente è possibile avere più file XML di mapping (ad esempio un per classe) e conseguentemente più tag <mapping> nel file di configurazione.

Analizziamo la struttura base del file di mapping partendo da quello dell’esempio:

Il tag <class> definisce il mapping tra una classe (o persisted class), indicata nella proprietà name, e la tabella, indicata nella proprietà table. Nel caso di coincidenza tra nome classe e nome tabella la proprietà table non è necessaria. Il tag <id> è utilizzato per indicare la colonna destinata a contenere la primary key della tabella e che quindi ne identifica in modo univoco la specifica riga. L’attributo name identifica la proprietà della persisted class destinata a contenere la primary key associata alla colonna indicata dall’attributo column. Infine il tab <property> è utilizzato per mappare le restanti proprietà della classe. Si noti che l’attributo column è sempre non necessario nel caso che il nome della proprietà sia identico a quello della colonna associata.

Gestione della Sessione

L’accesso alla base dati avviene attraverso un oggetto di tipo Session. Gli oggetti persistiti sono letti e scritti attraverso tale oggetto che offre i metodi di basi necessari alla lettura, cancellazione e inserimento di righe in una tabella mappata in una persisted class. Gli oggetti conservati in sessione possono assumere tre possibili stati:

  • TRANSIENT: è un qualsiasi oggetto di una persisted class non associato ad una sessione che non ha una chiave associata e nessuna corrispondenza nel database;
  • PERSISTENT: un oggetto transient diventa persistito quando è associato ad una sessione, ha un identificativo univoco ed una rappresentazione nella base dati;
  • DETACHED: una volta che la sessione è chiusa l’oggetto diviene detached ovvero non connesso.

La sessione dovrebbe essere tenuta aperta solo per il tempo necessario all’esecuzione delle operazioni richieste, infatti non essendo di tipo thread safe, la sessione dovrebbe essere aperta e chiusa all’occorrenza.

Nell’esempio abbiamo implementato una classe di utilità per il set-up di un oggetto di tipo SessionFactory, il quale legge i file di configurazione e restituisce, all’occorrenza, una nuova sessione.

Una tipica unità di lavoro che utilizza l’oggetto Session avrà quindi il seguente aspetto:
Applichiamo quanto detto al nostro esempio ed eseguiamo le seguenti operazioni:

  1. creazione di un nuovo utente;
  2. aggiornamento dell’utente appena creato;
  3. recupero di tutti gli utenti nella tabella.

Uso delle Annotazioni

In alternativa all’utilizzo dei file di mapping hbm.xml è possibile utilizzare le annotazioni JPA introdotte nel JDK 5.0 ed inserite nel package javax.persistence. Per farlo innanzitutto  eliminiamo l’attributo resource nel tag <mapping>  del file hibernate.cfg.xml e sostituiamolo con l’attributo class nel modo segunete:

Successivamente annotiamo la casse Utente.java, al fine di ottenere lo stesso mapping che avevamo con il file Utente.hbm.xml, nel modo seguente:

A livello di classe sono presenti due annotazioni:

  1. @Entity: dichiara la classe pojo come una entità che può essere persistita e quindi gestita da Hibernate. E’ possibile specificare un nome per l’entità valorizzando l’attributo name dell’annotazione o, come nel nostro caso, il nome corrisponderà al nome della classe. Il nome dell’entità è importante per la definizione delle query HQL (non trattate nel post).
  2. @Table: dichiara il mapping vero e proprio verso la specifica tabella del database. L’annotazione prevede diversi attributi, tutti opzionali, per specificare il catalogo, lo schema, i constraint, etc. Nel nostro esempio abbiamo valorizzato l’attributo name per indicare il nome della tabella, poichè altrimenti sarebbe stato implicitamente mappato su una tabella UTENTE non esistente.

A livello di proprietà abbiamo utilizzato la sola annotazione @Id che identifica la proprietà destinata a contenere la primary key dell’entità. Nel nostro esempio i nomi delle proprietà della classe pojo coincidono con i nomi delle colonne della tabella, quindi non sono necessarie ulteriori annotazioni. Alternativamente avremmo potuto utilizzare l’annotazione @Column sul metodo get della proprietà e specificare l’attributo name per indicare il nome della colonna.

Codice Sorgente

Il codice sorgente dell’esempio è scaricabile qui hb-user.