Wstęp
NHibernate jest rozwiązaniem ORM
dla platformy .NET. Dostarcza środowisko do mapowania
obiektowo-relacyjnego dla tradycyjnego, relacyjnego modelu bazy danych. Jego podstawową
funkcją jest mapowanie z klas platformy .NET do tabeli baz
danych oraz od typów danych CLR do typów danych
SQL.
Artykuł ten jest pisany dla osób początkujących dlatego też zostanie poruszona w nim
funkcjonalność pozwalająca na ładowanie obiektu biznesowego z bazy danych oraz zapisywanie
zmian dokonanych na tym obiekcie z powrotem do bazy danych.
Przygotowana zostanie aplikacja, który w najprostszy możliwy sposób pokaże jak skonfigurować
i używać NHibernate. Aplikacja będzie tworzyła obiekt
Car i przechowywała go w tabeli
Car. Pozwoli również na dokonanie różnych operacji takich
jak: pobieranie i usuwanie obiektów.
Aplikacja konsolowa
Przejdźmy teraz do utworzenia projektu aplikacji konsolowej. Z punktu widzenia architektury
nie jest to najlepsze rozwiązanie, jednak dla przykładu w zupełności wystarczy. Dodajemy nową
klasę do naszego projektu:
namespace nHibernate
{
class Car
{
public virtual int CarId { get; set; }
public virtual string Brand { get; set; }
public virtual string Model { get; set; }
}
}
Jedną z najmocniejszych zalet NHibernate jest fakt, że nie ma
potrzeby tworzenia specjalnych interfejsów klas biznesowych. Obiekty te nie są świadome
mechanizmu używanego do ładowania i zapisywania. Jednakże, wymagane jest utworzenie
właściwości zdefiniowanych jako wirtualne (virtual).
Definicja taka jest niezbędna, gdyż dzięki temu NHibernate
będzie w stanie utworzyć proxy, tj. pośrednik pomiędzy
warstwą bazy danych a naszą aplikacją.
Mapowanie pliku XML
W przypadku braku specyfikacji dotyczącej obiektów do mapowania, ktoś powinien pokierować
procesem tłumaczenia z bazy danych do obiektu biznesowego i odwrotnie. Może to zostać
osiągnietę poprzez mapowanie pliku XML lub poprzez
zastosowanie atrybutów i właściwości. W naszej aplikacji użyjemy pliku do mapowania
aby nasza klasa była jak najbardziej przejrzysta.
Dodajemy nowy plik XML do projektu. Plik ten będzie
używany jako plik mapujący. Nazwa pliku musi być zdefiniowana jako:
Car.hbm.xml. Obie klasy <nazwa>.cs oraz plik mapujący <nazwa>.hbm.xml powinny być w tym samym folderze i <nazwa>
powinna być taka sama. Dodajmy poniższą treść do naszego pliku:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernateWprowadzenie" assembly="NHibernateWprowadzenie">
<class name="Car" table="Car">
<id name="CarId" column="CarId">
<generator class="identity" />
</id>
<property name="Brand" column="Brand" />
<property name="Model" column="Model" />
</class>
</hibernate-mapping>
Struktury pliku XML nie będę tłumaczył. Wydaje się dosyć przejrzysta. Należy pamiętać, aby w przypadku innej nazwy projektu
sprawdzić czy namespace oraz assembly mają prawidłowe nazwy.
W ustawieniach pliku XML należy jeszcze ustawić Build Action jako Embedded Resource:
Konfiguracja
Dodajemy nową konfigurację do pliku app.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
</configSections>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name="query.substitutions">hqlFunction=SQLFUNC</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">
Data Source=PAWEL;Initial Catalog=nHibernate;Integrated Security=True
</property>
<!--Jeżeli poniższa właściwość będzie ustawiona na 'true' w konsoli zobaczycie wykonywane zapytania-->
<property name="show_sql">false</property>
<mapping assembly="NHibernateWprowadzenie" />
</session-factory>
</hibernate-configuration>
</configuration>
Pamiętacje o zmianie connection.connection_string na
połączenie do swojej bazy danych oraz mapping assembly=""
na odpowiednią nazwę.
Użycie kodu
Baza danych została przygotowana, pliki mapujące zostały utworzone, pora przejść do użycia kodu.
W poniższej sekcji zostaną przedstawione operacje konfiguracji, pobierania, zapisywania czy
kasowania danych w skróconej wersji. Komplenty kod wraz ze szczegółowym opisem zostanie
zaprezentowany pod koniec artykułu. Dowiedzie się m.in. o wymaganych referencjach czy
sposobie nawiązywania połączenia z bazą danych.
Przygotowanie konfiguracji:
// sprawdzamy czy przy uruchamianiu aplikacje sesje są otwarte
// jeżeli tak należy je zamknąć
if (mySession != null && mySession.IsOpen)
{
mySession.Close();
}
if (mySessionFactory != null && !mySessionFactory.IsClosed)
{
mySessionFactory.Close();
}
// Inicjowanie NHibernate
myConfiguration = new Configuration();
myConfiguration.Configure();
mySessionFactory = myConfiguration.BuildSessionFactory();
mySession = mySessionFactory.OpenSession();
Dodawanie danych:
// Dodanie wcześniej przygotowanych danych
using (mySession.BeginTransaction())
{
mySession.Save(myInitialObjects[0]);
mySession.Save(myInitialObjects[1]);
mySession.Transaction.Commit();
}
Pobieranie danych:
// Wyświetlenie wszystkich danych z tabeli Car
using (mySession.BeginTransaction())
{
// Poniżej trworzymy kryteria pobierania danych z tabeli
ICriteria criteria = mySession.CreateCriteria<Car>();
IList<Car> list = criteria.List<Car>();
// Gdybyśmy chcieli zdefiniować warunki wyszukiwania wystarczy zrobić to w poniższy sposób
// IList<Car> list = criteria.List<Car>().Where(a => a.CarId > 3).ToList();
foreach (var item in list)
{
Console.WriteLine("Id: {0}, Marka: {1}, Model: {2}", item.CarId, item.Brand, item.Model);
}
}
Usuwanie danych:
// Usunięcie wybranego rekordu
using (mySession.BeginTransaction())
{
mySession.Delete(myInitialObjects[0]);
mySession.Transaction.Commit();
}
Console.WriteLine("Rezultat operacji: Usnięto - Id: {0}, Marka: {1}, Model: {2}",
myInitialObjects[0].CarId, myInitialObjects[0].Brand, myInitialObjects[0].Model);
Komplenty kod:
using NHibernate;
using NHibernate.Cfg;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
namespace NHibernateWprowadzenie
{
class Program
{
static void Main(string[] args)
{
NHibernateExample NHibernate = new NHibernateExample();
}
}
class NHibernateExample
{
// Pola związane z NHibernate
private Configuration myConfiguration;
private ISessionFactory mySessionFactory;
private ISession mySession;
private Car[] myInitialObjects;
private Car[] myFinalObjects;
public NHibernateExample()
{
Configuration();
NextAction();
}
public void Configuration()
{
// sprawdzamy czy przy uruchamianiu aplikacje sesje są otwarte
// jeżeli tak należy je zamknąć
if (mySession != null && mySession.IsOpen)
{
mySession.Close();
}
if (mySessionFactory != null && !mySessionFactory.IsClosed)
{
mySessionFactory.Close();
}
// Inicjowanie NHibernate
myConfiguration = new Configuration();
myConfiguration.Configure();
mySessionFactory = myConfiguration.BuildSessionFactory();
mySession = mySessionFactory.OpenSession();
// Przygotowanie przykładowanych danych
PrepareCarData();
Console.WriteLine("Konfiguracja przebiegła pomyślnie..");
Console.WriteLine();
}
public void PrepareCarData()
{
myInitialObjects = new Car[2];
myFinalObjects = new Car[2];
// Dodajemy dwa samochody
Car car1 = new Car();
car1.Brand = "Audi";
car1.Model = "100";
myInitialObjects[0] = car1;
Car car2 = new Car();
car2.Brand = "Audi";
car2.Model = "RS6";
myInitialObjects[1] = car2;
}
public void DeleteData()
{
// Usunięcie wszystkich rekordów
using (ISession session = mySessionFactory.OpenSession())
{
SqlConnection con = session.Connection as SqlConnection;
SqlCommand cmd = new SqlCommand("Delete from Car", con);
cmd.ExecuteNonQuery();
}
Console.WriteLine("Rezultat operacji: Dane zostały usunięte z tabeli Cars");
Console.WriteLine();
NextAction();
}
public void DeleteSpecifiedData()
{
// Usunięcie wybranego rekordu
using (mySession.BeginTransaction())
{
mySession.Delete(myInitialObjects[0]);
mySession.Transaction.Commit();
}
Console.WriteLine("Rezultat operacji: Usnięto - Id: {0}, Marka: {1}, Model: {2}",
myInitialObjects[0].CarId, myInitialObjects[0].Brand, myInitialObjects[0].Model);
Console.WriteLine();
NextAction();
}
public void AddData()
{
// Dodanie wcześniej przygotowanych danych
using (mySession.BeginTransaction())
{
mySession.Save(myInitialObjects[0]);
mySession.Save(myInitialObjects[1]);
mySession.Transaction.Commit();
}
Console.WriteLine("Rezultat operacji: Dane zostały poprawnie dodane do tabeli Cars");
Console.WriteLine();
NextAction();
}
public void GetAllData()
{
// Wyświetlenie wszystkich danych z tabeli Car
using (mySession.BeginTransaction())
{
// Poniżej trworzymy kryteria pobierania danych z tabeli
ICriteria criteria = mySession.CreateCriteria<Car>();
IList<Car> list = criteria.List<Car>();
// Gdybyśmy chcieli zdefiniować warunki wyszukiwania wystarczy zrobić to w poniższy sposób
// IList<Car> list = criteria.List<Car>().Where(a => a.CarId > 3).ToList();
foreach (var item in list)
{
Console.WriteLine("Id: {0}, Marka: {1}, Model: {2}", item.CarId, item.Brand, item.Model);
}
Console.WriteLine();
NextAction();
}
}
public void NextAction()
{
Console.WriteLine("Wybierz następne działanie, które chcesz wykonać: ");
Console.WriteLine("1: Skasowanie danych z tabeli Car");
Console.WriteLine("2: Dodanie danych do tabeli Car");
Console.WriteLine("3: Wyświetlenie wszystkich danych z tabeli Car");
Console.WriteLine("4: Opuścić aplikację: ");
Console.Write("Twój wybór: ");
int actionId = Convert.ToInt32(Console.ReadLine());
Console.WriteLine();
switch (actionId)
{
case 1:
DeleteData();
break;
case 2:
AddData();
break;
case 3:
GetAllData();
break;
case 4:
System.Environment.Exit(0);
break;
default:
break;
}
Console.ReadKey();
}
}
}