zoukankan      html  css  js  c++  java
  • NHibernate初学者指南(5):映射模型到数据库之方式一

    映射类型

    当使用Nhibernate作为我们的ORM框架时,有四种主要的映射类型:

    1. 基于XML的映射。
    2. 基于特性的映射。
    3. Fluent映射。
    4. 基于约定的映射,有时也称为自动映射。

    在接下来的三篇文章里,将详细讲解除“基于特性映射”以外的映射类型。

    Fluent映射

    本篇文章的所有操作需要在NHibernate初学者指南(3):创建Model中创建的代码基础上完成,代码下载地址:点击这里下载。

    为了能够使用Fluent方式映射我们的模型到底层的数据库,需要添加Fluent NHibernate和NHibernate两个引用,这两个程序集可以在NHibernate初学者指南(2):一个完整的例子中创建的lib文件中找到。

    引用程序集

    映射类

    要映射实体到数据库表,必须往我们的项目里添加一个继承自ClassMap<T>(定义在FluentNHibernate.dll程序集中)的类。例如映射Product实体,我们应该定义一个如下面代码的映射类:

    public class ProductMap : ClassMap<Product>
    {
        public ProductMap()
        {
            // 这里定义映射信息
        }
    }

    映射信息必须定义在映射类的默认构造函数中。注意ClassMap<T>要求映射的类作为泛型参数,本例中是Product类。

    实体级别设置

    如果想让数据库理解领域模型,我们需要指定最起码的详细信息。然而,为了额外的灵活性,我们有能力明确指定更多的详细信息。作为一个例子,如果映射到的表名称不同于实体的类名称,我们可以定义表的名称。可以使用下面的代码定义:

    public ProductMap()
    {
        // 这里定义映射信息
        Table("tbl_Product");
    }

    如果一个特定的表驻留在另一个架构(schema)中,我们还可以使用下面的代码指定相应的架构:

    public ProductMap()
    {
        // 这里定义映射信息
        //Table("tbl_Product");
        Schema("OrderingSystem");
    }

    默认情况下,Fluent NHibernate配置实体为延迟加载(lazy loaded)。如果不喜欢这样,可以使用下面的代码改变默认的行为:

    public ProductMap()
    {
        // 这里定义映射信息
        //Table("tbl_Product");
        //Schema("OrderingSystem");
        Not.LazyLoad();
    }

    还有更多的信息可以指定,但不在本次介绍的范围之内。

    ID列

    通常我们首先要映射的是实体的ID。这需要使用Id方法完成。这个方法有很多选项,使用最简单的形式,如下面的代码:

    public ProductMap()
    {
        Id(x => x.ID).GeneratedBy.HiLo("1000");
    }

    使用上面的代码,我们就告诉了NHibernate,Product实体的Id属性应该作为主键映射,新的ID会由NHibernate的HiLo生成器自动生成。只要数据库里我们的字段为Id,一切处理正常。然而,如果命名为ID或PRODUCT_ID呢?答案是根本就不会处理了。这种情况下,必须添加可选的列参数指定列的名称:

    Id(x => x.ID, "PRODUCT_ID").GeneratedBy.HiLo("1000");

    另外,还可以写成下面的形式:

    Id(x => x.ID).Column("PRODUCT_ID").GeneratedBy.HiLo("1000");

    ID还有一个经常使用的可选属性:UnsavedValue。它指定一个新对象在持久化到数据库之前应该返回什么值。如下面的代码:

    Id(x => x.ID, "PRODUCT_ID").GeneratedBy.HiLo("1000").UnsavedValue(-1);

    注意我们没有显示的指定ID的类型。Fluent NHibernate可以通过反射获得它的类型。总之,我们应该避免指定冗余的信息,因为没有必要,只会使你的代码杂乱。

    属性

    映射简单的值属性,我们使用Map方法。简单值属性是含有基本.NET类型之一的属性,如int,float,double,decimal,bool,string或DateTime。这样的属性简单的映射到数据库列。例如,Product实体的Name属性,使用最简单的形式,如下面的代码所示:

    Map(x => x.Name);

    上面的代码告诉NHibernate映射Name属性到数据库中有相同名字的列。SQL Server中数据库列的类型是nvarchar,数据库列的最大长度是255。此外,列是可空的。

    如果列的最大长度为50,不为空,则如下面代码所示:

    Map(x => x.Name).Length(50).Not.Nullable();

    如果数据库列的名字和属性的名字不同,可以使用下面的代码指定,跟Id是一样的:

    Map(x => x.Name, "PRODUCT_NAME").Length(50).Not.Nullable();

    有时,NHibernate不能自动的理解我们如何映射某个属性,必须指定我们的意图。作为例子,我们想映射一个枚举类型的属性。假设实体Product有一个ProductTypes类型的属性ProducType,ProductTypes是一个枚举,如下:

    public enum ProductTypes
    {
        ProductTypeA,
        ProductTypeB
    }

    然后我们可以添加一个CustomType方法映射这个属性,如下:

    Map(x => x.ProductType).CustomType<ProductTypes>();

    在这种情况下,因为enum背后的类型是整型(默认是int),对SQL Server来说数据库字段会是int类型的。NHibernate会自动的在领域模型的enum值和数据库中的int类型的数字之间转换。

    另一个经常使用的是将bool类型的属性映射为数据库中char(1)类型的列,true就是'Y',false是'N'。NHibernate为此定义了一个特殊的映射,称为YesNo。例如人,我们可以使用下面的代码映射Product实体的Discontinued属性:

    Map(x => x.Discontinued).CustomType("YesNo");

    Fluent NHibernate非常完整,为我们映射简单值属性提供了很大的灵活性。在本节中,只讨论了最重要和最常用的设置。

    引用

    映射引用另一个实体的属性,如Product实体的Category属性,可以使用下面的代码简单的映射这种关系:

    References(x => x.Category);

    这种类型的映射表示“多对一”关系,在领域模型中经常使用。

    大多数情况下,我们想强制定义这个引用,可以使用下面的代码:

    References(x => x.Category).Not.Nullable();

    如果我们不显示指定,Product表到Category表的外键名称会是Category_Id,默认约定是属性的名称和ID用下划线组合的。当然,可以使用下面的代码修改外键的名称:

    References(x => x.Category).Not.Nullable().ForeignKey("ProductId");

    最后一个值得注意的设置是指定引用为唯一的。如下面的代码:

    References(x => x.Category).Not.Nullable().Unique();

    集合

    现在说说一对多关系。让我们看一个订单包含line item对象的集合的例子。line item集合的映射如下面的代码:

    public OrderMap()
    {
        HasMany(x => x.LineItems);
    }

    通常定义HasMany映射为Inverse。在order和line items的一对多关系中,如果不标记集合为inverse,那么NHibernate会执行一个额外的UPDATE操作。当标记为Inverse,那么NHibernate会首先持久化拥有集合的实体(order),然后持久化集合中的实体(line items)。避免了额外的UPDATE操作。如下面的代码:

    HasMany(x => x.LineItems).Inverse();

    还有一个重要的配置就是NHibernate的级联操作。大多数情况下,会使用这个配置,如下面的代码:

    HasMany(x => x.LineItems).Inverse().Cascade.AllDeleteOrphan();

    映射多对多关系

    我们还可以映射多对多关系。在领域模型中,Book实体包含Author子对象的集合,同时,Author实体也包含Book子对象的集合。定义这样的映射代码如下:

    HasMany(x =>x.Author); 

    如果指定中间表的名称,如下面的代码:

    HasMany(x => x.Author).Table("BookAuthor");

    映射值对象

    NHibernate初学者指南(3):创建Model一篇中,我们了解到值对象是领域模型非常重要的概念,所以,我们需要知道如何映射含有值对象作为类型的属性。拿Customer实体的Name属性作为例子。首先,我们可能想映射值对象自身。我们定义一个NameMap类,它继承自ComponentMap<Name>,如下面代码所示:

    public class NameMap : ComponentMap<Name>
    {
        ...}

    然后,在这个类的默认构造函数中添加映射的详细信息,如下面代码所示:

    public NameMap()
    {
        Map(x => x.LastName).Not.Nullable().Length(50);
        Map(x => x.MiddleName).Length(50);
        Map(x => x.FirstName).Not.Nullable().Length(50);
    }

    一旦映射了值对象,我们就可以定义Customer实体的Name属性的映射。在CustomerMap类的构造函数中添加如下代码:

    Component(x => x.CustomerName);

    讲完了Fluent映射所需的基本知识,下面完成我们模型的映射。

    实战时间–映射我们的Model

    1. 按照本篇文章开始的要求,下载源码,添加引用。

    2. 在NHibernate初学者指南(3):创建Model中我们使用了GUID类型的ID,这一篇中,我们想使用int类型的ID,打开Entity<T>类,修改属性ID为:

    public int ID { get; private set; }

    3. 将Entity<T>基类中的Guid.Empty使用0(零)替换。

    4. 在项目中新建一个文件夹Mappings。

    5. 映射Employee实体,新建一个EmployeeMap类,继承自ClassMap<Employee>,代码如下:

    public class EmployeeMap : ClassMap<Employee>
    {
        public EmployeeMap()
        {
            Id(x => x.ID).GeneratedBy.HiLo("100");
            Component(x => x.Name);
        }
    }

    6. 映射Name值对象,新建NameMap类,继承自ComponentMap<Name>,代码如下:

    public class NameMap : ComponentMap<Name>
    {
        public NameMap()
        {
            Map(x => x.LastName).Not.Nullable().Length(100);
            Map(x => x.MiddleName).Length(100);
            Map(x => x.FirstName).Not.Nullable().Length(100);
        }
    }

    7. 映射Customer实体,新建一个CustomerMap类,继承自ClassMap<Customer>,代码如下:

    public class CustomerMap : ClassMap<Customer>
    {
        public CustomerMap()
        {
            Id(x => x.ID).GeneratedBy.HiLo("100");
            Component(x => x.CustomerName);
            Map(x => x.CustomerIdentifier).Not.Nullable().Length(50);
            Component(x => x.Address);
            HasMany(x => x.Orders).Access.CamelCaseField().Inverse().Cascade.AllDeleteOrphan();
        }
    }

    这里需要解释一下,为了使NHibernate直接访问Customer类的私有字段orders,使用了Access.CamelCaseField()。Cascade.AllDeleteOrphan()告诉NHibernate从customer到它的orders自动的级联所有的插入、更新或删除操作。

    8. 映射值对象Address,新建一个AddressMap类,继承自ComponentMap<Address>,代码如下:

    public class AddressMap : ComponentMap<Address>
    {
        public AddressMap()
        {
            Map(x => x.Line1).Not.Nullable().Length(50);
            Map(x => x.Line2).Length(50);
            Map(x => x.ZipCode).Not.Nullable().Length(10);
            Map(x => x.City).Not.Nullable().Length(50);
            Map(x => x.State).Not.Nullable().Length(50);
        }
    }

    9. 映射Order实体,新建一个OrderMap类,继承自ClassMap<Order>,代码如下:

    public class OrderMap : ClassMap<Order>
    {
        public OrderMap()
        {
            Id(x => x.ID).GeneratedBy.HiLo("100");
            Map(x => x.OrderDate).Not.Nullable();
            Map(x => x.OrderTotal).Not.Nullable();
            References(x => x.Customer).Not.Nullable();
            References(x => x.Employee).Not.Nullable();
            HasMany(x => x.LineItems).Inverse().Cascade.AllDeleteOrphan();
        }
    }

    10. 映射LineItem实体,新建一个LineItem类,继承自ClassMap<LineItem>,代码如下:

    public class LineItemMap : ClassMap<LineItem>
    {
        public LineItemMap()
        {
            Id(x => x.ID).GeneratedBy.HiLo("100");
            References(x => x.Order).Not.Nullable();
            References(x => x.Product).Not.Nullable();
            Map(x => x.Quantity).Not.Nullable();
            Map(x => x.UnitPrice).Not.Nullable();
            Map(x => x.Discount).Not.Nullable();
        }
    }

    11. 最后,映射Product实体,新建一个ProductMap类,继承自ClassMap<Product>,代码如下:

    public class ProductMap : ClassMap<Product>
    {
        public ProductMap()
        {
            Id(x => x.ID).GeneratedBy.HiLo("100");
            Map(x => x.Name).Not.Nullable().Length(50);
            Map(x => x.Description).Length(4000);
            Map(x => x.UnitPrice).Not.Nullable();
            Map(x => x.ReorderLevel).Not.Nullable();
            Map(x => x.Discontinued);
        }
    }

    至此,我们完成了模型的映射。现在NHibernate可以理解如何在数据库中组织来自domain的数据了。

    作者:BobTian
    出处http://nianming.cnblogs.com/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    欢迎访问我的个人博客:程序旅途
  • 相关阅读:
    缠中说禅 摘选
    laravel中不使用 remember_token时退出报错,如何解决?
    关于Ubuntu拒绝root用户ssh远程登录
    laravel中类似于thinkPHP中trace功能
    js原生语法实现表格操作
    使用clear来清除localStorage保存对象的全部数据
    JS设置CSS样式的集中方式
    thinkphp两表联查并且分页
    生于忧患,死于安乐 (先秦:孟子及其弟子)
    在对年轻人最不友好的环境中,刘裕起于阡陌,成就霸业
  • 原文地址:https://www.cnblogs.com/nianming/p/2248716.html
Copyright © 2011-2022 走看看