Java – 一种代码松散的XML在我们学习 Fluent NHibernate 之前, 应该先了解下老式的 NHibernate 映射文件应该是怎样写的。 在一个典型的 NHibernate 配置中你会有很多类似这样的映射文件?xml version1.0 encodingutf-8 ? hibernate-mapping xmlnsurn:nhibernate-mapping-2.2 class nameNStackExample.Address, NStackExample.Core tableAddress composite-id key-many-to-one namePerson classNStackExample.Person, NStackExample.Core columnID / key-property nameType typeInt32 / /composite-id property nameCity typeString length255 / property nameLines typeString length255 / property nameState typeString length2 / property nameZip typeString length10 / /class /hibernate-mapping你必须为每一个实体类配置一个这样的映射文件这种做法是从 Java 的 Hibernate 中遗留下来的。在我看来这么做是非常痛苦的不过幸运的是有一个好办法来解决这个问题。更好的选择: Fluent Mappings有了 Fluent NHibernate 上面的映射文件就可以用下面这个类来代替using FluentNHibernate.Mapping; namespace NStackExample.Data { public class AddressMapping : ClassMapAddress { public AddressMapping() { UseCompositeId() .WithKeyReference(x x.Person) .WithKeyProperty(x x.Type); Map(x x.Lines).WithLengthOf(255); Map(x x.City).WithLengthOf(255); Map(x x.State).WithLengthOf(2); Map(x x.Zip).WithLengthOf(5); } } }看起来这个类可能比之前的映射文件还要复杂但是因为有智能感知我们能轻而易举的完成并且我们不用担心魔字符串(magic strings)的问题。当你使用重构工具来改变属性名称的时候你的映射文件也会同步改变。现在大家都知道基本的概念了吧那么让我们继续。Where?因为数据库连接、 NHibernate 配置、实体类映射和 DAO 的实现只是我们选择的ORM的执行细节所以应该把他们放到一个单独的程序集中。创建一个新的类库项目名字叫做NStackExample.Data 。添加新项目的引用将 Core 项目NHibernate.dll 和 FluentNHibernate.dll 添加进去。为了以后我们能轻松的检索一些应用程序的设置将System.Configuration.dll 也添加进去。此外在我们的 Web 项目中也需要将新建的项目添加到引用当中。下面让我们来完成我们的映射文件。using NStackExample; using FluentNHibernate.Mapping; namespace NStackExample.Data { public class CourseMapping : ClassMapCourse { public CourseMapping() { Id(x x.ID).GeneratedBy.GuidComb(); Map(x x.CourseNumber) .Not.Nullable() .WithLengthOf(4) .UniqueKey(CourseNaturalKey); Map(x x.Subject) .Not.Nullable() .WithLengthOf(4) .UniqueKey(CourseNaturalKey); Map(x x.Title) .Not.Nullable() .WithLengthOf(255); Map(x x.Description) .Not.Nullable() .WithLengthOf(1024); Map(x x.Hours) .Not.Nullable(); HasMany(x x.Sections) .AsSet() .WithForeignKeyConstraintName(CourseSections); } } }上面的代码非常容易理解最后得到的就是我们需要的映射文件。我们的映射类继承自 ClassMapCourse ClassMap 类是 Fluent NHibernate 搜索查找映射时的具体类型。在这里这个类提供了 Course 实体类的映射。在构造函数中我们定义了每个属性的具体映射。将 Id 设置成持久化对象标示符(POID)基本上这就是数据表的主键。对于有多个属性的主键请参照我们上面 AddressMapping 示例中的 UseCompositeId 。我不建议使用多重主键并且据我所知Fluent NHibernate 也不支持多重主键。GeneratedBy 是用来指定 POID 生成策略。在这里我们使用的是 GuidComb 。使用 GUID 做主键有非常多的好处具体的内容大家可以参考 Davy Brion在NHForge博客上发表的随笔。Map 只是将属性映射到数据库的列上。如果有需要的话你可以指定 Not.Nullable 和 WithLengthOf 。UniqueKey 指定了列的唯一索引。如果你对多个列指定了相同的名称那么这些列都会变为这个唯一索引的一部分。在这个示例中我们强制要求我们的自然键是唯一的。每个 subject 和 course number 都必须是唯一的。HasMany 是定义了一个一对多的关系你可以指定集合的确切行为。在这里有 Set 和 Bag 两个选项。AsSet 不允许重复的项目AsBag 允许重复的项目默认情况下所有关系都是延迟加载的。就是说当你从数据库中获取到了 course 和其相关联的 sections 并不是马上获取出来而是直到当你访问该属性的时候才会被加载进来如果你从未访问过该属性的话那么它永远都不会被加载这样可以大大的提高性能。这些功能都是用代理来实现的。下面容我们来映射 sections:using NStackExample; using FluentNHibernate.Mapping; namespace NStackExample.Data { public class SectionMapping : ClassMapSection { public SectionMapping() { Id(x x.ID).GeneratedBy.GuidComb(); Map(x x.FacultyName) .WithLengthOf(255); Map(x x.RoomNumber) .WithLengthOf(10); Map(x x.SectionNumber) .WithLengthOf(4) .Not.Nullable() .UniqueKey(SectionNaturalKey); References(x x.Course) .Not.Nullable() .UniqueKey(SectionNaturalKey); References(x x.Term) .Not.Nullable() .UniqueKey(SectionNaturalKey); HasMany(x x.StudentSections) .AsSet() .WithForeignKeyConstraintName(SectionStudentSections); } } }在这里引入了多对一关系的映射可以把它看成一对多关系的另一边。 本示例中就是从孩子 section 到它的父亲 course 的关系。练习完成所有实体类的映射。到这里或许你会在想这个系列是不是会很长很长呢到现在我们甚至连数据库都还没有开始建立。不用担心这些事 NHibernate 会帮我们做的。8小时 or 8分钟?在我没有使用 NHibernate 之前我至少需要一天的时间来建立数据库。这让我很郁闷估计大家也一样很不喜欢浪费这么多时间去建立数据库。不过这样的事情将在今天结束。声明: 如果你尝试使用现有的旧版数据库和数据库架构没有什么调整的机会或者很渺茫Fabio Maulo 的这篇随笔 将告诉你如何选择。首先让我们配置 NHibernate 。在 Fluent NHibernate Wiki 上 有一篇非常好的文章​编辑 介绍了应该如何配置。using FluentNHibernate.Cfg; using FluentNHibernate.Cfg.Db; using NHibernate; using NHibernate.Cfg; using NHibernate.Tool.hbm2ddl; using System.IO; using System.Configuration; namespace NStackExample.Data { public class Configuration { private ISessionFactory m_Factory; private string m_SchemaPath; public Configuration Configure() { m_SchemaPath ConfigurationManager.AppSettings[NStackExample.Data.Configuration.SchemaPath]; m_Factory Fluently.Configure() .Database(MsSqlConfiguration.MsSql2005 .ConnectionString( x x.FromConnectionStringWithKey(NStackExample.Data.Configuration.Db))) .Mappings(x x.FluentMappings.AddFromAssemblyOfCourseMapping() .ExportTo(m_SchemaPath)) .ExposeConfiguration(BuildSchema) .BuildSessionFactory(); return this; } private void BuildSchema(NHibernate.Cfg.Configuration cfg) { SchemaExport SchemaExporter new SchemaExport(cfg); SchemaExporter.SetOutputFile(Path.Combine(m_SchemaPath, schema.sql)); SchemaExporter.Create(true, false); } public ISession OpenSession() { if (m_Factory null) Configure(); return m_Factory.OpenSession(); } } }配置分为两个部分数据库和映射。在本示例中数据库使用的是 SQL 2005连接字符串是从 Web.config 文件中读取的。 所有的映射部分都是从fluent 中读取的没有自动映射。注意我们出口的映射是在 web.config 文件的 appsettings 节中指定的一个目录这会将我们 fluent 的映射分别转换成 hbm.xml文件。这么做是为了方便我们映射部分的调试尤其是在需要 NHibernate 在线帮助的时候。