NHibernate - ความสัมพันธ์
ในบทนี้เราจะดูความสัมพันธ์ใน NHibernate เรามาสนใจวิธีที่เราจะเข้าใจความสัมพันธ์ใน NHibernate กันเถอะ วิธีที่ง่ายที่สุดคือการคิดถึงความสัมพันธ์จากมุมมองของฐานข้อมูล
ก่อนอื่นเราจะสร้างแอปพลิเคชันใหม่ซึ่งเราจะสร้างความสัมพันธ์ระหว่างลูกค้าและหน่วยงานสั่งซื้อ
ความสัมพันธ์แรกที่เราจะดูคือความสัมพันธ์คอลเลกชันแบบคลาสสิก
เรามีลูกค้าที่รวบรวมคำสั่งซื้อ
นี่คือความสัมพันธ์แบบหนึ่งต่อกลุ่มและแสดงในฐานข้อมูลด้วยตาราง 2 ตารางและมีรหัสลูกค้าอยู่ในตารางคำสั่งซื้อและเรามีความสัมพันธ์คีย์ต่างประเทศกลับไปยังลูกค้า
อันดับแรกเราต้องสร้างฐานข้อมูลและสองตาราง Customer and Order คุณสามารถสร้างสิ่งนี้ได้โดยระบุแบบสอบถามต่อไปนี้ใน SQL Server Explorer
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
มันจะสร้างตารางสองตารางในฐานข้อมูล ภาพต่อไปนี้แสดงตารางลูกค้า
ภาพต่อไปนี้แสดงตารางคำสั่งซื้อซึ่งคุณสามารถดูความสัมพันธ์ของ Foreign Key กลับไปยังลูกค้าได้
เราจำเป็นต้องกำหนดสตริงการเชื่อมต่อในไฟล์ app.config นี่คือการใช้งานไฟล์ 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>
ในการติดตั้ง NHibernate ในแอปพลิเคชันของคุณให้รันคำสั่งต่อไปนี้ในหน้าต่าง NuGet Manager Console
install-package NHibernate
ในการกำหนดค่าคอนฟิกูเรชัน NHibernate เราจำเป็นต้องกำหนดการกำหนดค่าใน 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_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>
ในตัวอย่างนี้เราจะทำงานสองคลาสโดเมนลูกค้าและคำสั่งซื้อ
นี่คือการใช้งานไฟล์ Customer.cs ซึ่งเรามีสองคลาสคลาสหนึ่งคือคลาสลูกค้าและอีกคลาสที่ตั้งเป็นคลาสที่ใช้อ็อบเจ็กต์เป็นที่อยู่ในคลาสลูกค้า
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
}
}
นี่คือไฟล์การแมป 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>
</class>
</hibernate-mapping>
นอกจากนี้เรายังมีคลาสคำสั่งซื้อและนี่คือการใช้งาน Order.cs ไฟล์.
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);
}
}
}
ความสัมพันธ์แบบหลายต่อหนึ่ง
เราต้องแมปคลาส Order กับตาราง Order ในฐานข้อมูลด้วยดังนั้นนี่คือการนำไฟล์ Order.hbm.xml ไฟล์.
<?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>
ความสัมพันธ์แบบหนึ่งต่อกลุ่ม
ในที่นี้เราจะดูความสัมพันธ์แบบหนึ่งต่อกลุ่มในกรณีนี้ระหว่างลูกค้าและคำสั่งซื้อ เรามีลูกค้าของเราที่นี่เรากำลังสร้างใหม่และคุณจะเห็นว่าคอลเล็กชันเริ่มต้นด้วยคำสั่งซื้อคู่ต่อไปนี้
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;
}
ดังนั้นเราจะสร้างลูกค้าใหม่จากนั้นบันทึกหลังจากบันทึกแล้วเราจะพบ ID แล้วโหลดซ้ำในเซสชันอื่นในวิธีการหลักดังที่แสดงในโปรแกรมต่อไปนี้
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();
}
นี่คือสิ่งที่สมบูรณ์ Program.cs การใช้งานไฟล์
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;
}
}
}
เมื่อคุณเรียกใช้แอปพลิเคชันนี้คุณจะเห็นผลลัพธ์ต่อไปนี้
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...
ดังที่คุณจะเห็นว่าในตอนแรกลูกค้ามีคำสั่งซื้อ 2 รายการ แต่เมื่อเราโหลดใหม่จะไม่มีคำสั่งซื้อให้เห็น หากคุณมองไปที่customer.hbm.xmlคุณสามารถดูได้ที่นี่ว่าเราไม่ได้ทำแผนที่การรวบรวมคำสั่งซื้อจริง ดังนั้น NHibernate จึงไม่รู้อะไรเลย มาเพิ่มกันเถอะ
<?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>
นี่คือชุดและชื่อของคอลเลกชันนี้คือ 'คำสั่งซื้อ' ซึ่งเก็บไว้ในตารางที่เรียกว่าคำสั่งซื้อ เราจำเป็นต้องระบุคีย์ซึ่งเป็นชื่อของคีย์ต่างประเทศหรือเพื่อค้นหาคำสั่งซื้อ คำสั่งซื้อเหล่านี้ถูกระบุหรือเป็นของลูกค้าผ่านรหัสลูกค้า จากนั้นฉันต้องสังเกตว่านี่เป็นความสัมพันธ์แบบหนึ่งต่อกลุ่มและเป็นไปตามคลาสลำดับ
เราจำเป็นต้องเปลี่ยนแปลงวิธีการหลักเล็กน้อยโดยบันทึกคำสั่งซื้อของลูกค้าใหม่ลงในฐานข้อมูลตามที่แสดงในโปรแกรมต่อไปนี้
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();
}
เราได้ระบุด้วยว่าลูกค้ารายใดสั่งซื้อสินค้านั้น ๆ ดังนั้นเราจำเป็นต้องสร้างความสัมพันธ์แบบกลุ่มต่อหนึ่งเพื่อเชื่อมโยงคำสั่งซื้อนั้นกลับไปยังลูกค้ารายนั้น
ลองเข้าไปใน Order.hbm.xml ไฟล์และเพิ่มแบบหลายต่อหนึ่งจากนั้นตั้งชื่อฟิลด์ลูกค้าและคอลัมน์ด้วยรหัสลูกค้า
<?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>
ลองเรียกใช้แอปพลิเคชันนี้อีกครั้งและตอนนี้คุณจะเห็นผลลัพธ์ต่อไปนี้
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...