NHibernate - Szybki przewodnik
W tym rozdziale omówimy, czym jest NHibernate, na jakich platformach można go zaimplementować, jakie są jego zalety i inne aspekty z nim związane.
Co to jest NHibernate?
NHibernate jest dojrzałym narzędziem mapowania obiektowo-relacyjnym typu open source dla platformy .NET. Jest aktywnie rozwijany, w pełni funkcjonalny i używany w tysiącach udanych projektów. Jest zbudowany na szczycieADO.NET a aktualna wersja to NHibernate 4.0.4.
NHibernate to obiektowo-relacyjny program do mapowania .NET typu open source, który jest rozpowszechniany w ramach GNU Lesser General Public License.
Opiera się na Hibernate, popularnym mapowaniu obiektowo-relacyjnym Java i ma bardzo dojrzałą i aktywną bazę kodu.
Zapewnia strukturę do mapowania zorientowanego obiektowo modelu domeny na tradycyjną relacyjną bazę danych.
NHibernate został założony przez Tom Barrett a ten projekt istnieje od lutego 2003 roku, co było ich pierwszym zobowiązaniem.
To duży projekt i zapewnia dużą funkcjonalność.
Tam jest NuGet package dostępne, co bardzo ułatwia dodawanie do projektu.
Dlaczego NHibernate?
Teraz pytanie brzmi, dlaczego potrzebujemy object-relational mappers? Dzieje się tak, ponieważ istnieje rozłączenie między światem przedmiotowym a światem relacji.
W świecie przedmiotów wszystko jest wokół objects; nazwaliśmy obiektami te rzeczy, które mają nasze dane.
Świat relacyjny jest w całości oparty na zbiorach i mamy do czynienia z tabelami i wierszami, które są inne niż świat obiektów.
W świecie przedmiotów mamy unidirectional associations. Jeśli klient ma wskaźnik do zamówienia, niekoniecznie oznacza to, że zamówienie ma wskaźnik z powrotem do klienta, może lub nie.
W świecie relacji istnieją wszystkie skojarzenia bidirectional i można to zrobić za pomocą klucza obcego.
Wszystkie skojarzenia są z natury dwukierunkowe, więc kiedy mamy do czynienia z mapowaniem obiektowo-relacyjnym, musimy również poradzić sobie z tym rozłączeniem.
W świecie obiektów pracujemy ze wskaźnikami, które są jednokierunkowe, podczas gdy w świecie relacyjnym mamy klucze obce, które są z natury dwukierunkowe.
Świat obiektów ma to pojęcie dziedziczenia, w którym pojazd może mieć wiele różnych podklas, więc samochód jest typem pojazdu, łódź jest typem pojazdu, a samochód sportowy jest typem samochodu, tego typu relacje spadkowe.
Świat relacji nie ma pojęcia dziedziczenia.
Mapowanie
Jak więc mapujemy to wszystko disjoint relationships?Ta koncepcja mapowania pochodzi z mapowania relacyjno-obiektowego. Należy zrozumieć głównie trzy rzeczy, jak pokazano na poniższym diagramie.
W swojej aplikacji będziesz potrzebować definicji klas, które są zwykle kodem C # i jego kodem .NET, który reprezentuje nasze klasy, na przykład klasa pracownika, klasa klienta, klasa zamówienia itp.
Na dole widać schemat bazy danych, czyli nasz Data Definition Language w relacyjnej bazie danych, która określa, jak wygląda tabela klientów, jak wygląda tabela pracowników.
Pomiędzy nimi mamy metadane mapowania, które mówią programowi mapującemu obiektowo, jak tłumaczyć ze świata obiektów w języku C # do świata bazy danych w zakresie wierszy i kolumn oraz relacji kluczy obcych.
Te metadane mapowania można przedstawić na wiele różnych sposobów, a my przyjrzymy się kilku z tych różnych sposobów, najbardziej typowych w zastosowaniu NHibernate.
Jest reprezentowany przez HBM (Hibernate Mapping) pliki, które są plikami XML.
Obsługiwana baza danych
NHibernate obsługuje wiele różnych baz danych. NHibernate ma dostęp do każdej istniejącej relacyjnej bazy danych.
Serwer SQL to podstawowa obsługiwana baza danych, z której korzysta większość programistów podczas programowania, prawdopodobnie jest to najpopularniejsza.
To też works very well with Oracle.
Obsługuje również DB2, Firebird, MySQL, PostgreSQL, SQL Lite
To także ma ODBC and OLEDB drivers.
W dzisiejszych czasach wiele systemów jest projektowanych z architekturą warstwową, NHibernate również ją ma i doskonale współpracuje z tym projektem.
Architektura warstwowa
Architektura warstwowa dzieli system na kilka grup, z których każda zawiera kod odnoszący się do konkretnego obszaru problemowego i te grupy nazywane są warstwami. Większość aplikacji na poziomie przedsiębiorstwa używahigh-level application architecture które składają się z trzech warstw -
- Warstwa prezentacji
- Warstwa biznesowa
- Warstwa trwałości
Na przykład warstwa interfejsu użytkownika, znana również jako warstwa prezentacji, może zawierać cały kod aplikacji do tworzenia stron internetowych i przetwarzania danych wejściowych użytkownika.
Jedną z głównych zalet podejścia warstwowego jest to, że często można wprowadzać zmiany w jednej warstwie bez znaczących zakłóceń w innych warstwach, dzięki czemu systemy lesser fragile and more maintainable.
Warstwa prezentacji
Jest to najwyższa warstwa, która zawiera kod odpowiedzialny za rysowanie interfejsu użytkownika, stron, okien dialogowych lub ekranów oraz zbieranie danych wejściowych użytkownika i sterowanie nawigacją.
Warstwa biznesowa
Warstwa biznesowa jest odpowiedzialna za wdrażanie wszelkich reguł biznesowych lub wymagań systemowych, które użytkownicy zrozumieliby jako część domeny problemu.
Wykorzystuje również ponownie model zdefiniowany przez warstwę trwałości.
Warstwa trwałości
Warstwa trwałości składa się z klas i komponentów, które są odpowiedzialne za zapisywanie i pobieranie danych aplikacji.
Ta warstwa definiuje również mapowanie między klasą modelu a bazą danych. W tej warstwie stosowany jest przede wszystkim NHibernate.
Baza danych
- Baza danych istnieje poza aplikacją .NET.
- To rzeczywista, trwała reprezentacja stanu systemu.
- Jeśli używana jest baza danych SQL, zawiera ona schemat relacyjny i prawdopodobnie procedury składowane.
Klasy pomocnicze / użytkowe
Każda aplikacja ma zestaw klas pomocniczych lub narzędziowych, które obsługują inne warstwy: na przykład widżety interfejsu użytkownika, klasy obsługi wiadomości, klasy wyjątków i narzędzia rejestrujące.
Te elementy nie są uważane za warstwy, ponieważ nie są zgodne z regułami zależności międzywarstwowych w architekturze warstwowej.
Architektura NHibernate
Jest to widok wysokiego poziomu aplikacji NHibernate, ale można również zobaczyć prostą architekturę NHibernate.
Kod aplikacji wykorzystuje NHibernate ISession i IQuery API do operacji trwałych i tylko do zarządzania transakcjami bazy danych, najlepiej przy użyciu NHibernate ITransaction API.
Zanim zaczniemy naprawdę używać NHibernate, musimy zrozumieć podstawy, na których jest zbudowany. NHibernate to technologia trwałości oparta na idei mapowania obiektowo-relacyjnego lub ORM.
Co to jest ORM?
Mapowanie obiektowo-relacyjne (ORM) to plik programming techniquedo konwersji danych między niekompatybilnymi systemami typów w zorientowanych obiektowo językach programowania. Innymi słowy, jest to koncepcja odwzorowania obiektów biznesowych aplikacji na tabele relacyjnej bazy danych, dzięki czemu można łatwo uzyskać dostęp do danych i całkowicie je aktualizować za pośrednictwem modelu obiektowego aplikacji.
Jak już wiesz, relacyjne bazy danych zapewniają dobry sposób przechowywania danych, podczas gdy programowanie obiektowe jest dobrym podejściem do budowania złożonych aplikacji.
Ogólnie rzecz biorąc, NHibernate i ORM są najbardziej odpowiednie dla aplikacji z nietrywialną logiką biznesową, modelem domeny i pewnego rodzaju bazą danych.
Dzięki ORM bardzo łatwo jest utworzyć warstwę tłumaczeniową, która może z łatwością przekształcić obiekty w dane relacyjne iz powrotem.
Akronim ORM może również oznaczać modelowanie roli obiektu, a termin ten został wynaleziony, zanim mapowanie obiektowe / relacyjne stało się istotne.
Opisuje metodę analizy informacji wykorzystywaną w modelowaniu baz danych.
Dlaczego ORM?
ORM to plik framework która umożliwia odwzorowanie świata obiektów znalezionych w językach obiektowych na wiersze w tabelach relacyjnych znalezionych w relacyjnych bazach danych
Aby zrozumieć tę koncepcję, spójrzmy na poniższy diagram.
Na powyższym diagramie widać, że po prawej stronie mamy tabelę o nazwie Pracownik, która zawiera kolumny z każdą częścią danych skojarzoną z indywidualnym pracownikiem.
Mamy kolumnę na identyfikator, który jednoznacznie identyfikuje każdego pracownika.
Kolumna z nazwiskiem pracownika, inna kolumna z datą dołączenia i wreszcie kolumna z wiekiem pracownika.
Gdybyśmy chcieli napisać jakiś kod do przechowywania nowego pracownika w tabelach, nie jest to takie proste.
Na powyższym schemacie widać również, że mamy obiekt pracownika, który posiada pola na Id, imię, datę dołączenia oraz wiek.
Bez ORM musimy przetłumaczyć ten obiekt na kilka różnych instrukcji SQL, które wstawią dane pracownika do tabeli pracowników.
Zatem pisanie kodu w celu utworzenia kodu SQL w celu wykonania powyższego scenariusza nie jest takie trudne, ale jest trochę żmudne i dość łatwe do popełnienia błędu.
Używając ORM, takiego jak NHibernate, możemy zadeklarować, w jaki sposób określone klasy powinny być mapowane na tabele relacyjne i pozwolić ORM lub NHibernate zająć się nieprzyjemną pracą tworzenia SQL w celu wstawiania, aktualizowania, usuwania danych w zapytaniach w naszej tabeli pracowników.
To pozwala nam skupić się na używaniu obiektów i automatycznie przetłumaczyć te obiekty na tabele relacyjne.
Tak naprawdę to, co robi ORM, to oszczędza nam konieczności ręcznego mapowania obiektów na tabele.
Aby rozpocząć pracę nad NHibernate, będziemy potrzebować Visual Studio i pakietu NHibernate.
Instalacja programu Visual Studio
Firma Microsoft udostępnia free version programu Visual Studio, który zawiera również SQL Server i można go pobrać z https://www.visualstudio.com Poniżej przedstawiono kroki instalacji.
Step 1 - Po zakończeniu pobierania uruchom instalator, po czym zostanie wyświetlone następujące okno dialogowe.
Step 2 - Kliknij przycisk Instaluj, aby rozpocząć proces instalacji.
Step 3 - Po pomyślnym zakończeniu procesu instalacji zostanie wyświetlone następujące okno dialogowe.
Step 4 - Zamknij to okno dialogowe iw razie potrzeby uruchom ponownie komputer.
Step 5- Teraz otwórz program Visual studio z menu Start, co spowoduje otwarcie następującego okna dialogowego. Pierwsze przygotowanie zajmie trochę czasu.
Step 6 - Gdy to wszystko zostanie zrobione, pojawi się główne okno programu Visual Studio.
Instalacja pakietu NHibernate
NHibernate jest dojrzałym narzędziem mapowania obiektowo-relacyjnym typu open source dla platformy .NET. Jest aktywnie rozwijany, w pełni funkcjonalny i używany w tysiącach udanych projektów. Możesz zainstalować pakiet NHibernate za pomocą następujących metod.
Bezpośrednie pobieranie
Pobierz plik zip z pliku z https://sourceforge.net/ który zawiera wszystkie wymagane pliki binarne.
Wyodrębnij ten plik zip i dołącz wszystkie te pliki binarne do swojego projektu.
Zainstaluj za pomocą NuGet
Innym sposobem instalacji NHibernate jest użycie NuGet do zainstalowania pakietu NHibernate, co jest zdecydowanie najłatwiejszym sposobem włączenia NHibernate do projektu.
Pobierze wszystkie zależności NHibernate i utworzy odwołania do wszystkich wymaganych zestawów.
Aby zainstalować NHibernate, uruchom następujące polecenie w konsoli Menedżera pakietów.
install-package NHibernate
Teraz możesz rozpocząć swoją aplikację.
W tym rozdziale przyjrzymy się, jak rozpocząć prosty przykład z użyciem NHibernate. Będziemy budowaćsimple console application. Do stworzenia aplikacji konsolowej użyjemy Visual Studio 2015, który zawiera wszystkie funkcje potrzebne do stworzenia, przetestowania aplikacji przy użyciu pakietu NHibernate.
Poniżej przedstawiono kroki tworzenia projektu przy użyciu szablonów projektów dostępnych w programie Visual Studio.
Step 1 - Otwórz Visual studio i kliknij opcję menu Plik → Nowy → Projekt.
Step 2 - Otwiera się nowe okno dialogowe Projekt.
Step 3 - Z lewego panelu wybierz Szablony → Visual C # → Windows.
Step 4 - W środkowym okienku wybierz opcję Aplikacja konsoli.
Step 5 - Wprowadź nazwę projektu „NHibernateDemoApp” w polu Nazwa i kliknij przycisk OK, aby kontynuować.
Step 6 - Po utworzeniu projektu przez program Visual Studio w oknie Eksploratora rozwiązań zostanie wyświetlona liczba plików.
Jak wiecie, stworzyliśmy prosty projekt aplikacji konsolowej, teraz musimy dołączyć pakiet NHibernate do naszego projektu konsoli.
Przejdź do menu Narzędzia i wybierz Menedżer pakietów NuGet → Konsola Menedżera pakietów, otworzy się okno konsoli Menedżera pakietów.
Określ polecenie pokazane powyżej Package Manager Consolei naciśnij Enter, pobierze wszystkie zależności NHibernate i utworzy odniesienia do wszystkich wymaganych zestawów. Po zakończeniu instalacji zostanie wyświetlony komunikat przedstawiony na poniższej ilustracji.
Teraz, gdy dodaliśmy NHibernate, możemy rozpocząć wdrażanie. Więc zaczniemy od mapowania bardzo prostegotable nazywa Student, który ma po prostu całkowity klucz podstawowy o nazwie ID oraz kolumny FirstName i LastName.
Potrzebujemy klasy do reprezentowania tego ucznia, więc stwórzmy nową klasę o nazwie Student, klikając prawym przyciskiem myszy projekt w eksploratorze rozwiązań, a następnie wybierz Dodaj → Klasa, co otworzy okno dialogowe Dodaj nowy element.
Wchodzić Student.csw polu nazwy kliknij przycisk Dodaj. W tej klasie Student musimy mieć nasz całkowity klucz podstawowy o nazwie ID i musimy utworzyć ten ciąg,FirstName i LastName pola, jak pokazano w poniższej pełnej realizacji klasy Student.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NHibernateDemoApp {
class Student {
public virtual int ID { get; set; }
public virtual string LastName { get; set; }
public virtual string FirstMidName { get; set; }
}
}
Gdy mamy do czynienia z modelami w aplikacji NHibernate, najłatwiej jest uczynić wszystkie pola wirtualnymi. To jest nasz prosty model NHibernate, którego użyjemy i zmapujemy go do wewnętrznej bazy danych.
Przejdźmy teraz do metody Main w klasie Program i utwórzmy nowy obiekt konfiguracyjny NHibernate.
Pierwszą rzeczą, którą musimy zapewnić, jest plik connection string. Są to parametry połączenia specyficzne dla bazy danych, a najłatwiejszym sposobem znalezienia parametrów połączenia jest kliknięcie prawym przyciskiem myszy bazy danych wSQL Server Object Explorer i wybierz Właściwości.
Otworzy się okno Właściwości, teraz przewiń w dół, a zobaczysz pole Parametry połączenia w oknie Właściwości.
Skopiuj parametry połączenia i określ w kodzie. Poniżej znajduje się implementacja metody Main, w której potrzebujemy konfiguracji dla NHibernate.
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
//perform database logic
tx.Commit();
}
Console.ReadLine();
}
}
}
}
Po parametrach połączenia musimy dostarczyć sterownik, którym jest SQLClientDriver a następnie musimy również zapewnić mu dialekt, która wersja SQL Servera i będziemy używać MS SQL 2008.
NHibernate wie teraz, jak połączyć się z bazą danych. Inną rzeczą, którą musimy zrobić, jest dostarczenie mu listy modeli, które będziemy mapować.
Możemy to zrobić, dodając zestaw, więc określając plik Assembly.GetExecutingAssemblyi tutaj program znajdzie pliki mapujące. Pliki mapowania informują NHibernate, jak przejść z klas C # do tabel bazy danych.
SessionFactory kompiluje wszystkie metadane niezbędne do zainicjowania NHibernate. SessionFactory może służyć do budowania sesji, które są mniej więcej analogiczne do połączeń z bazą danych. Zatem właściwym sposobem jest użycie go w bloku using. mogę powiedziećvar session równa się sessionFactory.OpenSession i chcę to zrobić w ramach transakcji.
Po otwarciu sesji możemy nakazać sesji rozpoczęcie nowej transakcji, a następnie możemy wykonać tutaj logikę. Wykonaj więc logikę bazy danych i ostatecznie zatwierdź tę transakcję.
W tym rozdziale zajmiemy się niektórymi basic mappingi wiesz, że z ostatniego rozdziału mamy tabelę bazy danych oraz definicję klasy C #. Potrzebujemy teraz mapowania, które wyjaśnia, jak tłumaczyć z C # do bazy danych iz powrotem.
Więc przejdźmy dalej i dodajmy nowy plik XML, klikając prawym przyciskiem myszy projekt w eksploratorze rozwiązań i wybierając Dodaj → Nowy element ...
Wchodzić Student.hbm.xmlw polu nazwy. Musimy określić domyślny zestaw, który będzieNHibernateDemoAppa także określ domyślną przestrzeń nazw. To tylko skraca wiele innych definicji typów, które zamierzamy utworzyć w tym pliku.
Poniżej znajduje się implementacja w pliku XML -
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2"
assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp">
<class name = "Student">
<id name = "ID">
<generator class = "native"/>
</id>
<property name = "LastName"/>
<property name = "FirstMidName"/>
</class>
</hibernate-mapping>
Następną rzeczą, którą musimy zdefiniować, jest określenie klasy; ta klasa będzie naszaStudent class. Następnie musimy przekazać NHibernate nazwę identyfikatora, czyli ID, a także powiedzieć NHibernate, jak wygenerować identyfikator, więc nasz generator będzie typu native.
Generator typu natywnego oznacza, że w bazie danych, takiej jak SQL Server, będzie używać kolumny tożsamości, typu tożsamości.
Następną rzeczą, którą musimy zrobić, jest podanie nazw właściwości. Dlatego dodaj jeszcze dwie właściwości FirstName i LastName.
Teraz czytamy te pliki mapowania z zestawu. Więc preferowanym sposobem na zrobienie tego jest posiadanie tychHBM filesupieczony w twoim zestawie. Możemy to zrobić, po prostu ustawiając właściwość.
Teraz kliknij prawym przyciskiem myszy projekt w eksploratorze rozwiązań i wybierz Właściwości, zobaczysz plik Build Action field w którym Treść jest zaznaczona domyślnie.
Wybierz osadzony zasób z listy rozwijanej.
Więc to faktycznie osadza ten plik XML wewnątrz NHibernateDemoApp montaż.
W tym rozdziale zajmiemy się podstawami CRUD operations. Teraz, gdy nasz system jest gotowy do uruchomienia, ponieważ z powodzeniem wdrożyliśmy klasę Student domeny, zdefiniowaliśmy również pliki mapowania i skonfigurowaliśmy NHibernate. Możemy teraz używać niektórych zapytań do wykonywania operacji CRUD.
Utwórz dane
Jak widać, nie mamy danych w naszej tabeli Studentów w formacie NHibernateDemoDB Baza danych.
Aby dodać trochę danych, musimy wykonać Add/Create działanie, jak pokazano poniżej.
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var student1 = new Student {
ID = 1,
FirstMidName = "Allan",
LastName = "Bommer"
};
var student2 = new Student {
ID = 2,
FirstMidName = "Jerry",
LastName = "Lewis"
};
session.Save(student1);
session.Save(student2);
tx.Commit();
}
Console.ReadLine();
}
Jak widać, utworzyliśmy dwóch uczniów, a następnie wywołaliśmy metodę Save () klasy OpenSession a następnie wywołaj Commit () programu BeginTransaction. Oto pełna implementacja wProgram.cs plik
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var student1 = new Student {
ID = 1,
FirstMidName = "Allan",
LastName = "Bommer"
};
var student2 = new Student {
ID = 2,
FirstMidName = "Jerry",
LastName = "Lewis"
};
session.Save(student1);
session.Save(student2);
tx.Commit();
}
Console.ReadLine();
}
}
}
}
Teraz uruchommy tę aplikację, a następnie przejdź do Eksploratora obiektów SQL Server i odświeżmy bazę danych. Zobaczysz, że dwaj powyżsi uczniowie są teraz dodani do tabeli Studentów w bazie danych NHibernateDemoDB.
Odczytaj dane z tabeli uczniów
Możesz zobaczyć, że teraz mamy dwa rekordy w naszej tabeli uczniów. Aby odczytać te rekordy z tabeli, musimy wywołaćCreateCriteria() of OpenSession, jak pokazano w poniższym kodzie.
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var students = session.CreateCriteria<Student>().List<Student>();
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}",
student.ID,student.FirstMidName, student.LastName);
}
tx.Commit();
}
Console.ReadLine();
}
Więc jeśli chcesz listę rekordów, możemy po prostu powiedzieć lista typu Student.
Teraz użyj foreach przez wszystkich uczniów i powiedz: wydrukuj identyfikator, FirstMidName i LastNamena konsoli. Teraz uruchommy ponownie tę aplikację, a zobaczysz następujące dane wyjściowe w oknie konsoli.
1 Allan Bommer
2 Jerry Lewis
Możesz również pobrać dowolny rekord, określając identyfikator w pliku Get() metoda OpenSession przy użyciu następującego kodu.
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var students = session.CreateCriteria<Student>().List<Student>();
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}", student.ID,
student.FirstMidName, student.LastName);
}
var stdnt = session.Get<Student>(1);
Console.WriteLine("Retrieved by ID");
Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID,
stdnt.FirstMidName, stdnt.LastName);
tx.Commit();
}
Console.ReadLine();
}
Teraz po uruchomieniu aplikacji zobaczysz następujące dane wyjściowe.
1 Allan Bommer
2 Jerry Lewis
Retrieved by ID
1 Allan Bommer
Zaktualizuj rekord
Aby zaktualizować rekord w tabeli, musimy najpierw pobrać ten konkretny rekord, a następnie zaktualizować ten rekord, wywołując funkcję Update() metoda OpenSession, jak pokazano w poniższym kodzie.
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var students = session.CreateCriteria<Student>().List<Student>();
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}", student.ID,
student.FirstMidName, student.LastName);
}
var stdnt = session.Get<Student>(1);
Console.WriteLine("Retrieved by ID");
Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID, stdnt.FirstMidName, stdnt.LastName);
Console.WriteLine("Update the last name of ID = {0}", stdnt.ID);
stdnt.LastName = "Donald";
session.Update(stdnt);
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}", student.ID,
student.FirstMidName, student.LastName);
}
tx.Commit();
}
Console.ReadLine();
}
Teraz po uruchomieniu aplikacji zobaczysz następujące dane wyjściowe.
1 Allan Bommer
2 Jerry Lewis
Retrieved by ID
1 Allan Bommer
Update the last name of ID = 1
Fetch the complete list again
1 Allan Donald
2 Jerry Lewis
Jak widać, LastName o ID równym 1 jest aktualizowane z Bommera do Donalda.
Usuń rekord
Aby usunąć dowolny rekord z tabeli, musimy najpierw pobrać ten konkretny rekord, a następnie usunąć ten rekord, wywołując funkcję Delete() metoda OpenSession, jak pokazano w poniższym kodzie.
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var students = session.CreateCriteria<Student>().List<Student>();
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}", student.ID,
student.FirstMidName, student.LastName);
}
var stdnt = session.Get<Student>(1);
Console.WriteLine("Retrieved by ID");
Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID, stdnt.FirstMidName, stdnt.LastName);
Console.WriteLine("Delete the record which has ID = {0}", stdnt.ID);
session.Delete(stdnt);
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName,
student.LastName);
}
tx.Commit();
}
Console.ReadLine();
}
Teraz po uruchomieniu aplikacji zobaczysz następujące dane wyjściowe.
1 Allan Donald
2 Jerry Lewis
Retrieved by ID
1 Allan Bommer
Delete the record which has ID = 1
Fetch the complete list again
2 Jerry Lewis
Jak widać, rekord o identyfikatorze równym 1 nie jest już dostępny w bazie danych. Możesz również zobaczyć bazę danych w Eksploratorze obiektów SQL Server.
W tym rozdziale zrozumiemy, jak wyglądają wszystkie rekordy z bazy danych retrieved, updated, created, and deleted i jak dokładnie te zapytania są wykonywane?
Aby to wszystko zrozumieć, możemy po prostu dodać opcję do naszej konfiguracji, która loguje SQL w konsoli. Oto prosta instrukcja, która zarejestruje zapytanie SQL -
x.LogSqlInConsole = true;
Teraz mamy dwa rekordy w naszej tabeli uczniów w bazie danych NHibernateDemoDB. Pobierzmy wszystkie rekordy z bazy danych, jak pokazano w poniższym kodzie.
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
Console.WriteLine("\nFetch the complete list again\n");
var students = session.CreateCriteria<Student>().List<Student>();
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName,
student.LastName);
}
tx.Commit();
}
Console.ReadLine();
}
}
}
}
Więc przejdźmy dalej i ponownie uruchom tę aplikację, a zobaczysz następujące dane wyjściowe -
NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_,
this_.FirstMidName as FirstMid3_0_0_ FROM Student this_
Fetch the complete list again
3 Allan Bommer
4 Jerry Lewis
Jak widać, plik select clausewysyłany do bazy danych jest właściwie jak klauzula, która pobierze ID, FirstMidName i LastName. Tak więc wszystko to jest wysyłane do bazy danych i tam przetwarzane, a nie wiele rekordów przesyłanych z powrotem na serwer i przetwarzanych po stronie serwera.
NHibernate Profiler
Innym sposobem spojrzenia na te wyniki jest użycie NHibernate Profiler. NHibernate Profiler jest narzędziem komercyjnym, ale jest bardzo przydatne do pracy z aplikacjami NHibernate. Możesz łatwo zainstalować NHibernate Profiler w swojej aplikacji z NuGet.
Przejdźmy do konsoli Menedżera NuGet z menu Narzędzia, wybierając Menedżera pakietów NuGet → Konsola Menedżera pakietów. Otworzy się okno konsoli Menedżera pakietów. Wpisz następujące polecenie i naciśnij klawisz Enter.
PM> install-package NHibernateProfiler
Zainstaluje wszystkie wymagane pliki binarne dla NHibernate Profiler, po pomyślnym zainstalowaniu pojawi się następujący komunikat.
Zobaczysz również, że NHibernate Profiler jest uruchamiany po zainstalowaniu. Będzie to wymagało licencji, aby go używać, ale do celów demonstracyjnych możemy skorzystać z 30-dniowej wersji próbnej NHibernate Profiler.
Teraz NHibernate Profiler jest zoptymalizowany do pracy z aplikacjami internetowymi i zobaczysz, że został dodany App_Start folderw eksploratorze rozwiązań. Aby wszystko było proste, usuń folder App_Start, a także zauważysz, że jedna instrukcja jest dodawana na początku metody Main w klasie Program.
App_Start.NHibernateProfilerBootstrapper.PreStart();
Usuń również to oświadczenie i po prostu dodaj proste wywołanie NHibernateProfiler.Initialize jak pokazano w poniższym kodzie.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()){
var students = session.CreateCriteria<Student>().List<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName,
student.LastName);
}
tx.Commit();
}
Console.ReadLine();
}
}
}
}
Teraz, gdy uruchomisz aplikację, wyśle ona dane do aplikacji NHibernate Profiler.
Możesz zobaczyć tutaj, mamy ładny ekran, który pokazuje, że rozpoczęliśmy transakcję, co SQL robi z bazą danych w ładnym formacie.
Jest to więc bardzo przydatne do określenia, co dokładnie dzieje się w twojej aplikacji NHibernate. Staje się niezwykle przydatna, gdy aplikacja osiągnie pewien poziom złożoności, na którym potrzebujesz czegoś bardziej jak SQL Profiler, ale ze znajomością NHibernate.
W tym rozdziale dodamy IntelliSense do naszych plików mapowania NHibernate (*.hbm.xml files). Jak zauważyłeś podczas mapowania klasy Student domeny, że obecnie nie mamy dostępnej technologii IntelliSense. Bardzo przydatne jest posiadanieXML schemasdostępny. W tym rozdziale dowiesz się, jak dodać technologię IntelliSense w programie Visual Studio dla tych plików XML NHibernate.
Otwórz plik mapowania, a zobaczysz, że opcja menu XML pojawia się w menu głównym.
Wybierz opcję menu XML → Schematy…, co spowoduje wyświetlenie okna dialogowego Schematy XML.
Wybierz przycisk Dodaj…, który znajduje się w prawym górnym rogu okna dialogowego, które otwiera okno dialogowe pliku. Teraz przejdź dopackages folder, który znajduje się w folderze rozwiązania projektu i zobaczysz różne pakiety zawarte w projekcie.
Teraz kliknij dwukrotnie NHibernate.4.*** folder a zobaczysz dwa pliki schematów (* .xsd) lub pliki definicji schematu XML, które definiują konfigurację i mapowanie NHibernate.
Wybierz te dwa pliki schematu i kliknij przycisk Otwórz.
Jak widać, schematy NHibernate zostały dodane do okna dialogowego Schematy XML. Kliknij przycisk OK. Teraz zacznijmy nowy tag właściwości, a zobaczysz, że mamy tutaj pełną technologię IntelliSense.
Dostępna jest teraz technologia IntelliSense, która oszczędza dużo czasu podczas mapowania obiektowo-relacyjnego.
W tym rozdziale zajmiemy się mapowaniem typów danych. Mapowanie jednostek jest proste, klasy jednostek są zawsze mapowane na tabele bazy danych przy użyciu<class>, <subclass>, and <joined-subclass>mapowanie elementów. Typy wartości potrzebują czegoś więcej, czyli tam, gdzie są wymagane typy mapowania.
NHibernate jest w stanie mapować szeroką gamę typów danych. Oto lista najczęściej obsługiwanych typów danych.
Typ mapowania | Typ .NET | System.Data.DbType |
---|---|---|
Int16 | System.Int16 | DbType.Int16 |
Int32 | System.Int32 | DbType.Int32 |
Int64 | System.Int64 | DbType.Int64 |
Pojedynczy | System.Single | DbType.Single |
Podwójnie | System.Double | DbType.Double |
Dziesiętny | System.Decimal | DbType.Decimal |
Strunowy | System.String | DbType.String |
AnsiString | System.String | DbType.AnsiString |
Bajt | System.Byte | DbType.Byte |
Zwęglać | System.Char | DbType.StringFixedLength - jeden znak |
AnsiChar | System.Char | DbType.AnsiStringFixedLength - jeden znak |
Boolean | System.Boolean | DbType.Boolean |
Guid | System.Guid | DbType.Guid |
PersistentEnum | System.Enum (wyliczenie) | DbType dla wartości bazowej |
Prawda fałsz | System.Boolean | DbType.AnsiStringFixedLength - „T” lub „F” |
Tak nie | System.Boolean | DbType.AnsiStringFixedLength - „Y” lub „N” |
DateTime | DateTime | DbType.DateTime - ignoruje milisekundy |
Kleszcze | System.DateTime | DbType.Int64 |
Okres czasu | System.TimeSpan | DbType.Int64 |
Znak czasu | System.DateTime | DbType.DateTime - tak specyficzne, jak obsługuje baza danych |
Dwójkowy | System.Byte [] | DbType.Binary |
BinaryBlob | System.Byte [] | DbType.Binary |
StringClob | System.String | DbType.String |
Możliwość serializacji | Dowolny System.Object oznaczony SerializableAttribute | DbType.Binary |
CultureInfo | System.Globalization.CultureInfo | DbType.String - pięć znaków dla kultury |
Rodzaj | Rodzaj systemu | DbType.String przechowujący kwalifikowaną nazwę zespołu |
Powyższa tabela szczegółowo wyjaśnia poniższe wskazówki.
Wszystko, od prostych typów liczbowych po ciągi, które można odwzorowywać na różne sposoby za pomocą varchars, chars itp., a także ciągowe obiekty blob i wszystkie różne typy obsługiwane przez bazy danych.
Jest również w stanie mapować Booleans, zarówno do pól zawierających zera i jedynki, pól znakowych zawierających wartość true, false lub T i F.
Istnieje wiele różnych sposobów definiowania, w jaki sposób odwzorowuje się to na zaplecze, wartości logiczne w bazie danych.
Możemy obsłużyć mapowanie DateTime, włączając i wyłączając przesunięcia stref czasowych, czas letni itp.
Możemy również mapować enumerations; możemy odwzorować je na łańcuchy lub na ich podstawowe wartości liczbowe.
Spójrzmy na prosty przykład, w którym mamy te same nazwy właściwości zarówno w bazie danych, jak iw klasie Student.
Teraz zmieńmy FirstMidName na FirstName w klasie ucznia, gdzie nie zmienimy kolumny FirstMidName, ale zobaczymy, jak powiedzieć NHibernate, że ma przeprowadzić tę konwersję. Poniżej znajduje się zaktualizowana klasa uczniów.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NHibernateDemoApp {
class Student {
public virtual int ID { get; set; }
public virtual string LastName { get; set; }
public virtual string FirstName { get; set; }
}
}
Oto implementacja pliku mapowania NHibernate.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemoApp"
namespace = "NHibernateDemoApp">
<class name = "Student">
<id name = "ID">
<generator class = "native"/>
</id>
<property name = "LastName"/>
<property name = "FirstName" column = "FirstMidName" type = "String"/>
</class>
</hibernate-mapping>
W tym przykładzie załóżmy, że pole FirstName to ciąg .NET, a kolumna FirstMidName to SQL nvarchar. Teraz, aby powiedzieć NHibernate, jak przeprowadzić tę konwersję, ustaw nazwę na równąFirstName i kolumna równa FirstMidName i określ typ odwzorowania równy String, który jest odpowiedni dla tej konkretnej konwersji.
Poniżej znajduje się plik Program.cs implementacja plików.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var students = session.CreateCriteria<Student>().List<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstName,
student.LastName);
}
tx.Commit();
}
Console.ReadLine();
}
}
}
}
Teraz po uruchomieniu aplikacji zobaczysz następujące dane wyjściowe.
NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_,
this_.FirstMidName as FirstMid3_0_0_ FROM Student this_
Fetch the complete list again
3 Allan Bommer
4 Jerry Lewis
Jak widać, zamapował inną nazwę właściwości na nazwę kolumny w bazie danych.
Spójrzmy na inny przykład, w którym dodamy kolejną właściwość w klasie Student enumrodzaj. Oto implementacja klasy Student.
using System;
using System.Collections.Generic;
using System.Linq; using System.Text;
using System.Threading.Tasks;
namespace NHibernateDemoApp {
class Student {
public virtual int ID { get; set; }
public virtual string LastName { get; set; }
public virtual string FirstName { get; set; }
public virtual StudentAcademicStanding AcademicStanding { get; set; }
}
public enum StudentAcademicStanding {
Excellent,
Good,
Fair,
Poor,
Terrible
}
}
Jak widać, wyliczenie ma wiele różnych wartości, które może mieć, takie jak: Doskonała, Dobra, Dostateczna, Słaba i Okropna.
Przechodząc do pliku mapowania, można zauważyć, że każda z tych właściwości jest wymieniona w pliku mapowania, w tym nowo dodana AcademicStanding własność.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2"
assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp">
<class name = "Student">
<id name = "ID">
<generator class = "native"/>
</id>
<property name = "LastName"/>
<property name = "FirstName" column = "FirstMidName" type = "String"/>
<property name = "AcademicStanding"/>
</class>
</hibernate-mapping>
Teraz musimy również zmienić bazę danych, więc przejdź do Eksploratora obiektów SQL Server i kliknij bazę danych prawym przyciskiem myszy i wybierz opcję Nowa kwerenda….
Otworzy edytor zapytań, a następnie określi poniższe zapytanie.
DROP TABLE [dbo].[Student]
CREATE TABLE [dbo].[Student] (
[ID] INT IDENTITY (1, 1) NOT NULL,
[LastName] NVARCHAR (MAX) NULL,
[FirstMidName] NVARCHAR (MAX) NULL,
[AcademicStanding] NCHAR(10) NULL,
CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC)
);
To zapytanie najpierw usunie istniejącą tabelę uczniów, a następnie utworzy nową tabelę.
Kliknij ikonę Wykonaj, jak pokazano powyżej. Po pomyślnym wykonaniu zapytania zobaczysz komunikat.
Rozwiń bazę danych i listę rozwijaną Tabela, a następnie kliknij prawym przyciskiem myszy tabelę Ucznia i wybierz Projektanta widoku.
Teraz zobaczysz nowo utworzoną tabelę, która ma również nową właściwość AcademicStanding.
Dodajmy dwa rekordy, jak pokazano poniżej Program.cs plik.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var student1 = new Student {
ID = 1,
FirstName = "Allan",
LastName = "Bommer",
AcademicStanding = StudentAcademicStanding.Excellent
};
var student2 = new Student {
ID = 2,
FirstName = "Jerry",
LastName = "Lewis",
AcademicStanding = StudentAcademicStanding.Good
};
session.Save(student1);
session.Save(student2);
var students = session.CreateCriteria<Student>().List<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
student.FirstName, student.LastName, student.AcademicStanding);
}
tx.Commit();
}
Console.ReadLine();
}
}
}
}
Teraz uruchommy twoją aplikację, a zobaczysz następujące dane wyjściowe w oknie konsoli.
Fetch the complete list again
1 Allan Bommer Excellent
2 Jerry Lewis Good
Teraz spójrzmy do bazy danych, klikając prawym przyciskiem myszy tabelę uczniów.
Wybierz opcję Wyświetl dane, a zobaczysz dwa rekordy w tabeli uczniów, jak pokazano na poniższym zrzucie ekranu.
Możesz zobaczyć, że dodawane są dwa rekordy, a Allan ma AcademicStanding 0, a Jerry ma AcademicStanding 1. Dzieje się tak, ponieważ w .Net pierwsza wartość wyliczenia domyślnie ma 0, co oznacza doskonałe, jeśli spojrzysz na StudentAcademicStanding. Natomiast w pliku Student.cs Good jest drugim, więc ma wartość 1.
W tym rozdziale przyjrzymy się konfiguracji NHibernate. Mamy różne sposoby konfiguracji NHibernate. Dzieli się na dwie główne grupy
- Konfiguracja oparta na XML
- Konfiguracja oparta na kodzie
Konfiguracja oparta na kodzie
Konfiguracja oparta na kodzie jest wbudowana w NHibernate. Został on wprowadzony wokół NHibernate 3 i do tej pory używaliśmy konfiguracji baz kodu.
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
Wszystkie konfiguracje są określone w kodzie C #. Widać tutaj, że mamy nasz nowy obiekt konfiguracyjny, a następnie używamyloquacious configurationktóry został wprowadzony wraz z NHibernate 3.1 w celu skonfigurowania bazy danych. Jakich parametrów połączenia używamy, do jakiej bazy danych się łączymy i jakiego dialektu użyć. Dodajemy również nasz zespół mapowania bezpośrednio tutaj.
Konfiguracja oparta na języku XML
Jeśli używasz konfiguracji opartej na języku XML, możesz użyć pliku hibernate.cfg.xml plik, który jest po prostu samodzielnym plikiem xml używającym schematu NHibernate lub możesz osadzić tę specyficzną konfigurację NHibernate w swojej aplikacji lub web.cfg. Nazwa hibernate.cfg.xml jest domyślna, ale możemy również użyć dowolnej nazwy dla tego pliku xml.
Przyjrzyjmy się konfiguracji opartej na XML, dodając nowy plik xml do projektu NHibernateDemoApp i nazwijmy go hibernate.cfg.xml.
Wprowadź następujące informacje do pliku hibernate.cfg.xml.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2">
<session-factory>
<property name = "connection.connection_string">
Data Source = asia13797\\sqlexpress;
Initial Catalog = NHibernateDemoDB;
Integrated Security = True;
Connect Timeout = 15;
Encrypt = False;
TrustServerCertificate = False;
ApplicationIntent = ReadWrite;
MultiSubnetFailover = False;
</property>
<property name = "connection.driver_class">
NHibernate.Driver.SqlClientDriver
</property>
<property name = "dialect">
NHibernate.Dialect.MsSql2008Dialect
</property>
<mapping assembly = "NHibernateDemoApp"/>
</session-factory>
</hibernate-configuration>
Jak widać w powyższym pliku xml, określiliśmy tę samą konfigurację, jak wspomniano w C #.
Teraz skomentujmy tę konfigurację z pliku Program.cs i po prostu wywołaj Configure() metoda, która załaduje plik hibernate.cfg.xml plik, jak pokazano poniżej.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
//cfg.DataBaseIntegration(x =>
//{
// x.ConnectionString = "Data Source = asia13797;\\sqlexpress
Initial Catalog = NHibernateDemoDB;
Integrated Security = True;
Connect Timeout = 15;
Encrypt =False;
TrustServerCertificate = False;
ApplicationIntent = ReadWrite;
MultiSubnetFailover = False";
// x.Driver<SqlClientDriver>();
// x.Dialect<MsSql2008Dialect>();
// x.LogSqlInConsole = true;
//});
//cfg.AddAssembly(Assembly.GetExecutingAssembly());
cfg.Configure();
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var students = session.CreateCriteria<Student>().List<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
student.FirstName, student.LastName, student.AcademicStanding);
}
tx.Commit();
}
Console.ReadLine();
}
}
}
}
Uruchommy ponownie aplikację, a zobaczysz to samo wyjście.
Fetch the complete list again
1 Allan Bommer Excellent
2 Jerry Lewis Good
W tym rozdziale omówimy, jak zastąpić konfigurację NHibernate. Jest tylko kilka rzeczy, o których musisz pamiętać.
Przede wszystkim konfiguracja w NHibernate jest addytywna.
Więc nie musisz tylko używać pojedynczego pliku xml lub nie musisz po prostu używać konfiguracji opartej na kodzie lub płynnego NHibernate.
Możesz mieszać i dopasowywać wszystkie te metody w zależności od tego, jak chcesz skonfigurować swoją aplikację.
Ważną kwestią do zapamiętania jest to, że ostatecznie wygrywa konfiguracja.
W poniższym przykładzie widać, że tworzymy nasz obiekt konfiguracyjny, konfigurujemy go przy użyciu konfiguracji opartej na kodzie i ostatecznie wywołujemy plik cfg.configure() , która ładuje plik hibernate.cfg.xml.
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
});
cfg.Configure();
Zatem wszystko w pliku hibernate.cfg.xml zastępuje ustawienia ustawione przez konfigurację opartą na kodzie.
Odwracając te dwa procesy, możemy umieścić wartości domyślne w pliku hibernate.cfg.xml, a następnie wykonać nadpisania w konfiguracji opartej na kodzie.
Nie ma nic, co wykluczałoby używanie konfiguracji opartej na kodzie, a także nic nie stoi na przeszkodzie, aby użyć pliku hibernate.cfg.xml.
Przyjrzyjmy się prostemu przykładowi, w którym nadpisujemy konfigurację, używając kombinacji konfiguracji opartej na XML i kodzie.
Przenieśmy również parametry połączenia do app.config plik przy użyciu następującego kodu.
<?xml version = "1.0" encoding = "utf-8" ?>
<configuration>
<startup>
<supportedRuntime version = "v4.0" sku = ".NETFramework,Version = v4.5" />
</startup>
<connectionStrings>
<add name = "default" connectionString = "Data Source =
asia13797\\sqlexpress;
Initial Catalog = NHibernateDemoDB;
Integrated Security = True;
Connect Timeout = 15;
Encrypt = False;
TrustServerCertificate = False;
ApplicationIntent = ReadWrite;
MultiSubnetFailover = False"/>
</connectionStrings>
</configuration>
W niektórych plikach znajduje się ciąg połączenia app.configplik z domyślną nazwą. Teraz musimy wspomnieć domyślną nazwę w pliku hibernate.cfg.xml zamiast parametrów połączenia.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2">
<session-factory>
<property name = "connection.connection_string">default</property>
<property name = "connection.driver_class">
NHibernate.Driver.SqlClientDriver
</property>
<property name = "dialect">
NHibernate.Dialect.MsSql2008Dialect
</property>
<mapping assembly = "NHibernateDemoApp"/>
</session-factory>
</hibernate-configuration>
Skomentujmy część parametrów połączenia, sterownik i część dialektu z konfiguracji opartej na kodzie, ponieważ program odczyta je z pliku hibernate.cfg.xml i LogSqlInConsole część pozostanie w konfiguracji opartej na kodzie.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { //x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
//x.Driver<SqlClientDriver>();
//x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
});
cfg.Configure();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var students = session.CreateCriteria<Student>().List<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
student.FirstName, student.LastName, student.AcademicStanding);
}
tx.Commit();
}
Console.ReadLine();
}
}
}
}
Teraz po uruchomieniu aplikacji zobaczysz, że program odczytał dziennik konfiguracji opartej na kodzie i inną konfigurację z pliku hibernate.cfg.xml.
NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_,
this_.FirstMidName as FirstMid3_0_0_, this_.AcademicStanding as Academic4_0_0_ FROM
Student this_
Fetch the complete list again
1 Allan Bommer Excellent
2 Jerry Lewis Good
Więc teraz mamy część naszej konfiguracji w naszym hibernate.cfg.xml plik, część z nich znajduje się w konfiguracji opartej na kodzie i zależy od kolejności wywoływania kodu opartego na configure(), możemy zmienić, który z nich ma pierwszeństwo przed drugim.
W tym rozdziale zajmiemy się aktualizacją rozmiaru partii. Wielkość partii na to pozwalacontrol the number of updates które wychodzą w jednej rundzie do Twojej bazy danych dla obsługiwanych baz danych.
Wielkość partii aktualizacji została ustawiona domyślnie na NHibernate 3.2.
Ale jeśli używasz wcześniejszej wersji lub potrzebujesz dostroić swoją aplikację NHibernate, powinieneś przyjrzeć się rozmiarowi aktualizacji, który jest bardzo przydatnym parametrem, który może być użyty do dostrojenia wydajności NHibernate.
W rzeczywistości rozmiar partii kontroluje liczbę wstawek do wypchnięcia w grupie do bazy danych.
Obecnie tylko SQL Server i Oracle obsługują tę opcję, ponieważ podstawowy dostawca bazy danych musi obsługiwać przetwarzanie wsadowe zapytań.
Rzućmy okiem na prosty przykład, w którym ustawiliśmy rozmiar partii na 10, co spowoduje wstawienie 10 rekordów w zestawie.
cfg.DataBaseIntegration(x => {
x.ConnectionString = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
x.BatchSize = 10;
});
Oto pełna implementacja, w której do bazy danych zostanie dodanych 25 rekordów.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver>SqlClientDriver<();
x.Dialect>MsSql2008Dialect>();
x.LogSqlInConsole = true;
x.BatchSize = 10;
});
//cfg.Configure();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
for (int i = 0; i < 25; i++) {
var student = new Student {
ID = 100+i,
FirstName = "FirstName"+i.ToString(),
LastName = "LastName" + i.ToString(),
AcademicStanding = StudentAcademicStanding.Good
};
session.Save(student);
}
tx.Commit();
var students = session.CreateCriteria<Student>().List<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,student.FirstName,
student.LastName, student.AcademicStanding);
}
}
Console.ReadLine();
}
}
}
}
Teraz uruchommy twoją aplikację i zobaczysz, że wszystkie te aktualizacje przeskakują do profilera NHibernate. Mamy 26 indywidualnych przejazdów w obie strony do bazy danych, 25 w celu wstawienia i jednego w celu pobrania listy studentów.
Dlaczego tak jest? Powodem jest to, że NHibernate musi zrobićselect scope identity ponieważ używamy strategii generowania natywnych identyfikatorów w pliku mapowania dla identyfikatora, jak pokazano w poniższym kodzie.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2"
assembly = "NHibernateDemoApp"
namespace = "NHibernateDemoApp">
<class name = "Student">
<id name = "ID">
<generator class = "native"/>
</id>
<property name = "LastName"/>
<property name = "FirstName" column = "FirstMidName" type = "String"/>
<property name = "AcademicStanding"/>
</class>
</hibernate-mapping>
Musimy więc użyć innej metody, takiej jak guid.combmetoda. Jeśli mamy zamiar przejść do guid.comb, musimy przejść do naszego klienta i zmienić to naguid. Więc to zadziała dobrze. Teraz zmieńmy się z natywnego na guid.comb, używając następującego kodu.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly =
"NHibernateDemoApp" namespace = "NHibernateDemoApp">
<class name = "Student">
<id name = "ID">
<generator class = "guid.comb"/>
</id>
<property name = "LastName"/>
<property name = "FirstName" column = "FirstMidName" type = "String"/>
<property name = "AcademicStanding"/>
</class>
</hibernate-mapping>
Więc to baza danych jest odpowiedzialna za generowanie tych identyfikatorów. Jedynym sposobem, w jaki NHibernate może dowiedzieć się, jaki identyfikator został wygenerowany, było wybranie go natychmiast po tym. W przeciwnym razie, jeśli utworzyliśmy grupę uczniów, nie będzie ona w stanie dopasować identyfikatora utworzonego ucznia.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NHibernateDemoApp {
class Student {
public virtual Guid ID { get; set; }
public virtual string LastName { get; set; }
public virtual string FirstName { get; set; }
public virtual StudentAcademicStanding AcademicStanding { get; set; }
}
public enum StudentAcademicStanding {
Excellent,
Good,
Fair,
Poor,
Terrible
}
}
Musimy tylko zaktualizować naszą bazę danych. Upuśćmy tabelę uczniów i utwórzmy nową tabelę, określając następujące zapytanie, więc przejdź do Eksploratora obiektów SQL Server i kliknij prawym przyciskiem myszy bazę danych i wybierzNew Query… opcja.
Otworzy edytor zapytań, a następnie określi następujące zapytanie.
DROP TABLE [dbo].[Student]
CREATE TABLE [dbo].[Student] (
-- [ID] INT IDENTITY (1, 1) NOT NULL,
[ID] UNIQUEIDENTIFIER NOT NULL,
[LastName] NVARCHAR (MAX) NULL,
[FirstMidName] NVARCHAR (MAX) NULL,
[AcademicStanding] NCHAR(10) NULL,
CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC)
);
To zapytanie najpierw usunie istniejącą tabelę uczniów, a następnie utworzy nową tabelę. Jak widać, użyliśmyUNIQUEIDENTIFIER zamiast używać klucza podstawowego w postaci liczby całkowitej jako identyfikatora.
Wykonaj tę kwerendę, a następnie przejdź do Designer view a zobaczysz, że teraz identyfikator jest tworzony z unikalnym identyfikatorem, jak pokazano na poniższym obrazku.
Teraz musimy usunąć ID z pliku program.cs podczas wstawiania danych, ponieważ teraz wygeneruje on plik guids za to automatycznie.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
x.BatchSize = 10;
});
//cfg.Configure();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
for (int i = 0; i > 25; i++) {
var student = new Student {
FirstName = "FirstName"+i.ToString(),
LastName = "LastName" + i.ToString(),
AcademicStanding = StudentAcademicStanding.Good
};
session.Save(student);
}
tx.Commit();
var students = session.CreateCriteria<Student>().List<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
student.FirstName,student.LastName, student.AcademicStanding);
}
}
Console.ReadLine();
}
}
}
}
Teraz ponownie uruchom aplikację i spójrz na profiler NHibernate. Teraz profiler NHibernate zamiast 26 podróży w obie strony wykona tylko cztery.
Wstawiono dziesięć wierszy do tabeli, następnie kolejne dziesięć wierszy, a później pozostałe pięć. Po zatwierdzeniu wstawił jeszcze jeden w celu pobrania wszystkich rekordów.
Więc podzielił go na grupy po dziesięć, najlepiej jak potrafił.
Więc jeśli robisz dużo wstawek, może to znacznie poprawić wydajność wstawiania w twojej aplikacji, ponieważ możesz to zrobić wsadowo.
Dzieje się tak, ponieważ NHibernate sam przypisuje te przewodniki za pomocą rozszerzenia guid.comb algorytm i nie musi polegać na bazie danych, aby to zrobić.
Więc użycie rozmiaru partii to świetny sposób na jej dostrojenie.
W tym rozdziale omówimy, w jaki sposób cachingdziała w aplikacjach NHibernate. Posiada wbudowaną obsługę buforowania. Wygląda na prostą funkcję, ale w rzeczywistości jest to jedna z najbardziej złożonych funkcji. Zaczniemy od pamięci podręcznej pierwszego poziomu.
Pamięć podręczna pierwszego poziomu
Ten mechanizm pamięci podręcznej jest domyślnie włączony w NHibernate i nie musimy nic robić, aby pracować z pamięcią podręczną. Aby to zrozumieć, spójrzmy na prosty przykład, ponieważ widzisz, że mamy dwa rekordy w naszej bazie danych.
Teraz w tym przykładzie pobieramy studenta, którego identyfikator to 1, i dwukrotnie użyjemy tego samego zapytania sesji, jak pokazano w poniższym kodzie.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cache;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
x.BatchSize = 10;
});
//cfg.Configure();
cfg.Cache(c => {
c.UseMinimalPuts = true;
c.UseQueryCache = true;
});
cfg.SessionFactory().Caching .Through<HashtableCacheProvider>()
.WithDefaultExpiration(1440);
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()){
using (var tx = session.BeginTransaction()) {
var studentUsingTheFirstQuery = session.Get<Student>(1);
var studentUsingTheSecondQuery = session.Get<Student>(1);
}
Console.ReadLine();
}
}
}
}
Teraz uruchommy tę aplikację i zobaczmy wynik w NHibernate Profiler.
Będziesz zaskoczony, gdy zobaczysz, że NHibernate odpala tylko jedno zapytanie. W ten sposób NHibernate wykorzystuje pamięć podręczną pierwszego poziomu. Kiedy pierwsze zapytanie jest wykonywane, NHibernate buforuje Studenta o ID = 1 w swojej pamięci podręcznej pierwszego poziomu.
Tak więc, kiedy drugie zapytanie jest wykonywane, NHibernate najpierw wyszukuje pamięć podręczną pierwszego poziomu Jednostka Studenta o ID = 1, jeśli znajdzie tę jednostkę, to NHibernate wie, że nie ma potrzeby odpalania kolejnego zapytania w celu ponownego pobrania tego samego obiektu pracownika .
W tym rozdziale będziemy mówić o mapowaniu komponentów. W NHibernate,component is a value object. Nie ma własnej tożsamości.
Przykładem może być pieniądz, torebka lub portfel, który może zawierać pieniądze, ale dokładna tożsamość tych pieniędzy nie ma znaczenia.
Nie ma własnego klucza podstawowego, ale same komponenty są trwałe w tej samej tabeli, co obiekt będący ich właścicielem.
Spójrzmy na prosty przykład, w którym uczeń ma adres, który jest obiektem Location class powiązany z tym.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NHibernateDemoApp {
class Student {
public virtual int ID { get; set; }
public virtual string LastName { get; set; }
public virtual string FirstName { get; set; }
public virtual StudentAcademicStanding AcademicStanding { get; set; }
public virtual Location Address { get; set; }
}
public class Location {
public virtual string Street { get; set; }
public virtual string City { get; set; }
public virtual string Province { get; set; }
public virtual string Country { get; set; }
}
public enum StudentAcademicStanding {
Excellent,
Good,
Fair,
Poor,
Terrible
}
}
Teraz musimy również zaktualizować bazę danych, wykonując następujące zapytanie, które najpierw usunie tabelę uczniów, a następnie utworzy nową tabelę, która będzie również zawierać kolumnę dla klasy Location.
DROP TABLE [dbo].[Student]
CREATE TABLE [dbo].[Student] (
[ID] INT IDENTITY (1, 1) NOT NULL,
[LastName] NVARCHAR (MAX) NULL,
[FirstMidName] NVARCHAR (MAX) NULL,
[AcademicStanding] NCHAR(10) NULL,
[Street] NVARCHAR (100) NULL,
[City] NVARCHAR (100) NULL,
[Province] NVARCHAR (100) NULL,
[Country] NVARCHAR (100) NULL,
CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC)
);
Teraz mapowanie tych kolumn, które nie są bezpośrednio częścią klasy Student, ale są właściwościami klasy Location i obiektu klasy Location, jest zdefiniowane w klasie student. Potrzebujemy komponentu, aby poprawnie go odwzorować. Stwórzmy komponent wstudent.hbm.xml plik, jak pokazano w poniższym kodzie.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2"
assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp">
<class name = "Student">
<id name = "ID">
<generator class = "native"/>
</id>
<property name = "LastName"/>
<property name = "FirstName" column = "FirstMidName" type = "String"/>
<property name = "AcademicStanding"/>
<component name = "Address">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
</class>
</hibernate-mapping>
Ten składnik to Adres i ma różne właściwości. Dzięki tym informacjom NHibernate ma teraz wystarczająco dużo, aby faktycznie to zmapować.
Tutaj jest plik Program.cs, w którym nowy obiekt studenta jest tworzony i inicjowany, a następnie zapisywany w bazie danych. Następnie pobierze listę z bazy danych.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cache;
using NHibernate.Caches.SysCache;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var student1 = new Student {
ID = 1,
FirstName = "Allan",
LastName = "Bommer",
AcademicStanding = StudentAcademicStanding.Poor,
Address = new Location {
Street = "123 Street",
City = "Lahore",
Province = "Punjab",
Country = "Pakistan"
}
};
session.Save(student1);
tx.Commit();
var students = session.Query<Student>().ToList<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2} \t{3} \t{4} \t{5} \t{6} \t{7}",
student.ID,
student.FirstName,
student.LastName,
student.AcademicStanding,
student.Address.Street,
student.Address.City,
student.Address.Province,
student.Address.Country
);
}
}
Console.ReadLine();
}
}
}
}
Teraz możemy uruchomić tę aplikację, a NHibernate może zapisać te wartości w bazie danych. Po uruchomieniu aplikacji zobaczysz następujące dane wyjściowe.
Fetch the complete list again
2 Allan Bommer Poor 123 Street Lahore Punjab Pakistan
Oto wartości w bazie danych.
Komponenty pozwalają nam oddzielić kolumny, które są w tabeli bazy danych do ich własnej oddzielnej klasy.
Inną rzeczą, na którą należy zwrócić uwagę, jest to, że lokalizacja jest klasą, a nie bytem.
Jest to obiekt typu wartości i nie ma własnego klucza podstawowego.
Jest zapisywany w tej samej tabeli, co uczeń, który go zawiera.
Dlatego używamy tutaj komponentu.
Daje to dużą elastyczność w zmianie naszej warstwy klas, sposobu definiowania naszych klas w porównaniu z układem naszej bazy danych.
W tym rozdziale przyjrzymy się relacjom w NHibernate. Zwróćmy uwagę na to, jak możemy zrozumieć relacje w NHibernate. Najłatwiej jest pomyśleć o relacjach z perspektywy bazy danych.
Najpierw stworzymy nową aplikację, w której stworzymy pewne relacje między klientem a podmiotami zamawiającymi.
Pierwsza relacja, której przyjrzymy się, to klasyczny związek kolekcji.
Mamy klienta z kompletem zamówień.
Jest to relacja jeden do wielu i jest reprezentowana w bazie danych przez 2 tabele, aw tabeli zamówień znajduje się identyfikator klienta i mamy relację z kluczem obcym z powrotem do klienta.
Najpierw musimy stworzyć bazę danych oraz dwie tabele Customer and Order. Możesz to utworzyć, określając następującą kwerendę w Eksploratorze SQL Server.
USE [master]
GO
CREATE DATABASE [NHibernateDemo]
GO
USE [NHibernateDemo]
GO
CREATE TABLE [dbo].[Customer](
[Id] [uniqueidentifier] NOT NULL,
[FirstName] [nvarchar](100) NOT NULL,
[LastName] [nvarchar](100) NOT NULL,
[Points] [int] NULL, [HasGoldStatus] [bit] NULL,
[MemberSince] [date] NULL,
[CreditRating] [nchar](20) NULL,
[AverageRating] [decimal](18, 4) NULL,
[Street] [nvarchar](100) NULL,
[City] [nvarchar](100) NULL,
[Province] [nvarchar](100) NULL,
[Country] [nvarchar](100) NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
)
GO
CREATE TABLE [dbo].[Order](
[Id] [uniqueidentifier] NOT NULL,
[CustomerId] [uniqueidentifier] NULL,
[Ordered] [datetime] NULL,
[Shipped] [datetime] NULL,
[Street] [nvarchar](100) NULL,
[City] [nvarchar](100) NULL,
[Province] [nvarchar](100) NULL,
[Country] [nvarchar](100) NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
)
GO
Stworzy dwie tabele w bazie danych. Poniższy obraz przedstawia tabelę klientów.
Poniższy obraz przedstawia tabelę zamówień, w której można zobaczyć relację klucza obcego z powrotem do klienta.
Musimy zdefiniować parametry połączenia w app.config plik, tutaj jest implementacja pliku app.config.
<?xml version = "1.0" encoding = "utf-8" ?>
<configuration>
<connectionStrings>
<add name = "default" connectionString = "Data Source =
(localdb)\MSSQLLocalDB;Initial Catalog = NHibernateDemo;Integrated Security =
True;Connect Timeout = 30;Encrypt = False;TrustServerCertificate = False;
ApplicationIntent = ReadWrite;MultiSubnetFailover = False"/>
</connectionStrings>
</configuration>
Aby zainstalować NHibernate w aplikacji, uruchom następujące polecenie w oknie konsoli Menedżera NuGet.
install-package NHibernate
Aby skonfigurować konfigurację NHibernate, musimy zdefiniować konfigurację w hibernate.cfg.xml plik, jak pokazano w poniższym kodzie.
<xml version = "1.0" encoding = "utf-8" ?>
<hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2">
<session-factory>
<property name = "connection.connection_string_name">default</property>
<property name = "connection.driver_class">
NHibernate.Driver.SqlClientDriver
</property>
<property name = "dialect">
NHibernate.Dialect.MsSql2008Dialect
</property>
<property name = "show_sql">true</property>
</session-factory>
</hibernate-configuration>
W tym przykładzie będziemy pracować z dwiema klasami domeny, Klientem i Zamówieniem.
Oto implementacja pliku Customer.cs, w której mamy dwie klasy, jedna to klasa Customer, a druga to klasa Location, w której obiekt jest używany jako adres w klasie Customer.
using System;
using System.Text;
using Iesi.Collections.Generic;
namespace NHibernateDemo {
public class Customer {
public Customer() {
MemberSince = DateTime.UtcNow;
Orders = new HashedSet<Order>();
}
public virtual Guid Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual double AverageRating { get; set; }
public virtual int Points { get; set; }
public virtual bool HasGoldStatus { get; set; }
public virtual DateTime MemberSince { get; set; }
public virtual CustomerCreditRating CreditRating { get; set; }
public virtual Location Address { get; set; }
public virtual ISet<Order> Orders { get; set; }
public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; }
public override string ToString() {
var result = new StringBuilder();
result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus:
{4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating:
{8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince,
CreditRating, MemberSince.Kind, AverageRating);
result.AppendLine("\tOrders:");
foreach(var order in Orders) {
result.AppendLine("\t\t" + order);
}
return result.ToString();
}
}
public class Location {
public virtual string Street { get; set; }
public virtual string City { get; set; }
public virtual string Province { get; set; }
public virtual string Country { get; set; }
}
public enum CustomerCreditRating {
Excellent,
VeryVeryGood,
VeryGood,
Good,
Neutral,
Poor,
Terrible
}
}
Oto plik mapowania Customer.hbm.xml w którym klasa Customer jest mapowana na tabelę Customer.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Customer">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "FirstName"/>
<property name = "LastName"/>
<property name = "AverageRating"/>
<property name = "Points"/>
<property name = "HasGoldStatus"/>
<property name = "MemberSince" type = "UtcDateTime"/>
<property name = "CreditRating" type = "CustomerCreditRatingType"/>
<component name = "Address">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
</class>
</hibernate-mapping>
Mamy również klasę zamówienia i tutaj jest realizacja Order.cs plik.
using System; using Iesi.Collections.Generic;
namespace NHibernateDemo {
public class Order {
public virtual Guid Id { get; set; }
public virtual DateTime Ordered { get; set; }
public virtual DateTime? Shipped { get; set; }
public virtual Location ShipTo { get; set; }
public virtual Customer Customer { get; set; }
public override string ToString() {
return string.Format("Order Id: {0}", Id);
}
}
}
Relacja wiele do jednego
Musimy również zmapować klasę Order na tabelę Order w bazie danych, więc tutaj jest implementacja Order.hbm.xml plik.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Order" table = "`Order`">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "Ordered"/>
<property name = "Shipped"/>
<component name = "ShipTo">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<!--<many-to-one name = "Customer" column = "CustomerId" cascade =
"save-update"/>-->
</class>
</hibernate-mapping>
Relacja jeden do wielu
W tym miejscu przyjrzymy się relacji jeden do wielu, w tym przypadku między klientem a zamówieniami. Mamy tutaj naszego klienta, tworzymy nowego i widzisz, że kolekcja jest inicjowana następującą parą zamówień.
private static Customer CreateCustomer() {
var customer = new Customer {
FirstName = "John",
LastName = "Doe",
Points = 100,
HasGoldStatus = true,
MemberSince = new DateTime(2012, 1, 1),
CreditRating = CustomerCreditRating.Good,
AverageRating = 42.42424242,
Address = CreateLocation()
};
var order1 = new Order {
Ordered = DateTime.Now
};
customer.AddOrder(order1);
var order2 = new Order {
Ordered = DateTime.Now.AddDays(-1),
Shipped = DateTime.Now,
ShipTo = CreateLocation()
};
customer.AddOrder(order2);
return customer;
}
Stworzymy więc nowego klienta i następnie go zapiszemy, po zapisaniu znajdziemy ID i przeładujemy go w kolejnej sesji w metodzie Main, jak pokazano w poniższym programie.
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
Guid id;
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var newCustomer = CreateCustomer();
Console.WriteLine("New Customer:");
Console.WriteLine(newCustomer);
session.Save(newCustomer);
id = newCustomer.Id;
tx.Commit();
}
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var reloaded = session.Load<Customer>(id);
Console.WriteLine("Reloaded:");
Console.WriteLine(reloaded);
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
Oto kompletny Program.cs implementacja plików.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
Guid id;
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var newCustomer = CreateCustomer();
Console.WriteLine("New Customer:");
Console.WriteLine(newCustomer);
session.Save(newCustomer);
id = newCustomer.Id;
tx.Commit();
}
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var reloaded = session.Load<Customer>(id);
Console.WriteLine("Reloaded:");
Console.WriteLine(reloaded);
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Customer CreateCustomer() {
var customer = new Customer {
FirstName = "John",
LastName = "Doe",
Points = 100,
HasGoldStatus = true,
MemberSince = new DateTime(2012, 1, 1),
CreditRating = CustomerCreditRating.Good,
AverageRating = 42.42424242,
Address = CreateLocation()
};
var order1 = new Order {
Ordered = DateTime.Now
};
customer.AddOrder(order1);
var order2 = new Order {
Ordered = DateTime.Now.AddDays(-1),
Shipped = DateTime.Now,
ShipTo = CreateLocation()
};
customer.AddOrder(order2);
return customer;
}
private static Location CreateLocation() {
return new Location {
Street = "123 Somewhere Avenue",
City = "Nowhere",
Province = "Alberta",
Country = "Canada"
};
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x =&ht; {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10; x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Po uruchomieniu tej aplikacji zobaczysz następujące dane wyjściowe.
New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
CreditRating: Good
AverageRating: 42.42424242
Orders:
Order Id: 00000000-0000-0000-0000-000000000000
Order Id: 00000000-0000-0000-0000-000000000000
Reloaded:
John Doe (9b0fcf10-83f6-4f39-bda5-a5b800ede2ba)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Press <ENTER> to exit...
Jak widać, początkowo klient ma 2 zamówienia, ale kiedy go przeładujemy, nie ma żadnych zamówień do wyświetlenia. Jeśli spojrzysz nacustomer.hbm.xmlwidać tutaj, że nie odwzorowujemy faktycznej kolekcji zamówień. Więc NHibernate nic o tym nie wie. Przejdźmy dalej i dodajmy to.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2"
assembly = "NHibernateDemo" namespace = "NHibernateDemo">
<class name = "Customer">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "FirstName"/>
<property name = "LastName"/>
<property name = "AverageRating"/>
<property name = "Points"/>
<property name = "HasGoldStatus"/>
<property name = "MemberSince" type = "UtcDateTime"/>
<property name = "CreditRating" type = "CustomerCreditRatingType"/>
<component name = "Address">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<set name = "Orders" table = "`Order`">
<key column = "CustomerId"/>
<one-to-many class = "Order"/>
</set>
</class>
</hibernate-mapping>
To jest zestaw, a nazwa tej kolekcji to „Zamówienia”, która jest przechowywana w tabeli o nazwie order. Musimy podać klucz, który jest nazwą klucza obcego lub znaleźć zamówienia. Te zamówienia są identyfikowane lub należą do klienta za pomocą identyfikatora klienta. A potem muszę zauważyć, że jest to relacja jeden do wielu i dotyczy ona klasy zamówienia.
Musimy również nieco zmienić metodę Main, zapisując nowe zamówienia klientów w bazie danych, jak również pokazano w poniższym programie.
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
Guid id;
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var newCustomer = CreateCustomer();
Console.WriteLine("New Customer:");
Console.WriteLine(newCustomer);
session.Save(newCustomer);
foreach (var order in newCustomer.Orders) {
session.Save(order);
}
id = newCustomer.Id;
tx.Commit();
}
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var reloaded = session.Load<Customer>(id);
Console.WriteLine("The orders were ordered by: ");
foreach (var order in reloaded.Orders) {
Console.WriteLine(order.Customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine();
}
Określiliśmy również, który klient zamówił ten konkretny produkt. Musimy więc stworzyć relację typu „wiele do jednego”, aby powiązać to zamówienie z powrotem do tego klienta.
Więc przejdźmy do Order.hbm.xml plik i dodaj wiele do jednego, a następnie nazwij pole klienta i kolumnę z identyfikatorem klienta.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Order" table = "`Order`">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "Ordered"/>
<property name = "Shipped"/>
<component name = "ShipTo">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<many-to-one name = "Customer" column = "CustomerId"/>
</class>
</hibernate-mapping>
Uruchommy ponownie tę aplikację, a teraz zobaczysz następujące dane wyjściowe.
New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
CreditRating: Good
AverageRating: 42.42424242
Orders:
Order Id: 00000000-0000-0000-0000-000000000000
Order Id: 00000000-0000-0000-0000-000000000000
Reloaded:
John Doe (660a6f29-650e-4380-99e0-a5b800febbde)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3
Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3
The orders were ordered by:
John Doe (660a6f29-650e-4380-99e0-a5b800febbde)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3
Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3
John Doe (660a6f29-650e-4380-99e0-a5b800febbde)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3
Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3
Press <ENTER> to exit...
W tym rozdziale zajmiemy się sposobem reprezentowania kolekcji. Istnieją różne typy kolekcji, z których możemy korzystać w ramach NHibernate, takie jak:
- Lists
- Sets
- Bags
Teraz, z perspektywy .NET, mamy do czynienia z listami lub bardzo prostymi strukturami danych, listami, słownikami. NET nie ma wielu różnych typów kolekcji. Dlaczego więc NHibernate potrzebuje wszystkich tych różnych typów? Naprawdę wraca do bazy danych.
Lista
Lista to uporządkowany zbiór elementów, które niekoniecznie są unikalne.
Możemy to zmapować za pomocą IList <T>.
Więc chociaż konwencjonalnie możemy mieć listę adresów, a z punktu widzenia aplikacji wiemy, że elementy są unikalne, nic na liście nie stoi na przeszkodzie, aby wstawić zduplikowane elementy na tej liście.
Zestaw
Zestaw to nieuporządkowana kolekcja unikatowych elementów. Jeśli spróbujesz wstawić 2 zduplikowane elementy do zestawu, zgłosi wyjątek.
Nie ma w tym nic konkretnego w NHibernate.
To po prostu wygodny sposób na implementację zestawu ogólnego. Jeśli korzystasz z platformy .NET 4, możesz użyć nowej wersjiHashSet <T> aby je przedstawić, ale w większości zastosowań NHibernate reprezentujemy to, że jest to ISet.
Jest to nieuporządkowane, jeśli wyciągniesz listę adresów z bazy danych lub listy zamówień, nie wiesz, w jakiej kolejności przychodzą, chyba że wprowadzisz określoną klauzulę Order by.
Ogólnie rzecz biorąc, dane, które pobierasz z bazy danych, to zbiory.
To wyjątkowe kolekcje elementów, które są nieuporządkowane.
Torba
Inną popularną kolekcją, którą zobaczymy w świecie baz danych, jest worek, który jest jak zestaw, z wyjątkiem tego, że może mieć zduplikowane elementy.
W świecie .NET reprezentuje to IList.
Zestawy są prawdopodobnie najpopularniejsze, ale w zależności od zastosowania zobaczysz również listy i torby. Rzućmy okiem na poniższecustomer.hbm.xml plik z ostatniego rozdziału, w którym zdefiniowane są zlecenia Set.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Customer">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "FirstName"/>
<property name = "LastName"/>
<property name = "AverageRating"/>
<property name = "Points"/>
<property name = "HasGoldStatus"/>
<property name = "MemberSince" type = "UtcDateTime"/>
<property name = "CreditRating" type = "CustomerCreditRatingType"/>
<component name = "Address">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<set name = "Orders" table = "`Order`">
<key column = "CustomerId"/>
<one-to-many class = "Order"/>
</set>
</class>
</hibernate-mapping>
Jak widać, zmapowaliśmy zbiór zamówień jako zestaw. Pamiętaj, że zestaw to nieuporządkowana kolekcja unikatowych elementów.
Teraz, jeśli spojrzysz na klasę Customer, zobaczysz, że właściwość Orders jest zdefiniowana za pomocą ISet, jak pokazano w poniższym programie.
public virtual ISet<Order> Orders { get; set; }
Teraz, gdy ta aplikacja jest uruchomiona, zobaczysz następujące dane wyjściowe.
New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
CreditRating: Good
AverageRating: 42.42424242
Orders:
Order Id: 00000000-0000-0000-0000-000000000000
Order Id: 00000000-0000-0000-0000-000000000000
Reloaded:
John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6
Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7
The orders were ordered by:
John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6
Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7
John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6
Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7
Press <ENTER> to exit...
Jeśli elementy w kolekcji nie musiałyby być unikalne, gdybyś mógł mieć wiele zamówień z tym samym kluczem podstawowym występującym wiele razy w tej kolekcji, lepiej byłoby to odwzorować jako paczkę, jak pokazano w poniższym programie.
<bag name = "Orders" table = "`Order`">
<key column = "CustomerId"/>
<one-to-many class = "Order"/>
</bag>
Teraz, jeśli uruchomisz tę aplikację, otrzymasz wyjątek, ponieważ jeśli przyjrzymy się klasie klienta, zauważysz, że zamówienia są oznaczone jako ISet w kodzie C #.
Będziemy więc musieli zmienić to na IList, a następnie tutaj musielibyśmy zmienić z HashSet na List w konstruktorze.
public class Customer {
public Customer() {
MemberSince = DateTime.UtcNow;
Orders = new List<Order>();
}
public virtual Guid Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual double AverageRating { get; set; }
public virtual int Points { get; set; }
public virtual bool HasGoldStatus { get; set; }
public virtual DateTime MemberSince { get; set; }
public virtual CustomerCreditRating CreditRating { get; set; }
public virtual Location Address { get; set; }
public virtual IList<Order> Orders { get; set; }
public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; }
public override string ToString() {
var result = new StringBuilder();
result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus:
{4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating:
{8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince,
CreditRating, MemberSince.Kind, AverageRating); result.AppendLine("\tOrders:");
foreach(var order in Orders) {
result.AppendLine("\t\t" + order);
}
return result.ToString();
}
}
Po uruchomieniu aplikacji zobaczysz to samo zachowanie. Ale teraz możemy mieć kolejność występującą wiele razy w tej samej kolekcji.
John Doe (00000000-0000-0000-0000-000000000000)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
CreditRating: Good
AverageRating: 42.42424242
Orders:
Order Id: 00000000-0000-0000-0000-000000000000
Order Id: 00000000-0000-0000-0000-000000000000
Reloaded:
John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286
Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287
The orders were ordered by:
John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286
Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287
John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286
Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287
Press <ENTER> to exit...
W tym rozdziale omówimy, jak korzystać z funkcji Cascade. Jeśli masz zestaw lub kolekcję elementów lub relację między dwiema klasami, takimi jak nasz klient i zamówienie, i masz relację klucza obcego. Jeśli domyślnie usuniemy klienta, NHibernate nie zrobi nic z obiektami podrzędnymi, więc te, które należą do tego klienta, a my możemy być osieroconymi zamówieniami.
Moglibyśmy również naruszać ograniczenia klucza obcego, więc możemy użyć pojęcia kaskady.
Domyślnie NHibernate nie wykonuje kaskadowych operacji na obiektach podrzędnych.
Powodem tego jest to, że możesz mieć relacje, takie jak klient mający domyślny adres wysyłki, a adres wysyłki jest udostępniany wielu różnym klientom.
Więc nie chcesz kaskadować tej relacji koniecznie, ponieważ inni klienci wciąż się do niej odnoszą.
Zatem cała koncepcja kaskad polega na tym, aby powiedzieć NHibernate, jak ma postępować z bytami podrzędnymi.
Istnieją różne opcje kaskadowania, które są następujące -
none - co jest domyślne i oznacza brak kaskadowania.
all - który będzie kaskadowo zapisywał, aktualizował i usuwał.
save-update - będzie kaskadować, zapisywać i aktualizować.
delete - będzie usuwać kaskadowo.
all-delete-orphan - jest to specjalny, który jest dość często używany i jest taki sam jak All Except, jeśli znajdzie wiersze Delete-orphan, usunie również te wiersze.
Możesz określić wartość domyślną w pliku hbm.xml plik, dzięki czemu można podać domyślną kaskadę dla tego elementu mapowania Hibernate lub można ją również określić dla określonych kolekcji i relacji, takich jak wiele do jednego.
Rzućmy okiem na proste przykładowe kaskady, naprawmy problem w programie, w którym musimy ręcznie kaskadować zapisywanie do zamówień, jak pokazano w poniższym kodzie.
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var newCustomer = CreateCustomer();
Console.WriteLine("New Customer:");
Console.WriteLine(newCustomer);
session.Save(newCustomer);
foreach (var order in newCustomer.Orders) {
session.Save(order);
}
id = newCustomer.Id;
tx.Commit();
}
Na powyższym fragmencie kodu widać, że ręcznie zapisujemy wszystkie zamówienia dla klienta. Teraz usuńmy ręczny kod kaskadowy, w którym zapisane są wszystkie zamówienia.
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var newCustomer = CreateCustomer();
Console.WriteLine("New Customer:");
Console.WriteLine(newCustomer);
session.Save(newCustomer);
id = newCustomer.Id;
tx.Commit();
}
Musimy określić opcję kaskady w customer.hbm.xml.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Customer">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "FirstName"/>
<property name = "LastName"/>
<property name = "AverageRating"/>
<property name = "Points"/>
<property name = "HasGoldStatus"/>
<property name = "MemberSince" type = "UtcDateTime"/>
<property name = "CreditRating" type = "CustomerCreditRatingType"/>
<component name = "Address">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<set name = "Orders" table = "`Order`" cascade = "all-delete-orphan">
<key column = "CustomerId"/>
<one-to-many class = "Order"/>
</set>
</class>
</hibernate-mapping>
Teraz zamówienia w całości należą do klienta. Więc jeśli klienci zostaliby usunięci z bazy danych, nasza aplikacja chciałaby usunąć wszystkie te zamówienia, w tym te, które mogły zostać osierocone.
Skończy się na usunięciu. W ten sposób powie usuń z tabeli zamówień, gdzie identyfikator klienta jest klientem, którego usuwasz.
Możesz więc faktycznie kaskadować te usunięcia. Tak więc zAll, będzie zapisywać, aktualizować i usuwać.
Teraz po uruchomieniu tej aplikacji zobaczysz następujące dane wyjściowe.
New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
CreditRating: Good
AverageRating: 42.42424242
Orders:
Order Id: 00000000-0000-0000-0000-000000000000
Order Id: 00000000-0000-0000-0000-000000000000
Reloaded:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
The orders were ordered by:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
Press <ENTER> to exit...
Jak widać, usunęliśmy kod z programu, który ręcznie kaskadował i nasza aplikacja nadal działa.
Więc w zależności od twojego związku, możesz chcieć to kaskadować. Przyjrzyjmy się teraz innej relacji kaskadowej. Chodźmy doOrder.hbm.xml file i możemy kaskadować tę relację wiele do jednego.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Order" table = "`Order`">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "Ordered"/>
<property name = "Shipped"/>
<component name = "ShipTo">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<many-to-one name = "Customer" column = "CustomerId" cascade = "save-update"/>
</class>
</hibernate-mapping>
Więc jeśli utworzymy nowe zamówienie i jest do niego dołączony nowy klient, i powiemy, że zapiszemy to zamówienie, możemy chcieć to kaskadowo. Ale jedna rzecz, której prawdopodobnie nie chcielibyśmy robić, to usuwanie zamówienia w celu usunięcia odpowiedniego klienta.
Więc tutaj chcielibyśmy zrobić aktualizację zapisu, więc korzystając z aktualizacji zapisu, wszystkie zapisy lub aktualizacje będą kaskadowane dla tego klienta. Tak więc, jeśli zdobędziemy nowego klienta lub zmienimy klienta, będzie to kaskadowo. Jeśli jest to usunięcie, nie usunie tego z bazy danych.
Po ponownym uruchomieniu naszej aplikacji wszystko nadal działa zgodnie z oczekiwaniami.
New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
CreditRating: Good
AverageRating: 42.42424242
Orders:
Id: 00000000-0000-0000-0000-000000000000
Order Id: 00000000-0000-0000-0000-000000000000
Reloaded:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
The orders were ordered by:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
Press <ENTER> to exit...
Teraz powinieneś przyjrzeć się swojej aplikacji, pamiętaj, że wartością domyślną jest Brak i musisz pomyśleć o swoich encjach i relacjach między nimi, aby określić odpowiednie kaskady dla każdego z twoich encji, a także dla każdej relacji w tej bazie danych.
W tym rozdziale zajmiemy się funkcją leniwego ładowania. Domyślnie jest to zupełnie inna koncepcja, a NHibernate nie ma leniwego ładowania, na przykład jeśli ładujesz klienta, nie załaduje wszystkich zamówień.
Kolekcja zamówień zostanie załadowana na żądanie.
Każde skojarzenie, niezależnie od tego, czy jest to wiele do jednego, czy kolekcja, jest domyślnie ładowane z opóźnieniem, wymaga rozszerzenia Open ISession.
Jeśli zamknąłeś sesję lub zatwierdziłeś transakcję, możesz uzyskać wyjątek z opóźnionym ładowaniem, który nie może pobrać tych dodatkowych obiektów.
Musisz uważać na leniwe ładowanie i ile danych faktycznie potrzebujesz.
Możesz wyłączyć leniwe ładowanie dla całego skojarzenia lub ustawić lazy równa się fałsz lub możesz również określić strategię pobierania.
Tutaj jest Program.cs implementacja plików.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
Guid id;
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var newCustomer = CreateCustomer();
Console.WriteLine("New Customer:");
Console.WriteLine(newCustomer);
session.Save(newCustomer);
id = newCustomer.Id;
tx.Commit();
}
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var reloaded = session.Load<Customer>(id);
Console.WriteLine("Reloaded:");
Console.WriteLine(reloaded);
Console.WriteLine("The orders were ordered by: ");
foreach (var order in reloaded.Orders) {
Console.WriteLine(order.Customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Customer CreateCustomer() {
var customer = new Customer {
FirstName = "John",
LastName = "Doe",
Points =100,
HasGoldStatus = true,
MemberSince = new DateTime(2012, 1, 1),
CreditRating = CustomerCreditRating.Good,
AverageRating = 42.42424242,
Address = CreateLocation()
};
var order1 = new Order { Ordered = DateTime.Now };
customer.AddOrder(order1);
var order2 = new Order {
Ordered = DateTime.Now.AddDays(-1),
Shipped = DateTime.Now,
ShipTo = CreateLocation()
};
customer.AddOrder(order2); return customer;
}
private static Location CreateLocation() {
return new Location {
Street = "123 Somewhere Avenue",
City = "Nowhere",
Province = "Alberta",
Country = "Canada"
};
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect<();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Aby to zrozumieć, uruchommy aplikację i przyjrzyjmy się NHibernate Profiler.
Jak widać, mamy opcję Wybierz od klienta, podaną konkretnym identyfikatorem klienta, a także inną tabelę Wybierz z zamówień, kiedy faktycznie uzyskuje dostęp do kolekcji tego klienta.
Mamy więc 2 wycieczki do bazy danych. Czasami chcielibyśmy to zoptymalizować. Aby to zrobić, przejdźmy docustomer.hbm.xml plik, dodaj strategię pobierania i poproś o wykonanie operacji pobierania z dołączeniem.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Customer">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "FirstName"/>
<property name = "LastName"/>
<property name = "AverageRating"/>
<property name = "Points"/>
<property name = "HasGoldStatus"/>
<property name = "MemberSince" type = "UtcDateTime"/>
<property name = "CreditRating" type = "CustomerCreditRatingType"/>
<component name = "Address">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<set name = "Orders" table = "`Order`" cascade = "all-delete-orphan"
fetch = "join">
<key column = "CustomerId"/>
<one-to-many class = "Order"/>
</set>
</class>
</hibernate-mapping>
Jak widać, nie zmieniliśmy żadnego kodu w naszej aplikacji, właśnie dodaliśmy strategię pobierania w customer.hbm.xml. Uruchommy ponownie tę aplikację, nadal zachowuje się dokładnie tak samo. Spójrzmy na NHibernate Profiler.
Wcześniej program miał dwie rundy do bazy danych, teraz ma tylko jedną, a to dlatego, że wykonuje tutaj lewe sprzężenie zewnętrzne.
Widzimy, że wykonuje lewe zewnętrzne połączenie między tabelą klientów a tabelą zamówień na podstawie identyfikatora klienta, a zatem jest w stanie załadować wszystkie te informacje naraz.
Zapisaliśmy 1 podróż w obie strony do bazy danych.
Wadą jest to, że informacje o kliencie zostaną zduplikowane w obu wierszach i tak działa lewe sprzężenie zewnętrzne SQL.
Tak więc dzięki strategii pobierania pobieramy nieco więcej danych i oszczędzamy podróż w obie strony.
Możesz to również zrobić na poziomie zapytania, więc przejdźmy do Program.cs plik i spójrz na prostszy, ponownie załadowany przykład.
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
//var query = from customer in session.Query<Customer>()
// select customer;
//var reloaded = query.Fetch(x => x.Orders).ToList();
var reloaded = session.Load<Customer>(id);
Console.WriteLine("Reloaded:");
Console.WriteLine(reloaded);
Console.WriteLine("The orders were ordered by: ");
foreach (var order in reloaded.Orders) {
Console.WriteLine(order.Customer);
}
tx.Commit();
}
Tutaj robimy ładunek przez klienta. Teraz zmieńmy to na zapytanie i użyjemy zapytania łączącego, jak pokazano w poniższym kodzie.
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var query = from customer in session.Query<Customer>()
where customer.Id == id select customer;
var reloaded = query.Fetch(x => x.Orders).ToList().First();
Console.WriteLine("Reloaded:");
Console.WriteLine(reloaded);
tx.Commit();
}
Usuńmy również strategię pobierania z customer.hbm.xml plik.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Customer">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "FirstName"/>
<property name = "LastName"/>
<property name = "AverageRating"/>
<property name = "Points"/>
<property name = "HasGoldStatus"/>
<property name = "MemberSince" type = "UtcDateTime"/>
<property name = "CreditRating" type = "CustomerCreditRatingType"/>
<component name = "Address">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<set name = "Orders" table = "`Order`" cascade = "all-delete-orphan">
<key column = "CustomerId"/>
<one-to-many class = "Order"/>
</set>
</class>
</hibernate-mapping>
Uruchommy ponownie tę aplikację, a zobaczysz następujące dane wyjściowe.
New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
CreditRating: Good
AverageRating: 42.42424242
Orders:
Order Id: 00000000-0000-0000-0000-000000000000
Order Id: 00000000-0000-0000-0000-000000000000
Reloaded:
John Doe (6ebacd17-f9ba-4ad8-9817-a5bb01112a5a)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: 16a6596b-d56e-41c7-9681-a5bb01112a60
Order Id: d41d615b-0f21-4032-81db-a5bb01112a61
Press <ENTER> to exit...
Teraz spójrzmy na NHibernate Profiler, widzisz, że mamy to niecierpliwe pobieranie dołączenia ponownie, ale tym razem jest ono oparte na zapytaniu.
W tym rozdziale zajmiemy się inną funkcją, którą są relacje odwrotne. Jest to zabawna opcja, którą zobaczysz w kolekcji, która jest odwrotnie równa true, a także dezorientuje wielu programistów. Porozmawiajmy więc o tej opcji. Aby to zrozumieć, naprawdę musisz pomyśleć o modelu relacyjnym. Powiedzmy, że masz dwukierunkowe skojarzenia wykorzystujące pojedynczy klucz obcy.
Z relacyjnego punktu widzenia masz jeden klucz obcy, który reprezentuje zarówno klienta do zamówienia, jak i zamówienia do klienta.
Z modelu OO masz jednokierunkowe skojarzenia korzystające z tych odniesień.
Nic nie mówi, że dwa jednokierunkowe powiązania reprezentują to samo dwukierunkowe powiązanie w bazie danych.
Problem polega na tym, że NHibernate nie ma wystarczających informacji, aby to wiedzieć customer.orders i order.customer reprezentują tę samą relację w bazie danych.
Musimy zapewnić inverse equals true jako wskazówka, dzieje się tak, ponieważ jednokierunkowe asocjacje używają tych samych danych.
Jeśli spróbujemy zapisać te relacje, które mają do nich 2 odniesienia, NHibernate spróbuje zaktualizować to odniesienie dwukrotnie.
W rzeczywistości wykona dodatkową podróż w obie strony do bazy danych, a także będzie mieć 2 aktualizacje tego klucza obcego.
Odwrotność równa się prawdzie mówi NHibernate, którą stronę związku należy zignorować.
Kiedy zastosujesz go po stronie kolekcji, NHibernate zawsze zaktualizuje klucz obcy z drugiej strony, od strony obiektu podrzędnego.
Wtedy mamy tylko jedną aktualizację tego klucza obcego i nie mamy dodatkowych aktualizacji tych danych.
To pozwala nam zapobiegać takim zduplikowanym aktualizacjom klucza obcego, a także pomaga nam zapobiegać naruszeniom klucza obcego.
Rzućmy okiem na plik customer.cs plik, w którym zobaczysz plik AddOrderi chodzi o to, że mamy teraz ten wskaźnik wsteczny od zamówienia do klienta i trzeba go ustawić. Więc kiedy zamówienie jest dodawane do klienta, jego tylny wskaźnik jest ustawiany, w przeciwnym razie byłby zerowy, więc potrzebujemy tego, aby utrzymać to poprawnie połączone w grafie obiektów.
using System;
using System.Text;
using Iesi.Collections.Generic;
namespace NHibernateDemo {
public class Customer {
public Customer() {
MemberSince = DateTime.UtcNow; Orders = new HashedSet<Order>();
}
public virtual Guid Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual double AverageRating { get; set; }
public virtual int Points { get; set; }
public virtual bool HasGoldStatus { get; set; }
public virtual DateTime MemberSince { get; set; }
public virtual CustomerCreditRating CreditRating { get; set; }
public virtual Location Address { get; set; }
public virtual ISet<Order> Orders { get; set; }
public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; }
public override string ToString() {
var result = new StringBuilder();
result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus:
{4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating:
{8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince,
CreditRating, MemberSince.Kind, AverageRating);
result.AppendLine("\tOrders:");
foreach(var order in Orders) {
result.AppendLine("\t\t" + order);
}
return result.ToString();
}
}
public class Location {
public virtual string Street { get; set; }
public virtual string City { get; set; }
public virtual string Province { get; set; }
public virtual string Country { get; set; }
}
public enum CustomerCreditRating {
Excellent,
VeryVeryGood,
VeryGood,
Good,
Neutral,
Poor,
Terrible
}
}
Tutaj jest Program.cs implementacja plików.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
Guid id;
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var newCustomer = CreateCustomer();
Console.WriteLine("New Customer:");
Console.WriteLine(newCustomer);
session.Save(newCustomer);
id = newCustomer.Id;
tx.Commit();
}
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var query = from customer in session.Query<Customer>() where
customer.Id == id select customer;
var reloaded = query.Fetch(x => x.Orders).ToList().First();
Console.WriteLine("Reloaded:"); Console.WriteLine(reloaded);
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Customer CreateCustomer() {
var customer = new Customer {
FirstName = "John",
LastName = "Doe",
Points = 100,
HasGoldStatus = true,
MemberSince = new DateTime(2012, 1, 1),
CreditRating = CustomerCreditRating.Good,
AverageRating = 42.42424242,
Address = CreateLocation()
};
var order1 = new Order { Ordered = DateTime.Now };
customer.AddOrder(order1); var order2 = new Order {
Ordered = DateTime.Now.AddDays(-1),
Shipped = DateTime.Now,
ShipTo = CreateLocation()
};
customer.AddOrder(order2);
return customer;
}
private static Location CreateLocation() {
return new Location {
Street = "123 Somewhere Avenue",
City = "Nowhere",
Province = "Alberta",
Country = "Canada"
};
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Ma zamiar zapisać to w bazie danych, a następnie załadować ponownie. Teraz uruchommy twoją aplikację, otwórzmy NHibernate Profiler i zobaczmy, jak ją zapisał.
Zauważysz, że mamy 3 grupy zdań. Pierwsza wstawia klienta, a identyfikator klienta to Guid, który jest podświetlony. Drugie oświadczenie jest wstawiane do tabeli zamówień.
Zauważysz, że jest tam ustawiony ten sam identyfikator klienta, więc ustaw ten klucz obcy. Ostatnią instrukcją jest aktualizacja, która ponownie zaktualizuje klucz obcy do tego samego identyfikatora klienta.
Problem polega na tym, że klient ma zamówienia, a zamówienia - klient, nie ma mowy, żebyśmy nie powiedzieli NHibernate, że to w rzeczywistości ta sama relacja. Sposób, w jaki to robimy, jest taki, że odwrotność równa się prawda.
Więc przejdźmy do naszego customer.hbm.xml mapping file i ustaw wartość odwrotną równą true, jak pokazano w poniższym kodzie.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Customer">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "FirstName"/>
<property name = "LastName"/>
<property name = "AverageRating"/>
<property name = "Points"/>
<property name = "HasGoldStatus"/>
<property name = "MemberSince" type = "UtcDateTime"/>
<property name = "CreditRating" type = "CustomerCreditRatingType"/>
<component name = "Address">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<set name = "Orders" table = "`Order`" cascade = "all-delete-orphan"
inverse = "true">
<key column = "CustomerId"/>
<one-to-many class = "Order"/>
</set>
</class>
</hibernate-mapping>
Podczas zapisywania zleceń ustawi ten klucz obcy od strony zlecenia. Teraz uruchommy ponownie tę aplikację i otwórzmy profiler NHibernate.
Jeśli spojrzymy na to, jak są one wstawiane, otrzymujemy wkładkę u klienta i wkładkę do zamówień, ale nie mamy tej zduplikowanej aktualizacji klucza obcego, ponieważ jest on aktualizowany podczas zapisywania zamówień.
Teraz powinieneś zauważyć, że jeśli masz tylko jednokierunkowe skojarzenie i to zestaw utrzymuje tę zależność, to jeśli odwrócisz odwrotność równa się prawdzie, ten klucz obcy nigdy nie zostanie ustawiony, a te elementy nigdy nie będą miały swojego klucze obce ustawione w bazie danych.
Jeśli spojrzysz na relację wiele do jednego w Order.hbm.xml file i szukasz inverse, w rzeczywistości nie ma on atrybutu inverse.
Zawsze jest ustawiany na podstawie elementu podrzędnego, ale jeśli masz kolekcję wiele do wielu, możesz ustawić ją z dowolnej strony.
W tym rozdziale omówimy, jak działają funkcje Load and Get i jak możemy ich używać. Są to dwa bardzo podobne interfejsy API dostarczane przezISession do ładowania obiektu za pomocą klucza podstawowego.
Get - zwróci obiekt lub wartość null.
Load - zwróci obiekt lub wyrzuci plik ObjectNotFoundException.
Dlaczego mamy te dwa różne interfejsy API?
Załaduj
Dzieje się tak, ponieważ Load może znacznie efektywniej zoptymalizować wycieczki w obie strony do bazy danych.
Load w rzeczywistości zwraca obiekt proxy i nie musi uzyskiwać dostępu do bazy danych bezpośrednio po wywołaniu wywołania Load.
Podczas uzyskiwania dostępu do tego serwera proxy obiekt nie znajduje się w bazie danych, może w tym momencie zgłosić wyjątek ObjectNotFoundException.
Dostać
I odwrotnie, w przypadku Get z powodu ograniczeń środowiska CLR lub Common Language Runtime a NHibernate musi natychmiast przejść do bazy danych, sprawdzić, czy obiekty tam są i zwrócić wartość null, jeśli jej nie ma.
Nie ma opcji obiektu opóźniania tego pobierania, tego powrotu do bazy danych do późniejszego czasu, ponieważ nie może zwrócić obiektu proxy, a to zamieniło ten obiekt proxy na wartość null, gdy użytkownik faktycznie uzyskuje do niego dostęp.
Rzućmy okiem na prosty przykład, w którym zobaczysz, jak są one faktycznie używane i jaka jest różnica między Get i Load. Będziemy kontynuować te same klasy domenyCustomers i Orders i podobnie te same pliki mapowań z poprzedniego rozdziału.
W tym przykładzie najpierw użyjemy Get, jak pokazano w poniższym programie.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var id1 = Guid.Parse("4e97c816-6bce-11e1-b095-6cf049ee52be");
var id2 = Guid.Parse("AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE");
var customer1 = session.Get<Customer>(id1);
Console.WriteLine("Customer1 data");
Console.WriteLine(customer1);
var customer2 = session.Get<Customer>(id2);
Console.WriteLine("Customer2 data");
Console.WriteLine(customer2);
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Jak widać, mamy dwa GuidID, pierwszy z nich to dobry identyfikator, to identyfikator klienta, o którym wiemy, że znajduje się w bazie danych. Podczas gdy drugiego identyfikatora nie ma w bazie danych. Oba te identyfikatory są przekazywane jako parametr doGet() metoda, a następnie wynik jest drukowany na konsoli.
Kiedy powyższy kod zostanie skompilowany i wykonany, zobaczysz następujące dane wyjściowe.
Customer1 data
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 4/4/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
Customer2 data
Press <ENTER> to exit...
Jak widać, drukowane są dane Customer1, ale dane Customer2 są puste, to znaczy, że rekord Customer2 nie jest dostępny w bazie danych.
Kiedy ponownie uruchomisz aplikację, możemy wstawić punkt przerwania przed instrukcją commit, a następnie przyjrzyjmy się obu klientom w oknie Watch.
Jak widać, dane Customer1 są dostępne, a Customer2 ma wartość NULL, a typ to NHibernateDemo.Customer dla obu.
Teraz użyjmy metody Load zamiast Get w tym samym przykładzie, jak pokazano w poniższym kodzie.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var id1 = Guid.Parse("4e97c816-6bce-11e1-b095-6cf049ee52be");
var id2 = Guid.Parse("AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE");
var customer1 = session.Load<Customer>(id1);
Console.WriteLine("Customer1 data");
Console.WriteLine(customer1);
var customer2 = session.Load<Customer>(id2);
Console.WriteLine("Customer2 data");
Console.WriteLine(customer2);
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Teraz uruchommy ten przykład, a zobaczysz, że zgłaszany jest następujący wyjątek, jak widać na zrzucie ekranu.
Teraz, jeśli spojrzysz na okno Watch, zobaczysz, że typ to customer proxy dla obu obiektów. Te same dane dotyczące klienta 1 są również widoczne w oknie konsoli.
Customer1 data
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 4/4/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
Customer2 data
W tym rozdziale zajmiemy się innym popularnym interfejsem API, którego ludzie będą używać, to dostawca NHibernate LINQ. Jego dostęp za pośrednictwem metody rozszerzenia w ISession, a podpis to plikQuery <T>. Podczas korzystania z LINQ istnieją dwa typy składni -
- Składnia łańcuchów zapytań
- Składnia zapytania ze zrozumieniem
Składnia łańcuchów zapytań
Możesz uzyskać dostęp do dowolnego rekordu z bazy danych, używając składni łańcucha metod, jak pokazano w poniższym programie.
var customer = session.Query<Customer>() .Where(c => c.FirstName == "Laverne")
Widzisz, że mamy zapytanie, a także klauzulę WHERE, możesz mieć dodatkowe klauzule WHERE i podobnie klauzulę select.
Jest to standardowa składnia łańcucha metod, której można używać w normalnym LINQ.
LINQ to Objects lub LINQ to SQL, dowolny z innych dostawców LINQ, z którymi możesz być zaznajomiony.
Rzućmy okiem na prosty przykład, w którym odzyskamy klienta, który ma na imię Laverne. Obecnie istnieje możliwość, że możemy mieć więcej niż jednego klienta o imieniu Laverne, więc pobieramy tylko pierwszego.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customer = session.Query<Customer>()
.Where(c => c.FirstName == "Laverne").First();
Console.WriteLine(customer);
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Teraz, gdy powyższy kod zostanie skompilowany i wykonany, zobaczysz następujące dane wyjściowe.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 4/4/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
Press <ENTER> to exit...
Składnia zapytania ze zrozumieniem
Istnieje również składnia zapytań, która bardziej przypomina SQL przy użyciu słów kluczowych from, where i select.
Przyjrzyjmy się więc temu samemu przykładowi, ale tym razem używamy składni poleceń LINQ, która wygląda bardziej jak SQL, jak pokazano w poniższym programie.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customer = (from c in session.Query<Customer>()
where c.FirstName == "Laverne" select c).First();
Console.WriteLine(customer);
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Teraz uruchommy ponownie tę aplikację, a zobaczysz następujące dane wyjściowe.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 4/4/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
Press <ENTER> to exit...
Spójrzmy na inny przykład, w którym pobieramy wszystkich klientów, których imię zaczyna się na literę H.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customers = session.Query<Customer>() .Where(c =<
c.FirstName.StartsWith("H"));
foreach (var customer in customers.ToList()) {
Console.WriteLine(customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Podobnie składnia rozumienia zapytania będzie wyglądać jak w poniższym programie.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customers = from c in session.Query<Customer>()
where c.FirstName.StartsWith("H") select c;
foreach (var customer in customers.ToList()) {
Console.WriteLine(customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Uruchommy ponownie tę aplikację, a zobaczysz wszystkich klientów, których imię zaczyna się na literę H.
Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 12/3/2010 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be
Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be)
Points: 56
HasGoldStatus: False
MemberSince: 10/20/2008 12:00:00 AM (Utc)
CreditRating: Terrible
AverageRating: 0
Orders:
Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be
Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be)
Points: 82
HasGoldStatus: False
MemberSince: 4/10/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be
Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be
Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be
Press <ENTER> to exit...
W tym rozdziale zajmiemy się Hibernate Query Language. HQL jest współdzielony przez Hibernate i NHibernate w Javie.
Jest to najstarszy mechanizm zapytań wraz z Criteria.
Zostało zaimplementowane bardzo wcześnie i jest to zapytanie oparte na ciągach znaków API.
Masz do niego dostęp ISession CreateQuery, i jest prawie podobny do SQL.
Używa wielu takich samych słów kluczowych, ale ma uproszczoną składnię.
Jest to jeden z najczęstszych przykładów, jeśli szukasz sposobu wykonania zapytania, często znajdziesz przykłady HQL.
Poniżej znajduje się prosty przykład HQL -
var customers = session.CreateQuery("select c from Customer c where c.FirstName = 'Laverne'");
Więc tutaj widać, że wybierają C z klienta, wygląda to bardzo podobnie do SQL. Jeśli chodzi o NHibernate, jest to nieprzezroczysty ciąg, więc nie wiesz, czy jest to poprawny HQL do czasu uruchomienia, co jest jedną z wad.
Jedną z mocnych stron dostawcy LINQ jest możliwość uzyskania obsługi czasu kompilacji.
Ale HQL jest jednym z najbardziej elastycznych, często używanych mechanizmów zapytań. Mówi się, że jeśli nie ma innego sposobu, aby to zrobić, jest sposób, aby to zrobić w HQL.
Rzućmy okiem na prosty przykład, w którym zamiast tego utworzymy nasze zapytania LINQ przy użyciu języka HQL. Możesz uzyskać dostęp do HQL, dzwoniąc dosession.CreateQuery i przekazać jako parametr przy użyciu łańcucha HQL.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customers = session.CreateQuery("select c from Customer c
where c.FirstName = 'Laverne'");
foreach (var customer in customers.List<Customer>()) {
Console.WriteLine(customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Ten ciąg HQL wygląda bardzo podobnie do SQL, główna różnica polega na tym, że FirstName jest nazwą właściwości, a nie nazwą kolumny.
Jeśli więc istnieje rozbieżność między nimi, użyj nazwy właściwości. To samo wygląda jak nazwa tabeli, ale w rzeczywistości jest to nazwa klasy, z której wybieramy.
Jeśli tabela zaplecza została nazwana jako Klienci, nadal używalibyśmy Customer w naszym zapytaniu HQL.
Uruchommy tę aplikację, a zobaczysz następujące dane wyjściowe.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 4/4/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
Press <ENTER> to exit...
Rzućmy okiem na inny prosty przykład, w którym pobieramy wszystkich klientów, których FirstName zaczyna się na literę H, używając języka HQL.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customers = session.CreateQuery("select c from Customer c
where c.FirstName like 'H%'");
foreach (var customer in customers.List<Customer>()) {
Console.WriteLine(customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Uruchommy ponownie aplikację, a zobaczysz, że wszyscy klienci, których nazwa zaczyna się na literę H, są zwracani z tego zapytania.
Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 12/3/2010 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be
Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be)
Points: 56
HasGoldStatus: False
MemberSince: 10/20/2008 12:00:00 AM (Utc)
CreditRating: Terrible
AverageRating: 0
Orders:
Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be
Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be)
Points: 82
HasGoldStatus: False
MemberSince: 4/10/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be
Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be
Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be
Press <ENTER> to exit...
Możemy robić bardziej skomplikowane rzeczy, na przykład żądać wszystkich zamówień, w których liczba zamówień jest większa niż 9. Poniżej znajduje się zapytanie HQL dotyczące tego samego.
var customers = session.CreateQuery("select c from Customer c
where size(c.Orders) > 9");
foreach (var customer in customers.List<Customer>()) {
Console.WriteLine(customer);
}
Musimy również wskazać, że potrzebujemy tutaj rozmiaru lub liczby lub długości. W HQL mamy możliwość użycia specjalnej metody rozmiaru, jak pokazano powyżej.
Innym sposobem na zapisanie tego, jeśli wolisz, jest c.Orders.sizei to ma dokładny skutek.
var customers = session.CreateQuery("select c from Customer c
where c.Orders.size > 9");
foreach (var customer in customers.List<Customer>()) {
Console.WriteLine(customer);
}
Uruchommy tę aplikację.
Lindsay Towne (4ea3aef6-6bce-11e1-b0cb-6cf049ee52be)
Points: 50
HasGoldStatus: False
MemberSince: 4/13/2007 12:00:00 AM (Utc)
CreditRating: VeryGood
AverageRating: 0
Orders:
Order Id: 4ea3aef6-6bce-11e1-b0cc-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0cd-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0ce-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0cf-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0d0-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0d1-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0d2-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0d3-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0d4-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0d5-6cf049ee52be
Wyman Hammes (4ea61056-6bce-11e1-b0e2-6cf049ee52be)
Points: 32
HasGoldStatus: False
MemberSince: 2/5/2011 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 0
Orders:
Order Id: 4ea61056-6bce-11e1-b0e3-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0e4-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0e5-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0e6-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0e7-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0e8-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0e9-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0ea-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0eb-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0ec-6cf049ee52be
Press <ENTER> to exit...
Widać, że wszyscy klienci, którzy mają więcej niż 9 zamówień, są pobierani z bazy danych.
W tym rozdziale zajmiemy się mechanizmem zapytań kryterialnych. PlikNHibernate Query by Criteria API umożliwia tworzenie zapytań przez manipulowanie obiektami kryteriów w czasie wykonywania.
Takie podejście umożliwia dynamiczne określanie ograniczeń bez bezpośredniej manipulacji na ciągach znaków, ale nie traci wiele z elastyczności ani mocy HQL.
Z drugiej strony zapytania wyrażone jako kryteria są często mniej czytelne niż zapytania wyrażone w HQL.
Klasyczna składnia kryteriów to oparty na obiektach interfejs API zapytań, jak pokazano w poniższym programie.
var customers = session.CreateCriteria<Customer>().Add(Restrictions.Like("FirstName", "H%"));
Jak widać, wykonujemy sesję tworzenia kryteriów na kliencie, a teraz dodajemy obiekt ograniczenia do tego zapytania.
Jest to przydatne w przypadku stron zapytań, na których użytkownicy mogą wybierać określone opcje, ale nie inne.
Łatwiej jest zbudować zapytanie jako rodzaj drzewa, takiego jak struktura zapytania, a nie w HQL lub LINQ, gdzie można użyć klauzuli AND lub OR w WHERE.
Łatwiej jest po prostu dodać dodatkowe ograniczenia za pomocą tych obiektów kryteriów.
Spójrzmy na prosty przykład, w którym utworzymy zapytanie i uzyskamy dostęp do API kryteriów za pośrednictwem createCriteria a następnie dodaj ograniczenie, że imię zaczyna się od H.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customers = session.CreateCriteria<Customer>()
.Add(Restrictions.Like("FirstName", "H%"));
foreach (var customer in customers.List<Customer>()) {
Console.WriteLine(customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Kiedy powyższy kod zostanie skompilowany i wykonany, zobaczysz następujące dane wyjściowe.
Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 12/3/2010 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be
Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be)
Points: 56
HasGoldStatus: False
MemberSince: 10/20/2008 12:00:00 AM (Utc)
CreditRating: Terrible
AverageRating: 0
Orders:
Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be
Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be)
Points: 82
HasGoldStatus: False
MemberSince: 4/10/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be
Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be
Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be
Press <ENTER> to exit…
Spójrzmy na inny prosty przykład, w którym odzyskamy klienta, którego imię jest równe „Laverne”
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customers = session.CreateCriteria<Customer>()
.Add(Restrictions.Eq("FirstName", "Laverne")) .List<Customer>();
foreach (var customer in customers) {
Console.WriteLine(customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Uruchommy ponownie tę aplikację, a zobaczysz następujące dane wyjściowe.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 4/4/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
Press <ENTER> to exit...
Teraz jedną z głównych wad API kryteriów są te nieprzezroczyste ciągi w nazwach właściwości. Tak więc, jeśli imię zostało refaktoryzowane na coś innego, narzędzie refaktoryzujące niekoniecznie wychwyciłoby nieprzezroczysty ciąg.
W tym rozdziale zajmiemy się zapytaniami QueryOver. Jest to nowa składnia, która bardziej przypomina LINQ przy użyciu składni łańcucha metod, jak pokazano w poniższym zapytaniu.
var customers = session.QueryOver<Customer>() .Where(x => x.FirstName == "Laverne");
Nadal jest to kryteria pod osłonami, ale teraz nasze zapytania są silnie wpisywane.
Jak widzieliśmy w zapytaniu kryterialnym, imię jest po prostu nieprzezroczystym ciągiem, teraz używamy w rzeczywistości x.FirstName, więc imię zostanie refaktoryzowane i przemianowane, co zostanie zmienione w zapytaniu kryteriów stylu łącza za pomocą zapytania powyżej.
Nadal możemy zrobić wiele podobnych rzeczy, ale nie możesz używać składni rozumienia zapytań z zapytaniem, musisz użyć składni łańcuchowej metody i nie możesz łączyć i dopasowywać linku oraz kryteriów.
W przypadku wielu zapytań zapytanie za pośrednictwem interfejsu API jest bardzo przydatne i zapewnia znacznie łatwiejszą do zrozumienia składnię obiektu niż bezpośrednie użycie kryteriów.
Przyjrzyjmy się prostemu przykładowi, w którym pobieramy klienta, którego imię to Laverne, za pomocą zapytania ponad.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customers = session.QueryOver<Customer>()
.Where(x => x.FirstName == "Laverne");
foreach (var customer in customers.List()) {
Console.WriteLine(customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Jak widać, pod okładkami nadal znajdują się Kryteria, ale jest to po prostu ładniejsza składnia.
Kiedy powyższy kod zostanie skompilowany i wykonany, zobaczysz następujące dane wyjściowe.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 4/4/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
Press <ENTER> to exit...
Jedną z wad jest to, że powiedzmy, że chcemy to powiedzieć FirstName.StartsWith(“A”) jak pokazano w następującym programie.
var customers = session.QueryOver<Customer>() .Where(x => x.FirstName.StartsWith("A"));
foreach (var customer in customers.List()) {
Console.WriteLine(customer);
}
tx.Commit();
Teraz uruchommy ponownie aplikację, a zobaczysz, że nie jest to dostawca LINQ, ponieważ nie wie, co to jest StartsWith metoda jest taka, więc otrzymasz plik RunTime exception.
Wyjątek mówi, że nierozpoznane wywołanie metody. Tutaj robimy oczywistą rzecz, ale niekoniecznie działa.
Spróbujmy czegoś innego, na przykład FirstName równa się „A%”, jak pokazano w poniższym kodzie.
var customers = session.QueryOver<Customer>() .Where(x => x.FirstName == "A%");
foreach (var customer in customers.List()) {
Console.WriteLine(customer);
}
Uruchommy to jeszcze raz, a zobaczysz, że nie otrzymamy żadnych wyników, jak pokazano poniżej.
Press <ENTER> to exit...
Aby zrozumieć, dlaczego nie otrzymujemy żadnych wyników, spójrzmy na profiler NHibernate.
Jak widać, imię to A%, co nie jest. Znak% jest używany w języku SQL wraz z operatorem like. Teraz musimy utworzyć ograniczenie w klauzuli WHERE, jak pokazano w poniższym programie.
var customers = session.QueryOver<Customer>()
.Where(Restrictions.On<Customer>(c => c.FirstName).IsLike("A%"));
foreach (var customer in customers.List()) {
Console.WriteLine(customer);
}
Uruchommy ponownie aplikację, a zobaczysz, że wszyscy klienci są pobierani z imionami zaczynającymi się od A.
Alejandrin Will (4ea3aef6-6bce-11e1-b0b4-6cf049ee52be)
Points: 24
HasGoldStatus: False
MemberSince: 10/1/2011 12:00:00 AM (Utc)
CreditRating: VeryVeryGood
AverageRating: 0
Orders:
Order Id: 4ea3aef6-6bce-11e1-b0b5-6cf049ee52be
Austyn Nolan (4ea871b6-6bce-11e1-b110-6cf049ee52be)
Points: 67
HasGoldStatus: True
MemberSince: 12/29/2007 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea871b6-6bce-11e1-b111-6cf049ee52be
Antonia Murphy (4ea871b6-6bce-11e1-b121-6cf049ee52be)
Points: 72
HasGoldStatus: True
MemberSince: 6/15/2009 12:00:00 AM (Utc)
CreditRating: Terrible
AverageRating: 0
Orders:
Order Id: 4ea871b6-6bce-11e1-b122-6cf049ee52be
Order Id: 4ea871b6-6bce-11e1-b123-6cf049ee52be
Order Id: 4ea871b6-6bce-11e1-b124-6cf049ee52be
Order Id: 4ea871b6-6bce-11e1-b125-6cf049ee52be
Order Id: 4ea871b6-6bce-11e1-b126-6cf049ee52be
Order Id: 4ea871b6-6bce-11e1-b127-6cf049ee52be
Order Id: 4ea871b6-6bce-11e1-b128-6cf049ee52be
Order Id: 4ea871b6-6bce-11e1-b129-6cf049ee52be
Order Id: 4ea871b6-6bce-11e1-b12a-6cf049ee52be
Działa tak samo jak wcześniej, z wyjątkiem używania tego nowego QueryOverskładnia. Wielu programistów uważa, że składnia LINQ jest bardziej przystępna i często robi właściwe rzeczy.
Jeśli LINQ nie może sobie z tym poradzić, zaczniesz patrzeć na HQL lub Kryteria, aby sprawdzić, czy to będzie bardziej odpowiednie.
Po prostu daje ci inną składnię, więc Kryteria, zarówno kryteria tworzenia, jak i QueryOver zapewniają po prostu kolejny mechanizm zapytań, który pozwala wyciągać dane z bazy danych za pomocą NHibernate.
W tym rozdziale omówimy, jak używać natywnych zapytań SQL w NHibernate. Jeśli używasz odręcznego języka SQL od wielu lat, możesz obawiać się, że ORM odbierze Ci część ekspresji i elastyczności, do której jesteś przyzwyczajony.
Potężne funkcje zapytań NHibernate pozwalają zrobić prawie wszystko, co w SQL, a w niektórych przypadkach więcej.
W rzadkich przypadkach, w których nie możesz zmusić własnych narzędzi NHibernate do wykonywania zapytań dokładnie tak, jak chcesz.
NHibernate umożliwia pobieranie obiektów przy użyciu rodzimego dialektu SQL Twojej bazy danych.
Przyjrzyjmy się prostemu przykładowi zapytań Native SQL w NHibernate.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
using NHibernate;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
IQuery sqlQuery = session.CreateSQLQuery("SELECT * FROM
CUSTOMER").AddEntity(typeof(Customer));
var customers = sqlQuery.List<Customer>();
foreach (var customer in customers) {
Console.WriteLine(customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Powyższy przykład używa CreateSQLQuery() aby odzyskać listę obiektów, zauważysz również, że typ jednostki głównej, który ma zwrócić zapytanie, jest określony jako Customer.
Uruchommy Twoją aplikację, a zobaczysz, że wszyscy klienci są pobierani z bazy danych.
Emerson Prosacco (4ec2a0e0-6bce-11e1-b2cf-6cf049ee52be)
Points: 17
HasGoldStatus: False
MemberSince: 6/22/2007 12:00:00 AM (Utc)
CreditRating: Excellent
AverageRating: 0
Orders:
Order Id: 4ec2a0e0-6bce-11e1-b2d0-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2d1-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2d2-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2d3-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2d4-6cf049ee52be
Kaci Friesen (4ec2a0e0-6bce-11e1-b2d5-6cf049ee52be)
Points: 30
HasGoldStatus: True
MemberSince: 5/25/2007 12:00:00 AM (Utc)
CreditRating: VeryVeryGood
AverageRating: 0
Orders:
Order Id: 4ec2a0e0-6bce-11e1-b2d6-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2d7-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2d8-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2d9-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2da-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2db-6cf049ee52be
Eveline Waters (4ec2a0e0-6bce-11e1-b2dc-6cf049ee52be)
Points: 58
HasGoldStatus: False
MemberSince: 10/29/2009 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 0
Orders:
Order Id: 4ec2a0e0-6bce-11e1-b2dd-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2de-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2df-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2e0-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2e2-6cf049ee52be
Molly Kuhn (4ec2a0e0-6bce-11e1-b2e3-6cf049ee52be)
Points: 73
HasGoldStatus: False
MemberSince: 12/16/2007 12:00:00 AM (Utc)
CreditRating: VeryGood
AverageRating: 0
Orders:
Order Id: 4ec2a0e0-6bce-11e1-b2e4-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2e5-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2e6-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2e7-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2e8-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2e9-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2ea-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2eb-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2ec-6cf049ee52be
Oto inny sposób pisania natywnego zapytania SQL, jak pokazano poniżej.
IList<Customer> customers = session.CreateSQLQuery("SELECT * FROM CUSTOMER")
.AddScalar("Id", NHibernateUtil.Guid)
.AddScalar("FirstName", NHibernateUtil.String)
.AddScalar("LastName", NHibernateUtil.String) .List<Customer>();
Jak widać, powyższe zapytanie określało ciąg zapytania SQL oraz kolumny i typy do zwrócenia.
To zwróci IList of Object tablice z wartościami skalarnymi dla każdej kolumny w tabeli Customer.
Zwrócone zostaną tylko te trzy kolumny, mimo że zapytanie używa * i może zwrócić więcej niż trzy wymienione kolumny.
Spójrzmy na inny prosty przykład.
IList<Customer> customers = session.CreateSQLQuery("SELECT * FROM CUSTOMER WHERE
FirstName = 'Laverne'")
.AddEntity(typeof(Customer)) .List<Customer>();
foreach (var customer in customers) {
Console.WriteLine(customer);
}
Uruchommy ponownie aplikację, a zobaczysz następujące dane wyjściowe.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 4/4/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
Press <ENTER> to exit...
Podobnie można określić dowolny typ zapytania SQL w celu pobrania danych z bazy danych.
W tym rozdziale zajmiemy się płynnym NHibernate. Fluent NHibernate to inny sposób mapowania lub można powiedzieć, że jest to alternatywa dla standardowych plików mapowania XML NHibernate. Zamiast pisać XML(.hbm.xml files)dokumenty. Z pomocą Fluent NHibernate możesz pisać mapowania w silnie wpisanym kodzie C #.
W Fluent NHibernate mapowania są kompilowane wraz z resztą aplikacji.
Możesz łatwo zmienić swoje mapowania, tak jak kod aplikacji, a kompilator zakończy się niepowodzeniem w przypadku jakichkolwiek literówek.
Ma konwencjonalny system konfiguracji, w którym można określić wzorce zastępowania konwencji nazewnictwa i wiele innych rzeczy.
Możesz także ustawić jednorazowe nazewnictwo, a Fluent NHibernate zrobi resztę.
Rzućmy okiem na prosty przykład, tworząc nowy projekt konsoli. W tym rozdziale użyjemy prostej bazy danych, w której mamy prostą tabelę klientów, jak pokazano na poniższym obrazku.
Zainstaluj Fluent NHibernate
Pierwszym krokiem do uruchomienia Fluent NHibernate jest instalacja pakietu Fluent NHibernate. Więc otwórzNuGet Package Manager Console i wprowadź następujące polecenie.
PM> install-package FluentNHibernate
Po pomyślnym zainstalowaniu zostanie wyświetlony następujący komunikat.
Dodajmy prostą klasę modelu Customer, a poniższy program przedstawia implementację klasy Customer.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FluentNHibernateDemo {
class Customer {
public virtual int Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
}
}
Teraz musimy stworzyć mapowania używając płynnego NHibernate, więc dodaj jeszcze jedną klasę CustomerMapw swoim projekcie. Oto implementacja klasy CustomerMap.
using FluentNHibernate.Mapping;
using System;
using System.Collections.Generic;
using System.Linq; using System.Text;
using System.Threading.Tasks;
namespace FluentNHibernateDemo {
class CustomerMap : ClassMap<Customer> {
public CustomerMap() {
Id(x => x.Id);
Map(x => x.FirstName);
Map(x => x.LastName);
Table("Customer");
}
}
}
Dodajmy kolejną klasę NHibernateHelper w którym ustawimy różne ustawienia konfiguracyjne.
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Tool.hbm2ddl;
namespace FluentNHibernateDemo {
public class NHibernateHelper {
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory {
get {
if (_sessionFactory == null)
InitializeSessionFactory(); return _sessionFactory;
}
}
private static void InitializeSessionFactory() {
_sessionFactory = Fluently.Configure()
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
.Database(MsSqlConfiguration.MsSql2008 .ConnectionString(
@"Data Source + Initial Catalog + Integrated Security + Connect Timeout
+ Encrypt + TrustServerCertificate + ApplicationIntent +
MultiSubnetFailover") .ShowSql() )
.Mappings(m => m.FluentMappings
.AddFromAssemblyOf<Program>())
.ExposeConfiguration(cfg => new SchemaExport(cfg)
.Create(true, true))
.BuildSessionFactory();
}
public static ISession OpenSession() {
return SessionFactory.OpenSession();
}
}
}
Teraz przejdźmy do Program.cs plik, w którym rozpoczniemy sesję, a następnie utworzymy nowego klienta i zapiszemy go w bazie danych, jak pokazano poniżej.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FluentNHibernateDemo {
class Program {
static void Main(string[] args) {
using (var session = NHibernateHelper.OpenSession()) {
using (var transaction = session.BeginTransaction()) {
var customer = new Customer {
FirstName = "Allan",
LastName = "Bomer"
};
session.Save(customer);
transaction.Commit();
Console.WriteLine("Customer Created: " + customer.FirstName + "\t" +
customer.LastName);
}
Console.ReadKey();
}
}
}
}
Uruchommy Twoją aplikację, a zobaczysz następujące dane wyjściowe.
if exists (select * from dbo.sysobjects where id = object_id(N'Customer') and
OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table Customer
create table Customer (
Id INT IDENTITY NOT NULL,
FirstName NVARCHAR(255) null,
LastName NVARCHAR(255) null,
primary key (Id)
)
NHibernate: INSERT INTO Customer (FirstName, LastName) VALUES (@p0, @p1);
select SCOPE_IDENTITY();@p0 = 'Allan' [Type: String (4000)],
@p1 = 'Bomer' [Type: String (4000)]
Customer Created: Allan Bomer
Jak widać, tworzony jest nowy klient. Aby zobaczyć rekord klienta, przejdźmy do bazy danych i przejrzyj dane, a zobaczysz, że dodano 1 klienta.