zoukankan      html  css  js  c++  java
  • 一对多关系处理

    一对多关系处理

    目录

    写在前面

    系列文章

    一对多关系

    总结

    写在前面

    上篇文章简单介绍了,Fluent Nhibernate使用代码的方式生成Nhibernate的配置文件,以及如何生成持久化类的映射文件。通过上篇的学习你会发现,Fluent Nhibernate仍然需要引用Nhibernate的两个程序集(Nhibernate.dll和Iesi.Collections.dll),所以与Nhibernate最大的区别就在生成配置文件的方式上面,这里关于Nhibernate的特性方面就不再多赘述,可以参考Nhibernate相关的文章。这里主要研究一下Nhibernate中需在配置文件中进行配置的一些特性。那么就先提一下一对多关系的处理。

    测试用的数据库仍然采用学习Nhibernate时,使用的数据库。

    系列文章

    耗时两月,NHibernate系列出炉

    [Fluent NHibernate]第一个程序

    一对多关系

    这里将数据库表及关系图贴出,方面查看:

    这里就使用一个客户可以对应多个订单的一对多关系进行分析。

    首先在持久化类中添加一对多关系的处理

    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 using NHibernate;
     7 using FluentNHibernate;
     8 namespace Wolfy.Domain.Entities
     9 {
    10       /// <summary>
    11     /// 描述:客户实体,数据库持久化类
    12     /// 创建人:wolfy
    13     /// 创建时间:2014-10-16
    14     /// </summary>
    15     public class Customer
    16     {
    17         /// <summary>
    18         /// 客户id
    19         /// </summary>
    20         public virtual Guid CustomerID { get; set; }
    21         /// <summary>
    22         /// 客户名字
    23         /// </summary>
    24         public virtual string CustomerName { get; set; }
    25         /// <summary>
    26         /// 地址
    27         /// </summary>
    28         public virtual string CustomerAddress { set; get; }
    29         /// <summary>
    30         /// 版本控制
    31         /// </summary>
    32         public virtual int Version { get; set; }
    33         /// <summary>
    34         /// 一对多关系:一个Customer有一个或者多个Order
    35         /// </summary>
    36         public virtual System.Collections.Generic.ISet<Order> Orders { set; get; }
    37     }
    38 }
    复制代码
    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace Wolfy.Domain.Entities
     8 {   /// <summary>
     9     /// 描述:订单实体,数据库持久化类
    10     /// 创建人:wolfy
    11     /// 创建时间:2014-10-16
    12     /// </summary>
    13     public class Order
    14     {
    15         /// <summary>
    16         /// 订单id
    17         /// </summary>
    18         public virtual Guid OrderID { set; get; }
    19         /// <summary>
    20         /// 下订单时间
    21         /// </summary>
    22         public virtual DateTime OrderDate { set; get; }
    23         /// <summary>
    24         /// 下订单的客户,多对一的关系:orders对应一个客户
    25         /// </summary>
    26         public virtual Customer Customer { set; get; }
    27     }
    28 }
    复制代码

    在Nhibernate中处理一对多关系的映射文件为:

    Customer.hbm.xml

    复制代码
     1 <?xml version="1.0" encoding="utf-8" ?>
     2 <!--assembly:程序集,namespace:命名空间-->
     3 <hibernate-mapping  xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain"  namespace="Wolfy.Shop.Domain.Entities">
     4   <!--存储过程-->
     5   <class name="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain" table="TB_Customer">
     6     <!--二级缓存-->
     7     <cache usage="read-write"/>
     8     <!--主键-->
     9     <id name="CustomerID" type="Guid" unsaved-value="null">
    10       <column name="CustomerID" sql-type="uniqueidentifier" not-null="true" unique="true" />
    11       <generator class="assigned"></generator>
    12     </id>
    13     <!--版本控制-->
    14     <version name="Version" column="Version" type="integer"  unsaved-value="0"/>
    15     <!--一对多关系:一个客户可以有一个或者多个订单-->
    16     <!--子实体负责维护关联关系-->
    17     <set name="Orders" table="TB_Order" generic="true" inverse="true" cascade="all">
    18       <key column="CustomerID" foreign-key="FK_TB_Order_TB_Customer"></key>
    19       <one-to-many class="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain"/>
    20     </set>
    21 </class>
    22     </hibernate-mapping>
    复制代码

    Order.hbm.xml

    复制代码
     1 <?xml version="1.0" encoding="utf-8" ?>
     2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain"  namespace="Wolfy.Shop.Domain.Entities">
     3   <class name="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain" table="TB_Order">
     4     <id name="OrderID" column="OrderID" type="Guid" unsaved-value="null">
     5       <generator class="assigned" />
     6     </id>
     7     <property name="OrderDate" column="OrderDate" type="DateTime"
     8               not-null="true" />
     9     <!--多对一关系:Orders属于一个Customer-->
    10     <many-to-one  name="Customer" column="CustomerID" not-null="true" 
    11                  class="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain"
    12                  foreign-key="FK_TB_Order_TB_Customer" />
    13      </class>
    14 </hibernate-mapping>
    复制代码

    那么Fluent Nhibernate对于一对多关系是如何处理的呢?

    添加Order的映射类

    复制代码
     1 using FluentNHibernate.Mapping;
     2 using FluentNHibernate;
     3 using System;
     4 using System.Collections.Generic;
     5 using System.Linq;
     6 using System.Text;
     7 using System.Threading.Tasks;
     8 using Wolfy.Domain.Entities;
     9 namespace Wolfy.Domain.Mapping
    10 {
    11     /// <summary>
    12     /// 描述:订单实体映射类
    13     /// 创建人:wolfy
    14     /// 创建时间:2014-12-07
    15     /// </summary>
    16     public class OrderMapping : ClassMap<Order>
    17     {
    18         public OrderMapping()
    19         {
    20             //指定对应的数据表
    21             Table("TB_Order");
    22             //指定id主键
    23             Id<Guid>("OrderID").GeneratedBy.Guid();
    24             //映射其他的字段
    25             Map(m => m.OrderDate).Nullable();
    26             //处理多对一关系,多个order可以属于一个customer
    27             References<Customer>(r => r.Customer).Column("CustomerID").ForeignKey("CustomerID").Cascade.All();
    28         }
    29     }
    30 }
    复制代码

    在Order映射类中处理多对一关系使用References,更符合面向对象的概念,在实体类中的关系就这种引用的关系。

    修改Customer映射类

    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 using NHibernate;
     7 using FluentNHibernate.Mapping;
     8 using Wolfy.Domain.Entities;
     9 namespace Wolfy.Domain.Mapping
    10 {
    11     /// <summary>
    12     /// Customer映射实体类,需要集成ClassMap泛型类
    13     /// </summary>
    14     public class CustomerMapping : ClassMap<Customer>
    15     {
    16         /// <summary>
    17         /// 映射关系实体类的构造函数
    18         /// 在构造函数中处理好映射关系
    19         /// </summary>
    20         public CustomerMapping()
    21         {
    22             //指定持久化类对应的数据表
    23             Table("TB_Customer");
    24             //自动增长的id
    25             //Id(i => i.CustomerID);
    26             //映射关系
    27             Id<Guid>("CustomerID").GeneratedBy.Guid();
    28             Map(m => m.CustomerAddress).Length(50).Nullable();
    29             Map(m => m.CustomerName).Length(32).Nullable();
    30             Map(m => m.Version);
    31             //处理一对多关系的映射,一个客户可以有多个订单
    32             //关联的数据表进行懒加载,主键名为CustomerID,级联关系所有操作,cascade:All|delete|saveorUpdate
    33             HasMany<Order>(h => h.Orders).LazyLoad().AsSet().KeyColumn("CustomerID").Cascade.All();
    34         }
    35     }
    36 }
    复制代码

    单元测试

    描述:创建一个客户对象,并向客户的订单集合中添加两个订单,并断言结果为true,即添加成功。

    复制代码
     1        [TestMethod]
     2         public void AddCustomerTest2()
     3         {
     4             var customer = new Customer()
     5             {
     6                 Version = 1,
     7                 CustomerName = "wolfy",
     8                 CustomerAddress = "中国 北京",
     9                 CustomerID = Guid.NewGuid()
    10             };
    11             customer.Orders = new HashedSet<Order>();
    12             customer.Orders.Add(new Order() { Customer = customer, OrderDate = DateTime.Now, OrderID = Guid.NewGuid() });
    13             customer.Orders.Add(new Order() { Customer = customer, OrderDate = DateTime.Now, OrderID = Guid.NewGuid() });
    14             var result = _customerData.AddCustomer(customer);
    15             Assert.IsTrue(result);
    16         }
    复制代码

    运行测试

    生成的sql语句

    跟使用配置文件的对比可参考我这篇文章:http://www.cnblogs.com/wolf-sun/p/4068749.html

    那么看一下在c盘生成的xml文件:

    xml文件内容为:

    复制代码
     1 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
     2   <class xmlns="urn:nhibernate-mapping-2.2" name="Wolfy.Domain.Entities.Customer, Wolfy.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="TB_Customer">
     3     <id type="System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     4       <column name="CustomerID" />
     5       <generator class="guid" />
     6     </id>
     7     <set cascade="all" lazy="true" name="Orders">
     8       <key>
     9         <column name="CustomerID" />
    10       </key>
    11       <one-to-many class="Wolfy.Domain.Entities.Order, Wolfy.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    12     </set>
    13     <property name="CustomerAddress" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
    14       <column name="CustomerAddress" length="50" not-null="false" />
    15     </property>
    16     <property name="CustomerName" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
    17       <column name="CustomerName" length="32" not-null="false" />
    18     </property>
    19     <property name="Version" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
    20       <column name="Version" />
    21     </property>
    22   </class>
    23 </hibernate-mapping>
    复制代码
    复制代码
     1 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
     2   <class xmlns="urn:nhibernate-mapping-2.2" name="Wolfy.Domain.Entities.Order, Wolfy.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="TB_Order">
     3     <id type="System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     4       <column name="OrderID" />
     5       <generator class="guid" />
     6     </id>
     7     <property name="OrderDate" type="System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     8       <column name="OrderDate" not-null="false" />
     9     </property>
    10     <many-to-one cascade="all" class="Wolfy.Domain.Entities.Customer, Wolfy.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" foreign-key="CustomerID" name="Customer">
    11       <column name="CustomerID" />
    12     </many-to-one>
    13   </class>
    14 </hibernate-mapping>
    复制代码

    通过与nhibernate手写的xml映射文件,在结构上,内容上大概相似。

    级联查询

    查询客户信息时,将查询客户下的所有的订单

    在CustomerData层添加如下方法并进行测试

    复制代码
     1         /// <summary>
     2         /// 获得客户信息
     3         /// </summary>
     4         /// <param name="customerID"></param>
     5         /// <returns></returns>
     6         public Customer GetCustomer(Guid customerID)
     7         {
     8             ISession session = FluentNHibernateHelper.GetSession();
     9             return session.Load<Customer>(customerID);
    10         }
    复制代码

    测试,并运行

    复制代码
    1        [TestMethod]
    2         public void GetCustomerTest()
    3         {
    4             var customer = _customerData.GetCustomer(new Guid("EB99D91A-F504-4BB4-8E52-EA10C96E6637"));
    5             Assert.IsNotNull(customer);
    6             Console.WriteLine("该客户的订单数量:" + customer.Orders.Count);
    7         }
    复制代码

    测试结果

    这先查询TB_Customer表,然后需要用到Order的数量,所以又查询了TB_Order表。回头看看我们在Customer的映射类中指定了使用懒加载的方式

    1  HasMany<Order>(h => h.Orders).LazyLoad().AsSet().KeyColumn("CustomerID").Cascade.All();

    测试是否是懒加载?

    复制代码
    1         [TestMethod]
    2         public void GetCustomerTest()
    3         {
    4             var customer = _customerData.GetCustomer(new Guid("EB99D91A-F504-4BB4-8E52-EA10C96E6637"));
    5             Assert.IsNotNull(customer);
    6             Console.WriteLine("客户姓名:"+customer.CustomerName);
    7             // Console.WriteLine("该客户的订单数量:" + customer.Orders.Count);
    8         }
    复制代码

    测试结果

    在这个测试里面,我们并没有用到Order,所以就查询customer的信息,需要的时候再去查询Order表。在使用时发现,如果不输出CustomerName,连查询TB_Customer表的sql也不生成,充分说明,Nhibernate中的懒加载真够懒的。

    在测试的时候,顺手把customer的属性都输出了,发现一个自认为不科学的地方:

    发现了吧,很奇怪,对比一下生成的映射文件,和手写的Nhibernate的配置文件,将Customer和Order的映射类的id生成规则修改为:

    1  Id<Guid>("CustomerID").GeneratedBy.Assigned();

    Fluent Nhibernate的customer映射文件

    Nhibernate手写的配置文件

    1 <id name="CustomerID" type="Guid" unsaved-value="null">
    2       <column name="CustomerID" sql-type="uniqueidentifier" not-null="true" unique="true" />
    3       <generator class="assigned"></generator>
    4     </id>

    你会发现是何其的相似啊,为什么查询到Customer对象能拿到CustomerName却拿不到id呢?
    看看Assigned的注释:

    1 // 摘要: 
    2         //     lets the application to assign an identifier to the object before Save()
    3         //     is called.
    4         public TParent Assigned();

    大概意思就是:让应用程序在保存方法被调用前为对象指定一个标识符。

    关于guid类型作为主键的策略,为什么会返回Guid(0),可以参考这篇文章

    http://nhforge.org/blogs/nhibernate/archive/2009/05/21/using-the-guid-comb-identifier-strategy.aspx

    如果在映射类中设置主键的生成策略为:

    1  Id<Guid>("CustomerID").GeneratedBy.GuidComb();

    在添加数据的时候,可以不用指定CustomerID=Guid.NewGuid();就好像是自增的int类型的主键一样。
    那怎么才能拿到这个id呢?手动写的配置文件确实是可以拿到id的,而Fluent Nhibernate不可以。

    找了很久也没找到解决的办法,突然那么灵机一动,既然你返回的是一个0串,也就是没有赋值的情况,那么何不指定你的映射关系呢?

    如下:

    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 using NHibernate;
     7 using FluentNHibernate.Mapping;
     8 using Wolfy.Domain.Entities;
     9 namespace Wolfy.Domain.Mapping
    10 {
    11     /// <summary>
    12     /// Customer映射实体类,需要集成ClassMap泛型类
    13     /// </summary>
    14     public class CustomerMapping : ClassMap<Customer>
    15     {
    16         /// <summary>
    17         /// 映射关系实体类的构造函数
    18         /// 在构造函数中处理好映射关系
    19         /// </summary>
    20         public CustomerMapping()
    21         {
    22             //指定持久化类对应的数据表
    23             Table("TB_Customer");
    24             //自动增长的id
    25             //Id(i => i.CustomerID);
    26             //映射关系
    27             Id<Guid>("CustomerID").GeneratedBy.GuidComb();
    28             //指定主键后一定要加上主键字段的映射关系,不然返回的id为new Guid(),也就是一串0
    29             Map(m => m.CustomerID).Nullable();
    30             Map(m => m.CustomerAddress).Length(50).Nullable();
    31             Map(m => m.CustomerName).Length(32).Nullable();
    32             Map(m => m.Version);
    33             //处理一对多关系的映射,一个客户可以有多个订单
    34             //关联的数据表进行懒加载,主键名为CustomerID,级联关系所有操作,cascade:All|delete|saveorUpdate,级联删除时需加上Inverse()
    35             //     Inverse the ownership of this entity. Make the other side of the relationship
    36             //     responsible for saving.
    37             HasMany<Order>(h => h.Orders).LazyLoad().AsSet().KeyColumn("CustomerID").Cascade.All().Inverse();
    38         }
    39     }
    40 }
    复制代码

    从上面的代码,可以看出指定id后,又指定了CustomerID的映射关系。这样就可以拿到id了。对于从nhibernate过来的,估计已经思维定势了,觉得指定id就已经指定了id的映射关系了,确实没必要再去映射了。
    测试结果

    问题到此解决。

    级联删除

    级联删除的时候,需指定Cascade和Inverse。具体测试可参考Nhibernate中一对多关系级联删除内容。

    复制代码
     1         /// <summary>
     2         /// 删除指定客户
     3         /// </summary>
     4         /// <param name="customer"></param>
     5         /// <returns></returns>
     6         public bool DeleteCustomer(Customer customer)
     7         {
     8             ISession session = FluentNHibernateHelper.GetSession();
     9             using (var trans = session.BeginTransaction())
    10             {
    11                 try
    12                 {
    13                     session.Delete(customer);
    14                     session.Flush();
    15                     trans.Commit();
    16                     return true;
    17                 }
    18                 catch (Exception)
    19                 {
    20                     trans.Rollback();
    21                     return false;
    22                 }
    23             }
    24         }
    复制代码

    单元测试

    复制代码
     1        [TestMethod]
     2        public void DeleteCustomerTest()
     3         {
     4             //得到删除的对象
     5             Console.WriteLine("查询要删除的customer对象");
     6             var customer = _customerData.GetCustomer(new Guid("0D9862A6-D6FE-4861-BA61-A3FA00DC328C"));
     7             Assert.IsNotNull(customer);
     8             Console.WriteLine("将查询到的对象删除");
     9             var result = _customerData.DeleteCustomer(customer);
    10             Assert.IsTrue(result);
    11         }
    复制代码

    测试结果

    总结

    本文涉及到级联删除,添加,查询以及一对多关系的处理等内容。还是那句话,与Nhibernate的区别就是在生成配置文件及持久化类的映射文件的方式上。关于Fluent Nhibernate的其他的操作也是在映射文件上与Nhibernate设置上的区别(个人认为)。这里就不再进行介绍了。收集了一些这方面的文章,供大家学习,确实在目前的项目中没有用到,这东西,现在学了,也记不住,不经实践,很难掌握。

    [原创]Fluent NHibernate之旅(三)-- 继承

    [原创]Fluent NHibernate之旅

    [原创]Fluent NHibernate之旅二--Entity Mapping

    Fluent NHibernate RC 1.0 --升级内容

    [原创]Fluent NHibernate之旅(三)-- 继承

    [原创]Fluent NHibernate之旅(四)-- 关系(上)

    [原创]Fluent NHibernate之旅(四)-- 关系(中)

    [原创]Fluent NHibernate之旅(四)-- 关系(下)

    博客地址: http://www.cnblogs.com/wolf-sun/
    博客版权: 本文以学习、研究和分享为主,欢迎转载,但必须在文章页面明显位置给出原文连接。
    如果文中有不妥或者错误的地方还望高手的你指出,以免误人子弟。如果觉得本文对你有所帮助不如【推荐】一下!如果你有更好的建议,不如留言一起讨论,共同进步!
    再次感谢您耐心的读完本篇文章。
    技术交流群: 329276418
     
  • 相关阅读:
    CodeForces 219D Choosing Capital for Treeland (树形DP)
    POJ 3162 Walking Race (树的直径,单调队列)
    POJ 2152 Fire (树形DP,经典)
    POJ 1741 Tree (树的分治,树的重心)
    POJ 1655 Balancing Act (树的重心,常规)
    HDU 2196 Computer (树形DP)
    HDU 1520 Anniversary party (树形DP,入门)
    寒门子弟
    JQuery选择器(转)
    (四)Web应用开发---系统架构图
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4150095.html
Copyright © 2011-2022 走看看