One to One 的数据库模型设计与NHibernate配置
在数据库模型设计中,最基本的实体关系有三种:一对一、一对多、多对多。关于一对多和多对多使用的情况较多,之前也有过一些讨论,现在来说明一下在数据库中一对一的模型设计。 首先,关系数据库中使用外键来表示一对多,使用中间表和两边的外键来表示多对多,而一对一的话有三种表示方式:一种是使用相同的主键值,第二种是使用单边的外键,第三种就是使用双边外键。 1.主键关联 比如我们在做一个ER系统时,设计了一个Employee表保存员工的基本信息(主表),另外有一个EmployeePhoto表(外表),用于保存员工的证件照,员工和照片之间就是一对一的关系。 Employee:Entity
{
EmployeeNumber { get; set; }
Name { get; set; }
EmployeePhoto EmployeePhoto { get; set; }
}
EmployeePhoto:Entity
{
Photo { get; set; }
下面是FluentNHibernate的Mapping配置: EmployeeMap : ClassMap
这里需要注意的是EmployeePhoto的主键,不再是普通的生成方式,而是要选择通过Employee做外键生成。 关于NHibernate 的one to one标签上的constrained="true",该标签在外表上设置,千万不要在主表上设置。就是说明这个表的主键与另一个表的主键建立外键约束,也就是说在生成SQL脚本时,会为这个表创建外键,如果不加,是不会创建外键的。另外还有一个作用,就是在查询外表时,如果没有设置该属性,那么就会Join主表,而设置了该属性,就只需要查询外表。 在主键关联的情况下,如果从主表中移除从表的引用,这个时候保存主表,是不会删除从表的,也不会删除这个一对一的关系的。也就是说,我们不能单独保留Employee和Photo表,同时还要去掉两者之间的关系。 2.单向外键关联 比如我们做个中学的管理系统,设计了一个Class表保存班级,另一个Classroom表保存教室,班级和教室是一对一的关系,一个班级有且仅有一个教室,一个教室属于0到1个班级。 Class : Entity,IPermanent
{
IsDeleted { get; set; }
ClassName { get; set; }
StudentCount { get; set; }
ToString()
{
+ Id + + ClassName;
}
Classroom Classroom { get; set; }
}
Classroom:Entity,IPermanent
{
Building { get; set; }
RoomNumber { get; set; }
ToString()
{
+ Id + + Building + + RoomNumber;
}
IsDeleted { get; set; }
Class Class { get; set; }
}
Mapping的代码是: ClassMap : ClassMap
这里两个表中只需要有一个表持有对方的主键作为外键即可,我们可以在CLASS表中添加CLASSROOM_ID来作为外键,也可以在CLASSROOM表中添加CLASS表作为外键。选择哪一个好呢?如果相互之间都对应的是0到1个对方,那么其实选哪边都无所谓,但是如果我们假定一个Class必须要对应一个Classroom,而一个Classroom可以对应0到1个Class,那么我们就必须在CLASS表中添加CLASSROOM_ID,因为我们必须先创建Classroom,然后再创建Class,然后可以在数据库中将CLASS表中的CLASSROOM_ID设置为不允许为空(当然,设置为允许为空也没有问题,这样可以帮助NHibernate在级联保存时能够正确保存而不报错)。 单向外键关联时,如果数据库允许CLASSROOM_ID为空,那么是可以打断Class和Classroom的关系的,而使得这两个对象独立存在,这一点是和主键关联所不一样的地方。 另外,这个配置还存在一个问题,就是对于一个存在的Classroom A,我接下来建立Class X,Class Y,都可以将这些 Class的班级指向A,同时这也是保存成功的。但是这显然是不对的,我们需要的是一对一,不是一对多。如果查询Classroom A的Class属性,那么就会报错,因为根本不知道应该是X还是Y。所以我们需要在CLASS表的CLASSROOM_ID上建立唯一约束,体现在Mapping上就是: References(x => x.Classroom,"CLASSROOM_ID").Cascade.All().Unique(); 这样我们在保存X和Y的时候,就只能保存成功一个,第二个保存时就会报错。 这其实又带来了另外一个问题,这可能是NHibernate没有考虑到的地方,那就是我们采用的是软删除,也就是说根本不会从数据库删除数据,只是把IS_DELETED置为1。 那么,我们如果先保存了A和X的关系,接下来由于X被取消,所以我删除了X,接下来添加Y与A关联就会失败。所以需要取消唯一约束,就可以保存Y了,但是在取A的Class属性时仍然会出现异常,取不出正确的Class Y,这个暂时无解。 3.双向外键关联 就是说CLASS表中有CLASSROOM_ID,然后在CLASSROOM表中也有CLASS_ID。这是非常不推荐的方式,一来导致数据维护重复,二来导致数据可能存在不一致。所以,这里我就不再累述这种方案的实现了。 示例代码下载: (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net-web-api – Xamarin.Android上的StringContent为空
- asp.net – 从ASP .Net下载文件时,文本文件会附加HTML内容
- ASP.NET获取请求的url信息汇总
- asp.net – 代码无效或已过期.使用API??调用显式使会话无效
- asp.net-mvc – IIS8中最佳的“动态IP限制设置”是什么
- model-view-controller – 有没有办法在ASP.MVC 2中设置Hid
- ASP.net MVC4:在局部视图中使用不同的模型?
- Asp.net C#具有特殊字符的电子邮件地址
- asp.net – 如何修复此页面的状态信息无效,可能已损坏
- asp.net – 用于Webapp和桌面/移动应用程序的OpenID OAuth身
- 了解ASP.Net会话的生命周期
- asp.net-mvc-3 – Razor:隐藏密码控制(2页注册)
- asp.net-mvc – 当model是父模型上的属性且为nul
- ASP.NET Core / MVC 6 ViewModel中的依赖注入(DI
- 如何在Azure中部署和托管.NET Core自托管控制台应
- asp.net-core – 如何在ASP.NET Core中共享汇编信
- asp.net-mvc-4 – 在ASP.NET MVC中包文件位置
- asp.net – SignalR/signalr/hubs 404未找到
- asp.net-mvc-3 – 为什么会话状态超时会覆盖我的
- [ASP.NET MVC] 利用动态注入HTML的方式来设计复杂