NHibernate - Componente de mapeo
En este capítulo, hablaremos de componentes de mapeo. En NHibernate,component is a value object. No tiene una identidad propia.
Un ejemplo de esto sería un objeto de dinero, un bolso o una billetera pueden tener dinero, pero la identidad exacta de ese dinero es irrelevante.
No tiene su propia clave principal, pero los componentes en sí son persistentes en la misma tabla que el objeto propietario.
Echemos un vistazo a un ejemplo sencillo en el que un estudiante tiene una dirección, que es un objeto de Location class asociado a ello.
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
}
}
Ahora, también necesitamos actualizar la base de datos ejecutando la siguiente consulta, que primero eliminará la tabla de Estudiantes y luego creará una nueva tabla que también contendrá una columna para la clase Ubicación.
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)
);
Ahora, para mapear aquellas columnas que no forman parte directamente de la clase de estudiante, pero son propiedades de la clase de ubicación y el objeto de clase de ubicación se define en la clase de estudiante. Necesitamos un componente para mapearlo correctamente. Creemos un componente enstudent.hbm.xml archivo como se muestra en el siguiente código.
<?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>
Este componente es la Dirección y tiene estas diferentes propiedades. Con esta información, NHibernate ahora tiene suficiente para poder mapear esto.
Ahora aquí está el archivo Program.cs en el que se crea e inicializa un nuevo objeto de estudiante y luego se guarda en la base de datos. Luego recuperará la lista de la base de datos.
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();
}
}
}
}
Ahora podemos ejecutar esta aplicación y NHibernate puede guardar esos valores en la base de datos. Cuando ejecute la aplicación, verá el siguiente resultado.
Fetch the complete list again
2 Allan Bommer Poor 123 Street Lahore Punjab Pakistan
Aquí están los valores en la base de datos.
Los componentes nos permiten separar columnas que están en una tabla de base de datos en su propia clase separada.
La otra cosa a tener en cuenta aquí es que la Ubicación es una clase, no una entidad.
Es un objeto de tipo valor y no tiene su propia clave primaria.
Se guarda en la misma tabla que el Estudiante que lo contiene.
Es por eso que usamos el componente aquí.
Esto permite mucha flexibilidad para cambiar nuestra capa de clase, cómo se definen nuestras clases versus cómo se distribuye nuestra base de datos.