Il primo progetto con Hibernate

Degli ORM e di Hibernate se ne è già parlato in un precedente articolo quindi in questo verranno omesse introduzioni in merito agli stessi argomenti.

Quello che invece verrà fatto è creare da zero un’applicazione in C# che usi NHibernate come ORM per la base dati che verrà realizzata, in questo caso, con SqlExpress.

Per cominciare facciamo un’analisi approssimativa del progetto:

Supponiamo quindi di dover realizzare una semplice applicazione che si occupi di gestire la persistenza dei dati di un archivio di fornitori e di prodotti ad essi associati.

In tal caso avremo un semplice ERD come quello mostrato sotto:

ERD

e supponiamo di avere già in archivio 3 fornitori:

SupplierTable

e 3 prodotti:

ProductTable

 

Apriamo quindi il Visual Studio (io utilizzerò il 2008 Express) e creiamo un nuovo progetto di tipo “Console Application” che chiamiamo (in onore del grande progetto Hello World) “HelloNHibernate” (nei miei progetti antepongo sempre il prefisso “RobertoCaico”, quindi il mio progetto verrà visualizzato come RobertoCaico.HelloNHibernate). In automatico verrà generata la classe Program.cs. Inseriamo nello stesso progetto un nuovo file di configurazione, che chiameremo app.config, nel quale inseriremo le informazioni relative alla connessione di NHibernate al database come di seguito mostrato:

<?xml version=1.0?>

<configuration>

<configSections>

<section name=nhibernate type=System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089/>

</configSections>

<nhibernate>

<add key=hibernate.connection.provider value=NHibernate.Connection.DriverConnectionProvider/>

<add key=hibernate.dialect value=NHibernate.Dialect.MsSql2000Dialect/>

<add key=hibernate.connection.driver_class value=NHibernate.Driver.SqlClientDriver/>

<add key=hibernate.connection.connection_string value=Server=.\SQLEXPRESS;initial catalog=HelloNHibernate;Integrated Security=SSPI;Language=Italian/>

<add key=hibernate.use_outer_join value=true/>

<add key=hibernate.show_sql value=false/>

</nhibernate>

</configuration>

 

La maggior parte delle impostazioni sopra definite sono praticamente standard, quella da attenzionare particolarmente è la stringa di connessione al database, la “connection_string” nella quale si va a specificare l’istanza del database (in questo caso “.\SQLEXPRESS”) e l’alias del database (in questo caso “HelloNHibernate” che è il nome del dbf che ho dato alla mia base dati).

 

Temporeggiamo, per adesso, sul codice da inserire nella classe Program.cs e concentriamoci sulla parte relativa ad NHibernate.

 

Per prima cosa dobbiamo creare un progetto che contenga le classi corrispondenti alle entità da persistere nella base dati. Creiamo quindi il nuovo progetto, di tipo “Class Library”, che chiamiamo “HelloNHibernate.Entity”.

Rifacendoci all’ERD disegnato sopra inseriremo due classi, una per i Supplier ed una per i Product.

Nello specifico creiamo la classe Supplier.cs:

 

public class Supplier

{

private string _code;

/// <summary>

/// Codice identificativo del fornitore

/// </summary>

public virtual string Code

{

get { return _code; }

set { _code = value; }

}

private string _lastName;

/// <summary>

/// Cognome del fornitore

/// </summary>

public virtual string LastName

{

get { return _lastName; }

set { _lastName = value; }

}

private string _firstName;

/// <summary>

/// Nome del fornitore

/// </summary>

public virtual string FirstName

{

get { return _firstName; }

set { _firstName = value; }

}

private string _businessName;

/// <summary>

/// Nome commerciale (della società) del fornitore

/// </summary>

public virtual string BusinessName

{

get { return _businessName; }

set { _businessName = value; }

}

private string _fiscalCode;

/// <summary>

/// Codice fiscale/ partita IVA del fornitore

/// </summary>

public virtual string FiscalCode

{

get { return _fiscalCode; }

set { _fiscalCode = value; }

}

private string _address;

/// <summary>

/// Indirizzo del fornitore

/// </summary>

public virtual string Address

{

get { return _address; }

set { _address = value; }

}

private string _phone;

/// <summary>

/// Numero di telefono del fornitore

/// </summary>

public virtual string Phone

{

get { return _phone; }

set { _phone = value; }

}

private string _mail;

/// <summary>

/// Indirizzo e-mail del fornitore

/// </summary>

public virtual string Mail

{

get { return _mail; }

set { _mail = value; }

}

private bool _isActive;

/// <summary>

/// Booleano: true se il fornitore è attivo

/// </summary>

public virtual bool IsActive

{

get { return _isActive; }

set { _isActive = value; }

}

private DateTime _createdOn;

/// <summary>

/// Data di inserimento nel database del fornitore

/// </summary>

public virtual DateTime CreatedOn

{

get { return _createdOn; }

set { _createdOn = value; }

}

private string _note;

/// <summary>

/// Note varie

/// </summary>

public virtual string Note

{

get { return _note; }

set { _note = value; }

}

}

 

e come si può ben vedere la classe conterrà semplicemente delle property, chiamate esattamente come i campi della tabella, di tipo concorde con quello definito nel database.

 

Alla stessa maniera, dunque, operiamo per creiare la classe Product.cs:

public class Product

{

private string _code;

/// <summary>

/// Codice identificativo del prodotto

/// </summary>

public virtual string Code

{

get { return _code; }

set { _code = value; }

}

private string _name;

/// <summary>

/// Nome del prodotto

/// </summary>

public virtual string Name

{

get { return _name; }

set { _name = value; }

}

private string _category;

/// <summary>

/// Categoria merceologica

/// </summary>

public virtual string Category

{

get { return _category; }

set { _category = value; }

}

private Int32 _piecesInStock;

/// <summary>

/// Pezzi nello stock da vendere

/// </summary>

public virtual Int32 PiecesInStock

{

get { return _piecesInStock; }

set { _piecesInStock = value; }

}

private decimal _price;

/// <summary>

/// Prezzo del prodotto

/// </summary>

public virtual decimal Price

{

get { return _price; }

set { _price = value; }

}

private string _note;

/// <summary>

/// Note

/// </summary>

public virtual string Note

{

get { return _note; }

set { _note = value; }

}

private Supplier _supplier;

/// <summary>

/// Informazione su chi fornisce il prodotto

/// </summary>

public virtual Supplier Supplier

{

get { return _supplier; }

set { _supplier = value; }

}

}

 

in questa classe è bene prestare attenzione alla definizione di una property di tipo Supplier che permetterà di gestire, nel mapping, la relazione tra le due tabelle.

 

Adesso definiremo un nuovo progetto nel quale andare d esplicitare il mapping tra le entità sopra definite e le tabelle della base dati.

Creiamo quindi un nuovo progetto di tipo “Class Library” che chiamiamo NHibernateSession.Configuration. Qui andiamo ad inserire un file xml che chiameremo DomainModel.hbm.xml e nel quale definiremo tanti tag di tipo <class…> quante sono le tabelle che andremo opportunamente a collegare alle entità precedentemente definite, quindi effettueremo gli opportuni legami tra property e colonne della tabella:

 

<hibernate-mapping auto-import=true xmlns=urn:nhibernate-mapping-2.0>

<class name=RobertoCaico.HelloNHibernate.Entity.Supplier, RobertoCaico.HelloNHibernate.Entity table=Supplier mutable=true >

<id name=Code type=String unsaved-value=null>

<column name=code not-null=true />

<generator class=assigned />

</id>

<property name=LastName type=String>

<column name=LastName not-null=false />

</property>

<property name=FirstName type=String>

<column name=FirstName not-null=false />

</property>

<property name=BusinessName type=String>

<column name=BusinessName not-null=false />

</property>

<property name=FiscalCode type=String>

<column name=FiscalCode not-null=false />

</property>

<property name=Address type=String>

<column name=Address not-null=false />

</property>

<property name=Phone type=String>

<column name=Phone not-null=false />

</property>

<property name=Mail type=String>

<column name=Mail not-null=false />

</property>

<property name=IsActive type=Boolean>

<column name=IsActive not-null=false />

</property>

<property name=CreatedOn type=DateTime>

<column name=CreatedOn not-null=false />

</property>

<property name=Note type=String>

<column name=Note not-null=false />

</property>

</class>

<class name=RobertoCaico.HelloNHibernate.Entity.Product, RobertoCaico.HelloNHibernate.Entity table=Product mutable=true >

<id name=Code type=String unsaved-value=null>

<column name=code not-null=true />

<generator class=assigned />

</id>

<property name=Name type=String>

<column name=Name not-null=false />

</property>

<property name=Category type=String>

<column name=Category not-null=false />

</property>

<property name=PiecesInStock type=Int32>

<column name=PiecesInStock not-null=false />

</property>

<property name=Price type=Decimal>

<column name=Price not-null=false />

</property>

<property name=Note type=String>

<column name=Note not-null=false />

</property>

<many-to-one name=Supplier class=RobertoCaico.HelloNHibernate.Entity.Supplier, RobertoCaico.HelloNHibernate.Entity>

<column name=SupplierCode not-null=true />

</many-to-one>

</class>

come si vede il mapping è abbastanza semplice, basta dare un’occhiata all’ERD e nasce spontaneo, una sola osservazione alla definizione della relazione tra le tabelle: questa è realizzata mediante l’inserimento di un attributo di tipo many-to-one tra la colonna “SupplierCode” della tabella “Product” e l’entità Supplier.

OSS. Fate attenzione al fatto che questo file xml deve essere, per il progetto, una ”Embedded Resource” e deve essere sempre copiata, quindi verificare le sue proprietà ed eventualmente modificarle opportunamente.

 

Adesso occorre andare a scrivere le classi che si occuperanno di attuare la persistenza sul db. Definiamo, quindi, un nuovo progetto che chiamiamo HelloNHibernate.DAO e nel quale inseriremo una classe per la persistenza della tabella Supplier, una per la tabella Product ed una (questa la inserisco solo per utilità) per la verifica della corretta connessione col database.

Definiamo quindi una UtilDAO.cs per la gestione della connessione, alla quale facciamo esporre un metodo pubblico checkDbConnection() dove istanziamo una nuova session di hibernate nella quale generiamo una transazione vuota:

 

public static class UtilDAO

{

public static bool checkDbConnection()

{

#region Verifica esistenza connessione al Db

Object[] parameters = null;

NHibernateSession.SessionObject SO = new NHibernateSession.SessionObject();

try

{

SO.OpenSession();

SO.OpenTransaction();

SO.CloseAndCommitTransaction();

SO.CloseSession();

return true;

}

catch (Exception e)

{

return false;

}

#endregion

}

}

 

se tale metodo non genererà un’eccezione allora il mapping col database sarà ben fatto.

Adesso scriviamo la classe relativa al DAO del Supplier:

public class SupplierDAO

{

private bool DBconnectionChecked;


public SupplierDAO()

{

DBconnectionChecked = UtilDAO.checkDbConnection();

}

/// <summary>

/// Salva un fornitore

/// </summary>

/// <param name=”supplier”></param>

public void SaveSupplier(Supplier supplier)

{

try

{

if (DBconnectionChecked)

{

string lastCode = “”;

if (String.IsNullOrEmpty(supplier.Code))

{

lastCode = GetLastSupplierCode();

if (!String.IsNullOrEmpty(lastCode))

{

string newcode = Convert.ToString(Int32.Parse(lastCode) + 1).PadLeft(5, Convert.ToChar(“0″));

supplier.Code = newcode;

}

else

supplier.Code = “00000″;

}

NHibernateSession.SessionObject SO = new NHibernateSession.SessionObject();

SO.OpenSession();

SO.OpenTransaction();

SO.SaveOrUpdate(supplier);

SO.CloseAndCommitTransaction();

SO.CloseSession();

}

}

catch (Exception ec)

{

throw new Exception();

}

}

/// <summary>

/// Ritorna una lista dei fornitori salvati nel database

/// </summary>

/// <returns></returns>

public IList GetSupplier()

{

IList supplierlist = new ArrayList();

try

{

if (DBconnectionChecked)

{

NHibernateSession.SessionObject SO = new NHibernateSession.SessionObject();

SO.OpenSession();

SO.OpenTransaction();

string _query = ” from Supplier”;

supplierlist = SO.Find(_query, typeof(Supplier));

SO.CloseSession();

}

}

catch (Exception ec)

{

throw new Exception();

}

return supplierlist;

}

/// <summary>

/// Recupera il codice dell’ultimo fornitore inserito

/// </summary>

/// <returns></returns>

public string GetLastSupplierCode()

{

string supplierCode = “”;

try

{

if (DBconnectionChecked)

{

NHibernateSession.SessionObject SO = new NHibernateSession.SessionObject();

SO.OpenSession();

SO.OpenTransaction();

string _query = “select dbo.Code from Supplier as dbo Order by code desc”;

IList resultQuery = SO.Find(_query, typeof(Supplier));

SO.CloseAndCommitTransaction();

SO.CloseSession();

if (resultQuery != null && resultQuery.Count > 0)

supplierCode = Convert.ToString(resultQuery[0]);

}

}

catch (Exception ec)

{

throw new Exception();

}

return supplierCode;

}

/// <summary>

/// Cancella un fornitore

/// </summary>

/// <param name=”supplier”></param>

public void DeleteSupplier(Supplier supplier)

{

try

{

if (DBconnectionChecked)

{

NHibernateSession.SessionObject SO = new NHibernateSession.SessionObject();

SO.OpenSession();

SO.OpenTransaction();

SO.Delete(supplier);

SO.CloseAndCommitTransaction();

SO.CloseSession();

}

}

catch (Exception ec)

{

throw new Exception();

}

}

}

 

e quello per il Product:

 

public class ProductDAO

{

private bool DBconnectionChecked;

public ProductDAO()

{

DBconnectionChecked = UtilDAO.checkDbConnection();

}

/// <summary>

/// Salvataggio di un Product

/// </summary>

/// <param name=”product”></param>

public void SaveProduct(Product product)

{

try

{

if (DBconnectionChecked)

{

string lastCode = “”;

if (String.IsNullOrEmpty(product.Code))

{

lastCode = GetLastProductCode();

if (!String.IsNullOrEmpty(lastCode))

{

string newcode = Convert.ToString(Int32.Parse(lastCode) + 1).PadLeft(5, Convert.ToChar(“0″));

product.Code = newcode;

}

else

product.Code = “00000″;

}

NHibernateSession.SessionObject SO = new NHibernateSession.SessionObject();

SO.OpenSession();

SO.OpenTransaction();

SO.SaveOrUpdate(product);

SO.CloseAndCommitTransaction();

SO.CloseSession();

}

}

catch (Exception ec)

{

throw new Exception();

}

}

/// <summary>

/// Restituisce una lista con tutti i Prodotti salvati nel database

/// </summary>

/// <returns></returns>

public IList GetProducts()

{

IList productlist = new ArrayList();

try

{

if (DBconnectionChecked)

{

NHibernateSession.SessionObject SO = new NHibernateSession.SessionObject();

SO.OpenSession();

SO.OpenTransaction();

string _query = ” from Product”;

productlist = SO.Find(_query, typeof(Product));

SO.CloseSession();

}

}

catch (Exception ec)

{

throw new Exception();

}

return productlist;

}

/// <summary>

/// Restituisce il codice dell’ultimo prodotto inserito

/// </summary>

/// <returns></returns>

public string GetLastProductCode()

{

string productCode = “”;

try

{

if (DBconnectionChecked)

{

NHibernateSession.SessionObject SO = new NHibernateSession.SessionObject();

SO.OpenSession();

SO.OpenTransaction();

string _query = “select dbo.Code from Product as dbo Order by code desc”;

IList resultQuery = SO.Find(_query, typeof(Product));

SO.CloseAndCommitTransaction();

SO.CloseSession();

if (resultQuery != null && resultQuery.Count > 0)

productCode = Convert.ToString(resultQuery[0]);

}

}

catch (Exception ec)

{

throw new Exception();

}

return productCode;

}

/// <summary>

/// Cancella un prodotto

/// </summary>

/// <param name=”product”></param>

public void DeleteProduct(Product product)

{

try

{

if (DBconnectionChecked)

{

NHibernateSession.SessionObject SO = new NHibernateSession.SessionObject();

SO.OpenSession();

SO.OpenTransaction();

SO.Delete(product);

SO.CloseAndCommitTransaction();

SO.CloseSession();

}

}

catch (Exception ec)

{

throw new Exception();

}

}

}

 

come si vede i metodi sono abbastanza semplici ed intuitivi.

 

A questo punto abbiamo, in pratica, definito tutti i progetti e le classi della solution. Ci resta da aggiungere le corrette reference per ogni progetto coi rispettivi using nelle classi (ricordiamoci di aggiungere le dll di Hibernate scaricabili molto agevolmente dalla rete e comunque disponibili nel pacchetto completo del codice che metterò a disposizione).

Completiamo, dunque, l’applicazione scrivendo il codice del main della classe Program.cs del progetto d’avvio definito inizialmente.

Supponendo di voler inizialmente leggere i dati salvati nel database, quindi di inserire un nuovo fornitore ed un nuovo prodotto ed infine di rileggere tutti i dati del db; in questo caso il codice da inserire sarà:

 

class Program

{

static void Main(string[] args)

{

//Istanzio due variabili locali per l’accesso al DAO

SupplierDAO suppDao = new SupplierDAO();

ProductDAO prodDao = new ProductDAO();

//Leggo i Suppliers memorizzati nel database

IList suppliers = suppDao.GetSupplier();

//Li stampo in console

Console.WriteLine(“**************** Elenco Fornitori : ****************”);

foreach (Supplier supplier in suppliers)

{

Console.WriteLine(“LastName= “ + supplier.LastName + ” | FirstName= “ + supplier.FirstName + ” | BusinessName= “ + supplier.BusinessName);

Console.WriteLine(“Address= “ + supplier.Address + ” | Phone= “ + supplier.Phone + ” | E-mail= “ + supplier.Mail);

Console.WriteLine(“”);

}

//Leggo i Product memorizzati nel database

IList products = prodDao.GetProducts();

//Li stampo in console

Console.WriteLine(“**************** Elenco Prodotti : ****************”);

foreach (Product product in products)

{

Console.WriteLine(“Name= “ + product.Name + ” | Category= “ + product.Category + ” | Pieces= “ + product.PiecesInStock + ” | Price= “ + product.Price);

Console.WriteLine(“Produttore = “ + product.Supplier.BusinessName);

Console.WriteLine();

}

//Creo un nuovo Supplier

Supplier newsupplier = new Supplier();

newsupplier.FirstName = “Giovanni”;

newsupplier.LastName = “Girino”;

newsupplier.BusinessName = “Il miglior pollo”;

newsupplier.CreatedOn = DateTime.Now;

newsupplier.Address = “Via dell’Aia, 23″;

//E lo salvo nel database

suppDao.SaveSupplier(newsupplier);

//Creo un nuovo prodotto

Product newproduct = new Product();

newproduct.Category = “Food”;

newproduct.Name = “Pollo”;

newproduct.PiecesInStock = 8;

newproduct.Price = new decimal(2.35);

newproduct.Supplier = newsupplier;

//E lo salvo nel database

prodDao.SaveProduct(newproduct);

//Adesso rileggo i Suppliers memorizzati nel database

suppliers = suppDao.GetSupplier();

//Li stampo in console

Console.WriteLine(“**************** Elenco Fornitori Aggiornato ****************”);

foreach (Supplier supplier in suppliers)

{

Console.WriteLine(“LastName= “ + supplier.LastName + ” | FirstName= “ + supplier.FirstName+ ” | BusinessName= “ + supplier.BusinessName );

Console.WriteLine(“Address= “ + supplier.Address+ ” | Phone= “ + supplier.Phone + ” | E-mail= “ + supplier.Mail);

Console.WriteLine(“”);

}

//Leggo i Product memorizzati nel database

products = prodDao.GetProducts();

//Li stampo in console

Console.WriteLine(“**************** Elenco Prodotti Aggiornato: ****************”);

foreach (Product product in products)

{

Console.WriteLine(“Name= “ + product.Name + ” | Category= “ + product.Category + ” | Pieces= “ + product.PiecesInStock + ” | Price= “ + product.Price + ” | Supplier = “ + product.Supplier.BusinessName);

Console.WriteLine(“”);

}

Console.ReadKey();

}

 

E’ evidente, dalla lettura del codice, come sia agevole la gestione dei valori delle entità lette dal database o come sia semplice fare un insert o un update o un delete di uno o più record.

E’ ovvio che il codice scritto in questo metodo non è proprio “affine” alle principali regole di buona programmazione, ma la cosa è voluta in quanto in post successivi affronterò, partendo da questo esempio, altri temi quali il refactoring, la gestione della grafica etc…

 

Avviando, a questo punto, il debug (o la release) del progetto d’avvio si visualizzeranno, inizialmente, nella console di sistema, i dati dei fornitori e dei prodotti inseriti attualmente nel database (visionabili nelle tabelle riportate dopo l’ERD all’inizio dell’articolo:

Console1

quindi verranno inseriti i dati del nuovo fornitore (Giovanni Girino – Il miglior pollo – via dell’Aia 23) e quello del nuovo prodotto (Pollo – food – 8 – 2.35) ed infine riletti e visualizzati in console tutti i dati archiviati nelle due tabelle:


Console2

Con questo si conclude, almeno per adesso, la trattazione dell’argomento Hibernate; è ovvio che di Hibernate si possono avere accezioni molto varie e molto dettagliate e complicate ma il fine di questo articolo non voleva essere quello, ma voleva essere una possibilità di dare delle basi concrete, con esempi pratici e non con accenni come spesso capita in rete, per creare da zero un nuovo progetto con un ORM, cosa che, oggi giorno, sia agli sviluppatori neofiti che a quelli più smaliziati potrebbe risultare molto importante per gettare le basi di un progetto quantosivoglia complesso.

 

Per chiunque volesse, il codice visualizzato nelle varie parti dell’articolo è disponibile qui.

Lascia un commento