Gestione della persistenza con NHibernate

di Marco De Sanctis, in ASP.NET,

Le applicazioni data-driven, cioè quelle che basano gran parte della logica sulla manipolazione di informazioni memorizzate su un database, sono senz'altro tra le più diffuse. Un approccio sempre più utilizzato consiste nel modellare i dati sfruttando i vantaggi della programmazione object oriented, secondo il pattern comunemente denominato Domain Model. La gestione della persistenza e del fetch di un grafo di oggetti, a prima vista operazione assolutamente banale, nasconde in realtà una serie di insidie che sfociano spesso in complicazioni difficili da superare.

La figura 1, che mostra un semplice modello di dominio di un ipotetico gestionale, rende da sola l'idea di quanto possa essere articolato un grafo di oggetti e come possano essere di conseguenza complesse (oltre che noiose e ripetitive) le operazioni di memorizzazione e lettura dei dati da database.

Figura 1
Figura 1 - Esempio di un grafo delle dipendenze in un modello di dominio

Immaginiamo di dover leggere una fattura da database o di doverne memorizzare una. Operazioni di questo tipo, di per sé estremamente comuni, pongono già i primi dilemmi su quali siano le query da effettuare: dal momento che il grafo è allo stesso tempo complesso e notevolmente interconnesso, quali dati devono essere letti e quali possono essere invece trascurati? A cosa dobbiamo far puntare le reference rimaste vuote? E l'operazione di memorizzazione di una fattura quali entità (e quindi quali tabelle) coinvolge? Si potrebbe continuare a lungo con questi interrogativi, e questo perché il compito di gestire la persistenza di un domain model di questo tipo (tra l'altro volutamente molto semplice) è effettivamente molto complesso.

Un Object/Relational Mapper (O/RM) è un framework che si propone di risolvere problematiche di questo tipo, sostanzialmente generando in maniera intelligente, mirata e soprattutto automatica, query SQL a partire da istruzioni di alto livello impartite dallo sviluppatore. Uno dei prodotti più diffusi per chi sviluppa in tecnologia .NET è NHibernate (NH), porting di Hibernate del mondo Java, da diversi mesi passato sotto l'egida JBoss (una divisione di Red Hat). Si tratta di un O/RM open source, liberamente scaricabile dal sito www.hibernate.org, dichiarato stabile oramai da alcuni anni e utilizzato con profitto in un gran numero di applicazioni di livello enterprise.

Primi passi con NHibernate

Supponiamo di dover realizzare un'applicazione di fatturazione molto semplice, che lavori con il domain model illustrato in figura 2.

Figura 2
Figura 2 - Modello di dominio

Questo modello corrisponde ad uno schema di tabelle simile a quello rappresentato in figura 3.

Figura 3
Figura 3 - Schema relazionale

Affinché NHibernate possa generare automaticamente le query SQL per persistere questi oggetti, è necessario creare un file di mapping per ognuno di essi, in cui descriverne la struttura e indicarne la corrispondenza nel database relazionale. Sicuramente la classe Cliente è quella più facile da cui partire, non possedendo riferimenti verso altre entità di dominio. Il file Cliente.hbm.xml (è questa l'estensione che per convenzione si assegna) contiene qualcosa di simile a quanto riportato di seguito.

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Aspitalia.Entities" namespace="Aspitalia.Entities" 
default-lazy="false" default-access="field.camelcase">
  <class name="Cliente" table="CLIENTI">
    <id name="Id" unsaved-value="0" >
      <generator class="native" />
    </id>
    <property name="RagioneSociale" />
    <property name="DataContrattoFornitura" />
    <property name="Fido" />
  </class>
</hibernate-mapping>

Il contenuto è piuttosto lineare e merita veramente pochi commenti: ad ogni tipo deve corrispondere un nodo <class> tramite il quale associarlo ad una tabella nel mondo relazionale. Dato che ogni riga sul database è univocamente identificata da una chiave primaria, NHibernate ha bisogno di conoscere quale, tra le proprietà dell'oggetto, è quella che la rappresenta e, nel caso di una nuova istanza (quando cioé la chiave è pari a quanto indicato come unsaved-value), qual è il generator da utilizzare per calcolarne il valore. Nel nostro caso è stato utilizzato native dato che, su SQL Server, l'identificativo del cliente è contenuto in una colonna identity, ma ne esistono svariati altri, tutti ampiamente descritti nella documentazione ufficiale.

3 pagine in totale: 1 2 3

Attenzione: Questo articolo contiene un allegato.

Contenuti dell'articolo

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti