NHibernate - komponent mapujący
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ć obiekt pieniężny, 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.
Rzućmy okiem 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.