Fluent NHibernate关系映射
1.好处:Fluent NHibernate让你不再需要去写NHibernate的标准映射文件(.hbm.xml), 方便了我们的代码重构,提供了代码的易读性,并精简了项目代码
实现:
(1)、首先我们通过nuget包管理器添加FluentNHibernate与NHibernate的引用。
(2)、编写实体类,注意实体的属性都是virtual
(3)、实体类编写完成以后我们用代码的方式实现对实体的映射
Fluent NHibernate主要实现的映射关系:一对一、一对多、多对一、多对多
下面介绍几种映射关系中主讲一对多关联属性
2. 一对一映射 HasOne<>()
2.1. 定义支持的关联属性
Cascade 指该对象在进行操作时关联到的子对象的操作类型
PropertyRef属性一般用来解决遗留数据库一对多关系的问题
PropertyRef被关联到此外键的类中的对应属性的名字,若没指定,使用被关联类的主键.
PropertyRef不是数据库表中的字段名,而是定义的类中的属性名
Fetch 在外连接抓取或者序列选择抓取选择其一.
class:被关联的类的名字
constrained 表明该类对应的表对应的数据库表,和被关联的对象所对应的数据库表之间,通过一个外键引用对主键进行约束。 这个选项影响Save()和Delete()在级联执行时的先后顺序以及决定该关联能否被委托
2.1.1.映射范例
用户和用户信息表在实际中是一对一的关系,这两个表之间是通过使用UserID来相互关联的,UserDetail使用的主键ID与User的ID是一致的,所以我们要使用Foregin来获取User的ID,它们有一个共同的ID,在插入Users表的同时也要写入UserDetail表,所以需要添加一对一的限制关系,具体的在User和UsersDetai两表的映射方法如下代码
public UserMap()
{
Table("Users");
Id(u => u.UserID).GeneratedBy.Identity() ;
Map(u => u.UserName);
Map(u => u.Password);
Map(u => u.CreateTime);
HasOne<UserDetail>(u => u.Detail).Cascade.All().PropertyRef("User");
}
public UserDetailMap()
{
Table("UserDetais");
Id(u => u.UserID).Column("UserID").GeneratedBy.Foreign("User");
HasOne<User>(d => d.User).Cascade.All().Constrained();
Map(u => u.LastUpdated).Nullable();
Component<PersonName>(u => u.Name, p =>
{
p.Map(o => o.FirstName).Column("[First Name]");
p.Map(o => o.LastName).Column("[Last Name]");
});
}
2.1.2.补充说明
上面指定了All说明所有的操作都会关联到子表,还有SaveUpdate在添加和更新时关联子表,另外还有None类型不推荐使用此类型因为会出现很多问题。
一对一延迟加载
有时对我们来说,只需要User就可以了,我不需要查询UserDetail,或许你会说,使用以下方式来进行延迟加载:
HasOne<UserDetail>(u => u.Detail).Cascade.All().LazyLoad();
虽然Fluent支持,虽然编译通过,但在创建ISessionFactory的时候,却会抛出异常,因为NHibernate不支持HasOne的不支持一对一的延迟Lazy加载的特性, 可以用:
HasOne<UserDetail>(u => u.Detail).Cascade.All().Fetch.Select();
HasOne<User>(d => d.User).Cascade.All().Constrained();
3. 一对多HasMany<>() / 多对一 References<>()
3.1. 定义支持的关联属性
KeyColumn 表示主键名
Cascade表示级联取值,决定是否把对对象的改动反映到数据库中,所以Cascade对所有的关联关系都起作用, Cascade是操作上的连锁反映,Cascade取值可能是以下:
AllDeleteOrphan 在关联对象失去宿主(解除父子关系)时,自动删除不属于父对象的子对象, 也支持级联删除和级联保存更新.
DeleteOrphan 删除所有和当前对象解除关联关系的对象
All 级联删除, 级联更新,但解除父子关系时不会自动删除子对象.
Delete 级联删除, 但不具备级联保存和更新
None 所有操作均不进行级联操作
SaveUpdate 级联保存(load以后如果子对象发生了更新,也会级联更新). 在执行save/update/saveOrUpdate时进行关联操作,它不会级联删除
Inverse() 所描述的是对象之间关联关系的维护方式,作用是:是否将对集合对象的修改反映到数据库中。Inverse表示对集合对象的修改会被反映到数据库中;
为了维持两个实体类(表)的关系,而添加的一些属性,该属性可能在两个实体类(表)或者在一个独立的表里面,这个要看这双方直接的对应关系了: 这里的维护指的是当主控方进行增删改查操作时,会同时对关联关系进行对应的更新,Inverse是操作上的连锁反映。
ForeignKeyConstraintName 外键约束名
Access NHibernate用来访问属性的策略。
unique: 为外键字段生成一个唯一约束。此外, 这也可以用作PropertyRef的目标属性。这使关联同时具有 一对一的效果。
OptimisticLock 定这个属性在做更新时是否需要获得乐观锁定(OptimisticLock)。 换句话说,它决定这个属性发生脏数据时版本(version)的值是否增长。
NotFound 指定外键引用的数据不存在时如何处理: ignore会将数据不存在作为关联到一个空对象(null)处理。
IsProxy 指定一个类或者接口,在延迟装载时作为代理使用。
Schema
3.1.1. 映射范例
一个用户可以拥有多个订单,一个订单只能拥有一个用户,对于用户来说,不需要每次都加载订单列表,反之订单可能每次都需要加载用户信息。
public UserMap()
{
Table("Users");
Id(u => u.UserID).GeneratedBy.Identity() ;
Map(u => u.UserName);
Map(u => u.Password);
Map(u => u.CreateTime);
HasOne<UserDetail>(u => u.Detail).Cascade.All().Fetch.Select(); //一对一映射用户和用户信息表
HasMany<Order>(u => u.Orders).AsSet().KeyColumn("UserID").Cascade.All(); ////处理一对多关系的映射,一个User可以有多个订单, 关联的数据表进行懒加载,主键名为UserID,级联关系所有操作
}
public OrderMap()
{
Table("Orders");
Id(o => o.OrderID).GeneratedBy.Identity();
Map(o => o.Price);
Map(o => o.State).CustomType<OrderState>();
Map(o => o.Address);
Map(o => o.Coignee);
Map(o => o.CreateTime);
Map(o => o.Zip);
References<User>(o => o.User).Not.LazyLoad().Column("UserID"); //处理多对一关系,多个Order可以属于一个User
}
/// <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(50).Nullable();
Map(m => m.CustomerName).Length(32).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();
}
首先是Tasks表的映射,因为Tasks表是多的一端,所以要添加对Projects表的外键引用关系,另外因为是一种外键引用不关系到父表的操作,所以这里可以使用Cascade.None()。
public TasksMappping()
{
Table("Tasks");
LazyLoad();
Id(x => x.ID).Column("TaskID").GeneratedBy.Identity();
References(x => x.Project).Nullable().Column("ProjectID").Cascade.None();//处理多对一关系,多个Tasks可以属于一个Project
Map(x => x.Name).Nullable();
}
在Projects表中,因为该表中的一个ID会对应多个Tasks所以在添加HasMany方法,来表明Projects和Tasks的多对一的关系,如下代码它会涉及到任务的添加和更新操作,所以需要使用Cascade.SaveUpdate()。
public ProjectsMapping()
{
Table("Projects");
LazyLoad();
Id(x => x.ID).Column("ProjectID").GeneratedBy.Identity();
References(x => x.User).Column("UserID").Cascade.None(); //它的操作不会涉及到Projects的操作
Map(x => x.Name).Nullable();
HasMany(x => x.Task).KeyColumn("ProjectID").LazyLoad().Cascade.SaveUpdate();
//在save或者update Projects的时候会连带着修改Tasks
}
3.1.2.补充说明
一对多的映射,比起一对一来说还相对的简单点,默认是延迟加载,如果项目中,有些地方,需要立即加载,我们也可以使用 FetchMode.Eager 来加载;
4. 多对多映射 HasManyToMany <>()
4.1. 定义支持的关联属性
ParentKeyColumn 定义与A表关联的字段名
ChildKeyColumn 定义与B表关联的字段名
Table 关系表
4.1.1. 映射范例
比如电子商务站的订单和产品的关系
public ProductMap()
{
Table("Products");
Id(p => p.ProductID);
HasManyToMany<Order>(p => p.Orders)
.AsSet()
.LazyLoad()
.ParentKeyColumn("ProductID")
.ChildKeyColumn("OrderID")
.Table("OrderProduct");
Map(p => p.CreateTime);
Map(p => p.Name);
Map(p => p.Price);
}
public OrderMap()
{
Table("Orders");
Id(o => o.OrderID).GeneratedBy.Identity();
HasManyToMany<Product>(o => o.Products)
.AsSet()
.Not.LazyLoad()
.Cascade.All()
.ParentKeyColumn("OrderID")
.ChildKeyColumn("ProductID")
.Table("OrderProduct");
Map(o => o.Price);
Map(o => o.State).CustomType<OrderState>();
Map(o => o.Address);
Map(o => o.Coignee);
Map(o => o.CreateTime);
Map(o => o.Zip);
References<User>(o => o.User).Not.LazyLoad().Column("UserID"); //多个订单属于一个客户
}
这里我们用了一个单独的一个表OrderProduct来保存这个多对多关系
比如 一个Project会有有很多Product,同时一个Product也可能会在多个Project中
public ProjectsMapping()
{
Table("Projects");
LazyLoad();
Id(x => x.ID).Column("ProjectID").GeneratedBy.Identity();
References(x => x.User).Column("UserID").Cascade.None();
Map(x => x.Name).Nullable();
HasMany(x => x.Task).KeyColumn("ProjectID").LazyLoad().Cascade.SaveUpdate();
HasManyToMany(x => x.Product).ParentKeyColumn("ProjectID").ChildKeyColumn("ProductID").Table("ProjectProduct");
}
public ProductMapping()
{
Table("Product");
Id(x => x.ID).Column("ProductID").GeneratedBy.Identity();
Map(x => x.Name).Nullable();
Map(x => x.Color).Nullable();
HasManyToMany(x => x.Project).ParentKeyColumn("ProductID").ChildKeyColumn("ProjectID").Table("ProjectProduct");
}
4.1.2.补充说明
具体添加关联的步骤如下:
(1)在映射的两端同时添加HasManyToMany的关系这样就形成了双向的关联关系
(2)指定映射的ParentKey和ChildKey,一般会将对象本身的ID指定为ParentKey,关联对象的ID指定为ChildKey
(3)指定关联关系的关系表,使用Table方法指定关联表,如上示例的Table("ProjectProduct")。
以上只为突出一对一的双向关联映射,一对多的单向关联和多对多的双向关联关系
Fluent NHibernate关系映射的更多相关文章
- NHibernate系列文章十八:NHibernate关系之一对多(附程序下载)
摘要 这篇文章介绍NHibernate最实用的内容:关系映射. NHibernate的关系映射方式有三种: Set:无序对象集合,集合中每一个元素不能重复. List:有序对象集合,集合中的元素可以重 ...
- [Fluent NHibernate]一对多关系处理
目录 写在前面 系列文章 一对多关系 总结 写在前面 上篇文章简单介绍了,Fluent Nhibernate使用代码的方式生成Nhibernate的配置文件,以及如何生成持久化类的映射文件.通过上篇的 ...
- fluent nhibernate映射的数值类型问题
fluent nhibernate中,数值类型设置不当,就可能会引发一些意想不到错误. 一.引发映射错误 比如,oracle数据库中,字段ID类型是number,结果用codesmith生成代码,实体 ...
- EF使用Fluent API配置映射关系
定义一个继承自EntityTypeConfiguration<>泛型类的类来定义domain中每个类的数据库配置,在这个自定义类的构造函数中使用我们上次提到的那些方法配置数据库的映射. 映 ...
- 【翻译】Fluent NHibernate介绍和入门指南
英文原文地址:https://github.com/jagregory/fluent-nhibernate/wiki/Getting-started 翻译原文地址:http://www.cnblogs ...
- [Fluent NHibernate]第一个程序
目录 写在前面 Fluent Nhibernate简介 基本配置 总结 写在前面 在耗时两月,NHibernate系列出炉这篇文章中,很多园友说了Fluent Nhibernate的东东,也激起我的兴 ...
- Fluent NHibernate
Fluent NHibernate]第一个程序 目录 写在前面 Fluent Nhibernate简介 基本配置 总结 写在前面 在耗时两月,NHibernate系列出炉这篇文章中,很多园友说了Flu ...
- Fluent Nhibernate code frist简单配置
Fluent Nhibernate code frist简单配置 前言 在以前的项目开发过程中使用nhibernate做完orm映射工具需要编写大量的xml映射文件,项目过程中往往会因为一个字段等 ...
- Fluent NHibernate AutoMapping Conventions
前言 使用nhibernate在项目中不管是代码或者xml文件映射方式,如果项目较大编写映射也应该算一笔大的工作量了,使用Fluent Nhibernate自己编写映射规则,将从conventions ...
随机推荐
- cordova Process finished with exit code -1
安装完cordova之后,创建一个测试项目后,运行报Process finished with exit code -1,经过查找原因,是因为gradle没有安装,在http://www.androi ...
- 点击页面div弹窗以外隐藏的两种思路
在本文为大家介绍两种思路实现点击页面其它地方隐藏该div,第一种是对document的click事件绑定事件处理程序.. 第一种思路分两步 第一步:对document的click事件绑定事件处理程序, ...
- 备忘:文本编辑器(z.B. Sublime Text 2)策略,git策略
1.以Sublime Text 2 为例: 新建一个test.py文件,敲完例程 代码 之后,再另存为比如 if.py, list_tuple.py云云 而test.py可以一直用来编辑 2.git ...
- PHP通过串口发短信
随技术进步,短信收发领域按时间先后产生了三种模式:BLOCK MODE,基于AT指令的TEXT MODE,基于AT指令的PDU MODE.其中,TEXT MODE比较简单,多款诺基亚手机均支持此款模式 ...
- jmeter的使用(一)
1.下载jmeter:http://jmeter.apache.org/download_jmeter.cgi 2.启动jmeter,打开jmeter.bat 3.添加线程组 4.添加http请求 5 ...
- 【读书笔记《Android游戏编程之从零开始》】2.Hello,World!
本人看的是PDF文档,很多都是直接都是复制粘贴的记录,简单的记录下笔记! 2.1 创建一个Android项目 Application Name: 应用名称(安装在手机上显示的名字)Project Na ...
- 第11章 Windows线程池(2)_Win2008及以上的新线程池
11.2 Win2008以上的新线程池 (1)传统线程池的优缺点: ①传统Windows线程池调用简单,使用方便(有时只需调用一个API即可) ②这种简单也带来负面问题,如接口过于简单,无法更多去控制 ...
- Android签名机制:生成keystore、签名、查看签名信息
转自:http://www.ourunix.org/post/146.html
- React Native iOS环境搭建
前段时间React Native for Android发布,感觉React Native会越来越多的公司开始研究.使用.所以周六也抽空搭建了iOS的开发环境,以便以后利用空闲的时间能够学习一下. 废 ...
- iOS原生地图开发进阶——使用导航和附近兴趣点检索
iOS原生地图开发进阶——使用导航和附近兴趣点检索 iOS中的mapKit框架对国际化的支持非常出色.在前些篇博客中,对这个地图框架的基础用法和标注与覆盖物的添加进行了详细的介绍,这篇博客将介绍两个更 ...