目录

写在前面

系列文章

一对多关系

总结

写在前面

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

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

系列文章

耗时两月,NHibernate系列出炉

[Fluent NHibernate]第一个程序

一对多关系

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

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

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

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NHibernate;
using FluentNHibernate;
namespace Wolfy.Domain.Entities
{
/// <summary>
/// 描述:客户实体,数据库持久化类
/// 创建人:wolfy
/// 创建时间:2014-10-16
/// </summary>
public class Customer
{
/// <summary>
/// 客户id
/// </summary>
public virtual Guid CustomerID { get; set; }
/// <summary>
/// 客户名字
/// </summary>
public virtual string CustomerName { get; set; }
/// <summary>
/// 地址
/// </summary>
public virtual string CustomerAddress { set; get; }
/// <summary>
/// 版本控制
/// </summary>
public virtual int Version { get; set; }
/// <summary>
/// 一对多关系:一个Customer有一个或者多个Order
/// </summary>
public virtual System.Collections.Generic.ISet<Order> Orders { set; get; }
}
}
 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.Domain.Entities
{ /// <summary>
/// 描述:订单实体,数据库持久化类
/// 创建人:wolfy
/// 创建时间:2014-10-16
/// </summary>
public class Order
{
/// <summary>
/// 订单id
/// </summary>
public virtual Guid OrderID { set; get; }
/// <summary>
/// 下订单时间
/// </summary>
public virtual DateTime OrderDate { set; get; }
/// <summary>
/// 下订单的客户,多对一的关系:orders对应一个客户
/// </summary>
public virtual Customer Customer { set; get; }
}
}

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

Customer.hbm.xml

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

Order.hbm.xml

 <?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain" namespace="Wolfy.Shop.Domain.Entities">
<class name="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain" table="TB_Order">
<id name="OrderID" column="OrderID" type="Guid" unsaved-value="null">
<generator class="assigned" />
</id>
<property name="OrderDate" column="OrderDate" type="DateTime"
not-null="true" />
<!--多对一关系:Orders属于一个Customer-->
<many-to-one name="Customer" column="CustomerID" not-null="true"
class="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain"
foreign-key="FK_TB_Order_TB_Customer" />
</class>
</hibernate-mapping>

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

添加Order的映射类

 using FluentNHibernate.Mapping;
using FluentNHibernate;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Wolfy.Domain.Entities;
namespace Wolfy.Domain.Mapping
{
/// <summary>
/// 描述:订单实体映射类
/// 创建人:wolfy
/// 创建时间:2014-12-07
/// </summary>
public class OrderMapping : ClassMap<Order>
{
public OrderMapping()
{
//指定对应的数据表
Table("TB_Order");
//指定id主键
Id<Guid>("OrderID").GeneratedBy.Guid();
//映射其他的字段
Map(m => m.OrderDate).Nullable();
//处理多对一关系,多个order可以属于一个customer
References<Customer>(r => r.Customer).Column("CustomerID").ForeignKey("CustomerID").Cascade.All();
}
}
}

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

修改Customer映射类

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NHibernate;
using FluentNHibernate.Mapping;
using Wolfy.Domain.Entities;
namespace Wolfy.Domain.Mapping
{
/// <summary>
/// Customer映射实体类,需要集成ClassMap泛型类
/// </summary>
public class CustomerMapping : ClassMap<Customer>
{
/// <summary>
/// 映射关系实体类的构造函数
/// 在构造函数中处理好映射关系
/// </summary>
public CustomerMapping()
{
//指定持久化类对应的数据表
Table("TB_Customer");
//自动增长的id
//Id(i => i.CustomerID);
//映射关系
Id<Guid>("CustomerID").GeneratedBy.Guid();
Map(m => m.CustomerAddress).Length().Nullable();
Map(m => m.CustomerName).Length().Nullable();
Map(m => m.Version);
//处理一对多关系的映射,一个客户可以有多个订单
//关联的数据表进行懒加载,主键名为CustomerID,级联关系所有操作,cascade:All|delete|saveorUpdate
HasMany<Order>(h => h.Orders).LazyLoad().AsSet().KeyColumn("CustomerID").Cascade.All();
}
}
}

单元测试

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

        [TestMethod]
public void AddCustomerTest2()
{
var customer = new Customer()
{
Version = ,
CustomerName = "wolfy",
CustomerAddress = "中国 北京",
CustomerID = Guid.NewGuid()
};
customer.Orders = new HashedSet<Order>();
customer.Orders.Add(new Order() { Customer = customer, OrderDate = DateTime.Now, OrderID = Guid.NewGuid() });
customer.Orders.Add(new Order() { Customer = customer, OrderDate = DateTime.Now, OrderID = Guid.NewGuid() });
var result = _customerData.AddCustomer(customer);
Assert.IsTrue(result);
}

运行测试

生成的sql语句

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

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

xml文件内容为:

 <hibernate-mapping xmlns="urn:nhibernate-mapping-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">
<id type="System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="CustomerID" />
<generator class="guid" />
</id>
<set cascade="all" lazy="true" name="Orders">
<key>
<column name="CustomerID" />
</key>
<one-to-many class="Wolfy.Domain.Entities.Order, Wolfy.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</set>
<property name="CustomerAddress" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="CustomerAddress" length="50" not-null="false" />
</property>
<property name="CustomerName" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="CustomerName" length="32" not-null="false" />
</property>
<property name="Version" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="Version" />
</property>
</class>
</hibernate-mapping>
 <hibernate-mapping xmlns="urn:nhibernate-mapping-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">
<id type="System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="OrderID" />
<generator class="guid" />
</id>
<property name="OrderDate" type="System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="OrderDate" not-null="false" />
</property>
<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">
<column name="CustomerID" />
</many-to-one>
</class>
</hibernate-mapping>

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

级联查询

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

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

         /// <summary>
/// 获得客户信息
/// </summary>
/// <param name="customerID"></param>
/// <returns></returns>
public Customer GetCustomer(Guid customerID)
{
ISession session = FluentNHibernateHelper.GetSession();
return session.Load<Customer>(customerID);
}

测试,并运行

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

测试结果

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

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

测试是否是懒加载?

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

测试结果

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

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

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

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

Fluent Nhibernate的customer映射文件

Nhibernate手写的配置文件

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

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

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

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

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

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

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

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

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

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

如下:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NHibernate;
using FluentNHibernate.Mapping;
using Wolfy.Domain.Entities;
namespace Wolfy.Domain.Mapping
{
/// <summary>
/// Customer映射实体类,需要集成ClassMap泛型类
/// </summary>
public class CustomerMapping : ClassMap<Customer>
{
/// <summary>
/// 映射关系实体类的构造函数
/// 在构造函数中处理好映射关系
/// </summary>
public CustomerMapping()
{
//指定持久化类对应的数据表
Table("TB_Customer");
//自动增长的id
//Id(i => i.CustomerID);
//映射关系
Id<Guid>("CustomerID").GeneratedBy.GuidComb();
//指定主键后一定要加上主键字段的映射关系,不然返回的id为new Guid(),也就是一串0
Map(m => m.CustomerID).Nullable();
Map(m => m.CustomerAddress).Length().Nullable();
Map(m => m.CustomerName).Length().Nullable();
Map(m => m.Version);
//处理一对多关系的映射,一个客户可以有多个订单
//关联的数据表进行懒加载,主键名为CustomerID,级联关系所有操作,cascade:All|delete|saveorUpdate,级联删除时需加上Inverse()
// Inverse the ownership of this entity. Make the other side of the relationship
// responsible for saving.
HasMany<Order>(h => h.Orders).LazyLoad().AsSet().KeyColumn("CustomerID").Cascade.All().Inverse();
}
}
}

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

问题到此解决。

级联删除

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

         /// <summary>
/// 删除指定客户
/// </summary>
/// <param name="customer"></param>
/// <returns></returns>
public bool DeleteCustomer(Customer customer)
{
ISession session = FluentNHibernateHelper.GetSession();
using (var trans = session.BeginTransaction())
{
try
{
session.Delete(customer);
session.Flush();
trans.Commit();
return true;
}
catch (Exception)
{
trans.Rollback();
return false;
}
}
}

单元测试

        [TestMethod]
public void DeleteCustomerTest()
{
//得到删除的对象
Console.WriteLine("查询要删除的customer对象");
var customer = _customerData.GetCustomer(new Guid("0D9862A6-D6FE-4861-BA61-A3FA00DC328C"));
Assert.IsNotNull(customer);
Console.WriteLine("将查询到的对象删除");
var result = _customerData.DeleteCustomer(customer);
Assert.IsTrue(result);
}

测试结果

总结

本文涉及到级联删除,添加,查询以及一对多关系的处理等内容。还是那句话,与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之旅(四)-- 关系(下)

[Fluent NHibernate]一对多关系处理的更多相关文章

  1. [NHibernate]一对多关系(关联查询)

    目录 写在前面 文档与系列文章 一对多查询 总结 写在前面 上篇文章介绍了nhibernate的一对多关系如何配置,以及级联删除,级联添加数据的内容.这篇文章我们将学习nhibernate中的一对多关 ...

  2. [NHibernate]一对多关系(级联删除,级联添加)

    目录 写在前面 文档与系列文章 一对多关系 一个例子 级联删除 级联保存 总结 写在前面 在前面的文章中,我们只使用了一个Customer类进行举例,而在客户.订单.产品中它们的关系,咱们并没有涉及, ...

  3. [NHibernate]多对多关系(关联查询)

    目录 写在前面 文档与系列文章 多对多关系关联查询 总结 写在前面 上篇文章介绍了nhibernate中对一对多关系进行关联查询的几种方式,以及在使用过程需要注意的问题.这篇文章对多对多关系的查询处理 ...

  4. Fluent NHibernate关系映射

    1.好处:Fluent NHibernate让你不再需要去写NHibernate的标准映射文件(.hbm.xml), 方便了我们的代码重构,提供了代码的易读性,并精简了项目代码 实现: (1).首先我 ...

  5. [转]NHibernate之旅(9):探索父子关系(一对多关系)

    本节内容 引入 NHibernate中的集合类型 建立父子关系 父子关联映射 结语 引入 通过前几篇文章的介绍,基本上了解了NHibernate,但是在NHibernate中映射关系是NHiberna ...

  6. 【翻译】Fluent NHibernate介绍和入门指南

    英文原文地址:https://github.com/jagregory/fluent-nhibernate/wiki/Getting-started 翻译原文地址:http://www.cnblogs ...

  7. Fluent NHibernate AutoMapping Conventions

    前言 使用nhibernate在项目中不管是代码或者xml文件映射方式,如果项目较大编写映射也应该算一笔大的工作量了,使用Fluent Nhibernate自己编写映射规则,将从conventions ...

  8. 10.Configure One-to-Many(配置一对多关系)【Code-First系列】

    现在,我们将学习怎么配置一对多的关系. Visit Entity Relationship section to understand how EF manages one-to-one, one-t ...

  9. Fluent Nhibernate之旅(五)--利用AutoMapping进行简单开发

    Fluent Nhibernate(以下简称FN)发展到如今,已经相当成熟了,在Nhibernate的书中也相应的推荐了使用FN来进行映射配置,之前写的FN之旅至今还有很多人会来私信我问题,说来惭愧, ...

随机推荐

  1. Linux – Usermod命令参数解析和实例说明

    usermod 命令修改系统帐户文件来反映通过命令行指定的变化 1. 首先看看usermod都是有哪些参数 [root@hxweb101 ~]$ usermod --help Usage: userm ...

  2. MariaDB的GTID复制和多源复制

    什么是GTID? GTID就是全局事务ID(global transaction identifier ),最初由google实现,官方MySQL在5.6才加入该功能.GTID实际上是由UUID+TI ...

  3. 轻松搞懂WebService工作原理

    用更简单的方式给大家谈谈WebService,让你更快更容易理解,希望对初学者有所帮助. WebService是基于网络的.分布式的模块化组件. 我们直接来看WebService的一个简易工作流程: ...

  4. 洛谷P1156 垃圾陷阱[背包DP]

    题目描述 卡门――农夫约翰极其珍视的一条Holsteins奶牛――已经落了到“垃圾井”中.“垃圾井”是农夫们扔垃圾的地方,它的深度为D(2<=D<=100)英尺. 卡门想把垃圾堆起来,等到 ...

  5. 用extract-text-webpack-plugin提取出来的css文件中背景图片url的不正确的问题

    在一个main.js中require一个scss文件,scss文件中用了背景图片,图片url是用的相对路径,用extract-text-webpack-plugin插件提取出的css文件背景图片路径不 ...

  6. java多线程系类:JUC线程池:02之线程池原理(一)

    在上一章"Java多线程系列--"JUC线程池"01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我 ...

  7. JavaScript中两种类型的全局对象/函数

    这里所说的JavaScript指浏览器环境中的包括宿主环境在内的. 第一种是ECMAScript Global Object,第二种是宿主环境(Host)下的全局对象/函数. 一.核心JavaScri ...

  8. 从客户端(Content="<EM ><STRONG ><U >这是测试这...")中检测到有潜在危险的Request.Form 值。

    说明: 请求验证过程检测到有潜在危险的客户端输入值,对请求的处理已经中止.该值可能指示存在危及应用程序安全的尝试,如跨站点脚本攻击.若要允许页面重写应用程序请求验证设置,请将 httpRuntime  ...

  9. 当Table中td内容为空时,显示边框的办法

    1. 在 table的css里面加: border-collapse:collapse;在 td 的css里面加: empty-cells:show; 2 .最简单的就是 在TD里写个    说明: ...

  10. ionic 开发笔记

    1.AngularJS 外部的控制器(DOM 事件.外部的回调函数如 jQuery UI 空间等)调用了 AngularJS 函数之后,必须调用$apply.在这种情况下,你需要命令 AngularJ ...