NHibernate系列文章二十七:NHibernate Mapping之Fluent Mapping(附程序下载)

摘要

从这一节起,介绍NHibernate Mapping的内容。前面文章都是使用的NHibernate XML Mapping。NHibernate XML Mapping是NHibernate最早最成熟的Mapping方法。其他的Mapping方法都是基于XML Mapping的思想进行的“变种”,要么暂时不能完全像XML Mapping那样功能丰富。其他的Mapping方法目前包括:Fluent Mapping、Attribute Mapping和Mapping by Conventions。他们各自都有优缺点。使用者应该根据实际情况选择适合自己项目的Mapping方式。

这篇文章介绍Fluent Mapping。本篇文章的代码可以到Fluent NHibernate下载。

1、Fluent Mapping的优点

  • Fluent Mapping提供了大量的Fluent API进行映射配置。相比XML Mapping,在代码中进行配置能够在编译时发现很多问题。
  • Fluent Mapping的可读性更强,代码更简洁。
  • Fluent Mapping将映射配置的类和实体映射类相分离,在一定程度上保持了实体类的简洁性。
  • Fluent Mapping使用Lamda表达式和静态类型反射技术,不用写大量常量字符串,避免了很多粗心的错误。

2、Fluent Mapping的缺点

  • 在定义了实体类之后,需要另外定义一个实体映射类。
  • 许多XML Mapping支持的功能,Fluent Mapping暂时不支持,需要等到Fluent Mapping新版本出来后才能支持。
  • Fluent Mapping底层其实还是将代码定义的映射翻译成XML映射文件,因此在程序启动的时候比XML Mapping稍慢。
  • 如果数据库表名称和实体类名称不一致,或者数据库列名称和属性名称不一致,还是需要用字符串的形式做映射,这基本是避免不了的。

3、程序演示

继续使用以之前文章使用过的NHibernateDemoDB数据库。

1)新建工程Demo.Fluent。

2)新建Class Library,名称为Demo.Fluent.Entities。移除Class1.cs文件。

3)在新建的工程中,使用NuGet安装FluentNHibernate。

单击“Install”按钮,会出现Priview对话框,列出将要添加的引用。安装FluentNHibernate将会安装他所依赖的NHibernate和Isesi.Collections。

点击“OK”按钮。等上几分钟时间去喝口茶, 安装完成之后Output将显示Finished。

展开工程的Reference,看到已经将FluentNHibernate添加进来了。

4)添加Domain文件夹和Mapping文件夹。

5)在Domain文件夹内,添加实体类的抽象泛型基类Entity。

 1 namespace Demo.Fluent.Entities.Domain
 2 {
 3     public abstract class Entity<T> where T : Entity<T>
 4     {
 5         public virtual int Id { get; private set; }
 6
 7         public override bool Equals(object obj)
 8         {
 9             var other = obj as T;
10             if (other == null) return false;
11             var thisIsNew = Equals(Id, 0);
12             var otherIsNew = Equals(other.Id, 0);
13             if (thisIsNew && otherIsNew)
14             {
15                 return ReferenceEquals(this, other);
16             }
17             return Id.Equals(other.Id);
18         }
19
20         private int? oldHashCode;
21         public override int GetHashCode()
22         {
23             // once we have a hashcode we‘ll never change it
24             if (oldHashCode.HasValue)
25             {
26                 return oldHashCode.Value;
27             }
28             // when this instance is new we use the base hash code
29             // and remember it, so an instance can NEVER change its
30             // hash code.
31             var thisIsNew = Equals(Id, 0);
32             if (thisIsNew)
33             {
34                 oldHashCode = base.GetHashCode();
35                 return oldHashCode.Value;
36             }
37             return Id.GetHashCode();
38         }
39
40         public static bool operator ==(Entity<T> lhs, Entity<T> rhs)
41         {
42             return Equals(lhs, rhs);
43         }
44         public static bool operator !=(Entity<T> lhs, Entity<T> rhs)
45         {
46             return !Equals(lhs, rhs);
47         }
48     }
49 }
  • 抽象基类Entity定义了实体类共有的Id属性。
  • 抽象基类Entity重写了object类的Equals方法和GetHashCode方法,同时重载了运算符==和!=。

6)在Domain文件夹下,添加值对象类Address类、Name类,实体类:Customer类、Product类和Order类。

Address类

 1 namespace Demo.Fluent.Entities.Domain
 2 {
 3     public class Address
 4     {
 5         public virtual string Street { get; set; }
 6         public virtual string City { get; set; }
 7         public virtual string Province { get; set; }
 8         public virtual string Country { get; set; }
 9
10         public bool Equals(Address other)
11         {
12             if (other == null) return false;
13             if (ReferenceEquals(this, other)) return true;
14             return Equals(other.Street, Street) &&
15                 Equals(other.City, City) &&
16                 Equals(other.Province, Province) &&
17                 Equals(other.Country, Country);
18         }
19
20         public override bool Equals(object obj)
21         {
22             return Equals(obj as Address);
23         }
24
25         public override int GetHashCode()
26         {
27             unchecked
28             {
29                 var result = Street.GetHashCode();
30                 result = (result * 397) ^ (City != null ? City.GetHashCode() : 0);
31                 result = (result * 397) ^ Province.GetHashCode();
32                 result = (result * 397) ^ Country.GetHashCode();
33                 return result;
34             }
35         }
36     }
37 }

Name类

 1 using System;
 2
 3 namespace Demo.Fluent.Entities.Domain
 4 {
 5     public class Name
 6     {
 7         public string LastName { get; private set; }
 8         public string FirstName { get; private set; }
 9
10         public Name() { }
11
12         public Name(string firstName, string lastName)
13         {
14             if (string.IsNullOrWhiteSpace(firstName))
15             {
16                 throw new ArgumentException("First name must be defined.");
17             }
18             if (string.IsNullOrWhiteSpace(lastName))
19             {
20                 throw new ArgumentException("Last name must be defined.");
21             }
22             FirstName = firstName;
23             LastName = lastName;
24         }
25
26         public override int GetHashCode()
27         {
28             unchecked
29             {
30                 var result = FirstName.GetHashCode();
31                 result = (result * 397) ^ LastName.GetHashCode();
32                 return result;
33             }
34         }
35
36         public bool Equals(Name other)
37         {
38             if (other == null) return false;
39             if (ReferenceEquals(this, other)) return true;
40             return Equals(other.FirstName, FirstName) &&
41                 Equals(other.LastName, LastName);
42         }
43
44         public override bool Equals(object other)
45         {
46             return Equals(other as Name);
47         }
48     }
49 }

Address和Name两个类注意两点:

  • 重写了object类的Equals方法和GetHashCode方法。
  • 因为是值对象类型,因此不继承Entity类。

Customer类

 1 using System;
 2 using System.Collections.Generic;
 3
 4 namespace Demo.Fluent.Entities.Domain
 5 {
 6     public class Customer : Entity<Customer>
 7     {
 8         public Customer()
 9         {
10             MemberSince = DateTime.UtcNow;
11         }
12
13         public virtual Name Name { get; set; }
14         public virtual double AverageRating { get; set; }
15         public virtual int Points { get; set; }
16         public virtual bool HasGoldStatus { get; set; }
17         public virtual DateTime MemberSince { get; set; }
18         public virtual CustomerCreditRating CreditRating { get; set; }
19         public virtual Address Address { get; set; }
20
21         private readonly IList<Order> orders;
22
23         public virtual IList<Order> Orders
24         {
25             get
26             {
27                 return orders;
28             }
29         }
30     }
31
32     public enum CustomerCreditRating
33     {
34         Excellent, VeryVeryGood, VeryGood, Good, Neutral, Poor, Terrible
35     }
36 }

这里有六个需要注意的地方:

  • Customer类继承泛型类Entity<Customer>。
  • 不用再在Customer类里定义Id属性。
  • 必须有一个无参数的构造函数,可以在这个构造函数中定义属性的默认值。
  • 集合属性类型必须定义成接口类型,Fluent NHibernate通过反射生成Fluent对应的集合类型。
  • 不能在构造函数中对集合属性进行初始化。
  • 所有成员函数(如果有的话)和成员属性都以virtual修饰。

Product类

 1 using System.Collections.Generic;
 2
 3 namespace Demo.Fluent.Entities.Domain
 4 {
 5     public class Product : Entity<Product>
 6     {
 7         public virtual string ProductCode { get; set; }
 8
 9         public virtual string ProductName { get; set; }
10
11         public virtual string Description { get; set; }
12
13         private readonly IList<Order> orders;
14
15         public virtual IList<Order> Orders
16         {
17             get
18             {
19                 return orders;
20             }
21         }
22     }
23 }

Order类

 1 using System;
 2 using System.Collections.Generic;
 3
 4 namespace Demo.Fluent.Entities.Domain
 5 {
 6     public class Order : Entity<Order>
 7     {
 8         public virtual DateTime Ordered { get; set; }
 9         public virtual DateTime? Shipped { get; set; }
10         public virtual Address ShipTo { get; set; }
11         public virtual Customer Customer { get; set; }
12
13         private readonly IList<Product> products;
14
15         public virtual IList<Product> Products
16         {
17             get
18             {
19                 return products;
20             }
21         }
22     }
23 }

7)在Mapping文件夹下,定义映射类AddressMap、NameMap、CustomerMap、ProductMap和OrderMap。

类名称必须是值对象类型名称或实体类名称后面跟Map。

在映射类的无参构造函数内,调用Fluent NHibernate的API函数,定义映射。

AddressMap类

 1 using Demo.Fluent.Entities.Domain;
 2 using FluentNHibernate.Mapping;
 3
 4 namespace Demo.Fluent.Entities.Mapping
 5 {
 6     public class AddressMap : ComponentMap<Address>
 7     {
 8         public AddressMap()
 9         {
10             Map(x => x.Street).Length(100);
11             Map(x => x.City).Length(100);
12             Map(x => x.Province).Length(100);
13             Map(x => x.Country).Length(100);
14         }
15     }
16 }

NameMap类

 1 using Demo.Fluent.Entities.Domain;
 2 using FluentNHibernate.Mapping;
 3
 4 namespace Demo.Fluent.Entities.Mapping
 5 {
 6     public class NameMap : ComponentMap<Name>
 7     {
 8         public NameMap()
 9         {
10             Map(x => x.LastName).Not.Nullable().Length(10);
11             Map(x => x.FirstName).Not.Nullable().Length(10);
12         }
13     }
14 }
  • Address类和Name类都时值对象类,因此他们的映射类文件都继承ComponetMap的泛型类。
  • Map、Not.Nullable、Length都时Fluent NHibernate的API函数(见名知意),通过链式调用,对单个属性进行映射定义。

CustomerMap类

 1 using Demo.Fluent.Entities.Domain;
 2 using FluentNHibernate.Mapping;
 3
 4 namespace Demo.Fluent.Entities.Mapping
 5 {
 6     public class CustomerMap : ClassMap<Customer>
 7     {
 8         public CustomerMap()
 9         {
10             Id(x => x.ID).GeneratedBy.Native();
11             Component(x => x.Address);
12             Component(x => x.Name);
13             Map(x => x.Points);
14             Map(x => x.HasGoldStatus);
15             Map(x => x.MemberSince);
16             Map(x => x.CreditRating).CustomType<int>();
17             HasMany(x => x.Orders).Inverse().Cascade.AllDeleteOrphan().Fetch.Join();
18         }
19     }
20 }
  • Customer类是实体类,继承ClassMap的泛型类。
  • Id方法定义主键属性,调用Generate.Native()方法指出主键生成策略是indentity的。
  • 对值对象类型的属性,调用Component方法,定义映射。
  • 将枚举类型映射成int类型。Fluent NHibernate暂时无法直接将枚举类型属性映射成枚举值。
  • HasMany方法生成OneToManyPart对象,映射一对多关系。
  • HasMany方法调用后面的一串方法:Cascade.AllDeleteOrphan().Fetch.Join()对应了XML映射响应的属性。

关系映射的API方法:

一对一:HasOne

一对多:HasMany

多对对:HasManyToMany

ProductMap类

using Demo.Fluent.Entities.Domain;
using FluentNHibernate.Mapping;

namespace Demo.Fluent.Entities.Mapping
{
    public class ProductMap : ClassMap<Product>
    {
        public ProductMap()
        {
            Id(x => x.ID).GeneratedBy.Native();
            Map(x => x.ProductCode).Not.Nullable().Length(10);
            Map(x => x.ProductName).Not.Nullable().Length(50);
            Map(x => x.Description).Length(100);
            HasManyToMany(x => x.Orders).Table("ProductOrder").ParentKeyColumn("ProductId").ChildKeyColumn("OrderId").Cascade.AllDeleteOrphan();
        }
    }
}
  • HasManyToMany方法生成ManyToManyPart对象,映射Many-to-Many关系。
  • Table("ProductOrder").ParentKeyColumn("ProductId").ChildKeyColumn("OrderId")
  • 上面连串方法调用定义了Many-to-Many映射的中间表表名。对于Product表,这个关系的主键列和外键列。

OrderMap类

 1 using FluentNHibernate.Mapping;
 2
 3 namespace Demo.Fluent.Entities.Domain
 4 {
 5     public class OrderMap : ClassMap<Order>
 6     {
 7         public OrderMap()
 8         {
 9             Table("`Order`");
10             Id(x => x.ID).GeneratedBy.Native();
11             Map(x => x.Ordered);
12             Map(x => x.Shipped);
13             Component(x => x.ShipTo);
14             References(x => x.Customer).Column("CustomerId").Cascade.SaveUpdate();
15             HasManyToMany(x => x.Products).Table("ProductOrder").ParentKeyColumn("OrderId").ChildKeyColumn("ProductId").Cascade.All();
16         }
17     }
18 }

Table方法定义映射的表名称,因为Order是SQL Server关键字,因此调用此方法,传入字符串"`Order`"作为表名称。生成的SQL语句的表名称字符串是"[Order]"。

Many-to-One关系,实体类属性用Reference方法定义,指定外键列名称。

8)添加用于测试的控制台应用程序Demo.Fluent.Console工程。

9)添加用于NHibernate设置的FluentConfig类。

 1 using Demo.Fluent.Entities.Mapping;
 2 using FluentNHibernate.Cfg;
 3 using FluentNHibernate.Cfg.Db;
 4 using NHibernate;
 5
 6 namespace Demo.Fluent.Console
 7 {
 8     class FluentConfig
 9     {
10         const string connString = "server=localhost;" + "database=NHibernateDemoDB;" + "integrated security=SSPI;";
11
12         public static ISessionFactory CreateSessionFactory()
13         {
14             return Fluently.Configure()
15                 .Database(MsSqlConfiguration.MsSql2008.ConnectionString(connString))
16                 .Mappings(m => m.FluentMappings.AddFromAssemblyOf<CustomerMap>())
17                 .BuildSessionFactory();
18         }
19     }
20 }

9)修改Program类。

 1 using Demo.Fluent.Entities.Domain;
 2 using NHibernate.Linq;
 3 using System.Linq;
 4
 5 namespace Demo.Fluent.Console
 6 {
 7     class Program
 8     {
 9         static void Main(string[] args)
10         {
11             var factory = FluentConfig.CreateSessionFactory();
12             using (var session = factory.OpenSession())
13             {
14                 var customer = session.Get<Customer>(2);
15                 System.Console.WriteLine("{0} {1}", customer.Name.LastName, customer.Name.FirstName);
16
17                 System.Console.WriteLine("order count: {0}",customer.Orders.Count());
18
19                 System.Console.WriteLine();
20                 System.Console.WriteLine("customers and their order count:");
21                 var queryCount = session.Query<Customer>().Select(c => new
22                 {
23                     CustomerId = c.Id,
24                     CustomerName = c.Name.FirstName + " " + c.Name.LastName,
25                     Count = c.Orders.Count()
26                 });
27                 var listCount = queryCount.ToList();
28                 if (listCount.Count > 0)
29                 {
30                     listCount.ForEach(o =>
31                     {
32                         System.Console.WriteLine("{0}-{1}: {2}", o.CustomerId, o.CustomerName, o.Count);
33                     });
34                 }
35
36                 System.Console.WriteLine();
37
38                 System.Console.WriteLine("customers whose oders count greater than 2:");
39                 var queryCountGreater = session.Query<Customer>().Where(c => c.Orders.Count > 2);
40                 var listCountGreater = queryCountGreater.ToList();
41                 if (listCountGreater.Count > 0)
42                 {
43                     listCountGreater.ForEach(o =>
44                     {
45                         System.Console.WriteLine("{0}-{1} {2}", o.Id, o.Name.FirstName, o.Name.LastName);
46                     });
47                 }
48             }
49             System.Console.WriteLine();
50             System.Console.WriteLine("Finished");
51             System.Console.ReadLine();
52         }
53     }
54 }

这里写了三个查询用来测试。第一个查询是通过Id查找Customer对象。第二个查询使用Linq to NHibernate对Customer和订单数量分组查询。第三个查询查找订单数大于2的Customer信息。

执行程序,得到结果(与数据库记录有关)。

结语

虽然Fluent NHibernate目前还不是很成熟,比如不支持枚举映射,但是绝大部分Mapping功能都已经可以能满足了。前面提过了他的优缺点,有兴趣的可以到Fluent NHibernate官网上去查看更详细的内容。

时间: 2024-10-06 02:39:25

NHibernate系列文章二十七:NHibernate Mapping之Fluent Mapping(附程序下载)的相关文章

NHibernate系列文章二十二:NHibernate查询之HQL查询(附程序下载)

摘要 NHibernate提供了多种查询方式,最早的HQL语言查询.Criteria查询和SQL Query,到NHibernate 3.0的Linq NHibernate,NHIbernate 4.0又添加了Query Over.每种方式各有优缺点,任何一个SQL查询可以使用任何查询方式查询.根据程序员每种方式掌握的情况,可以使用不同的查询方式.本篇文章介绍HQL语言查询.HQL(Hibernate Query Language)是NHibernate特有的面向对象查询语言,他具有继承.多态的

NHibernate系列文章二十五:NHibernate查询之Query Over查询(附程序下载)

摘要 这一篇文章介绍在NHibernate 3.2里引入的Query Over查询,Query Over查询跟Criteria查询类似.首先创建IQueryOver对象,然后通过调用该对象的API函数,进行对象查询.这篇文章使用Query Over重写之前所有的查询. 本篇文章的代码可以到NHibernate查询下载 1.创建IQueryOver对象,返回所有Customer信息 1 public IList<Customer> QueryAllOver() 2 { 3 return Sess

NHibernate系列文章二十四:NHibernate查询之Linq查询(附程序下载)

摘要 NHibernate从3.0开始支持Linq查询.写Linq to NHibernate查询就跟写.net linq代码一样,非常灵活,可以很容易实现复杂的查询.这篇文章使用Linq to NHibernate重写之前所有的查询. 本篇文章的代码可以到NHibernate查询下载 1.创建IQueryable对象,返回所有Customer对象信息 1 public IList<Customer> QueryAllLinq() 2 { 3 return Session.Query<C

NHibernate系列文章二十:NHibernate关系之一对一(附程序下载)

摘要 NHibernate一对一关系虽然不经常碰到,但是在对于数据库结构优化的时候,经常会碰到一对一关系.比如,产品详细信息比较多的时候,可以把产品详细信息放到另一张表里面,Product主表只记录产品主要信息.这样能够显著提高产品的查询效率. 这篇文章的附件:NHibernate Demo下载. 1.建立ProductDetail表 这里将ProductId设置为主键. Product和ProductDetail之间的关系. ProductId既是主键又是外键. 创建ProductDetail

NHibernate系列文章二十一:延迟加载

摘要 NHibernate的延迟加载机制是很重要的内容.通过关系映射将数据库表之间的关系映射成对象之间的关系,如果没有延迟加载机制,从主表的一个对象的查询将直接查询出所有与该对象关联的其他对象,如果关联的对象上还有其他的关联对象,还要去查询其他的对象.如果这张“网”非常大,或者关联的数据库记录非常多的话,每次查询主表记录都要把整个数据库都查询一遍,这样效率会非常低下.为了解决这个问题产生了NHibernate延迟加载.对一些属性设置延迟加载,只在对象访问到这些属性的时候才去查询数据库,很大程度上

JVM系列文章(二):垃圾回收机制

作为一个程序员,仅仅知道怎么用是远远不够的.起码,你需要知道为什么可以这么用,即我们所谓底层的东西. 那到底什么是底层呢?我觉得这不能一概而论.以我现在的知识水平而言:对于Web开发者,TCP/IP.HTTP等等协议可能就是底层:对于C.C++程序员,内存.指针等等可能就是底层的东西.那对于Java开发者,你的Java代码运行所在的JVM可能就是你所需要去了解.理解的东西. 我会在接下来的一段时间,和读者您一起去学习JVM,所有内容均参考自<深入理解Java虚拟机:JVM高级特性与最佳实践>(

《sort命令的k选项大讨论》-linux命令五分钟系列之二十七

本原创文章属于<Linux大棚>博客,博客地址为http://roclinux.cn.文章作者为rocrocket. 为了防止某些网站的恶性转载,特在每篇文章前加入此信息,还望读者体谅. === [正文开始] 有时候学习脚本,你会发现sort命令后面跟了一堆类似-k1,2,或者-k1.2 -k3.4的东东,有些匪夷所思.今天,我们就来搞定它—-k选项! 1 准备素材 $ cat facebook.txt google 110 5000 baidu 100 5000 guge 50 3000 s

vue-小爱ADMIN系列文章(二):微信微博等分享,国际化,前端性能优化,nginx服务器部署

最近在做我的小爱ADMIN后台管理系统,结合当前市场后台管理系统对相关功能的需求,我又开始新增了一些新的功能和组件,如分享功能组件,项目国际化功能:项目完成后,部署在nginx服务器,发现首次访问的速度特别慢,严重的影响了用户体验,因此,我又开始进行了一系列的前端性能优化;以及将优化后的项目部署到nginx服务器二级子目录的注意细节. 效果演示地址 github地址 分享功能 背景说明 用微信,微博等做网站的第三方登录及用微信和支付宝进行支付,都需要注册开发者账号和添加网站应用,比较麻烦.另外,

Spring Boot2 系列教程(二十七)Nginx 极简扫盲入门

上篇文章和大家聊了 Spring Session 实现 Session 共享的问题,有的小伙伴看了后表示对 Nginx 还是很懵,因此有了这篇文章,算是一个 Nginx 扫盲入门吧! 基本介绍 Nginx 是一个高性能的 HTTP 和反向代理 web 服务器,同时也提供了 IMAP/POP3/SMTP 服务. Nginx 是由伊戈尔·赛索耶夫为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日. Nginx 特点是占有内存少,