One to One 的数据库模型设计与NHibernate配置

在数据库模型设计中,最基本的实体关系有三种:一对一、一对多、多对多。关于一对多和多对多使用的情况较多,之前也有过一些讨论,现在来说明一下在数据库中一对一的模型设计。

首先,关系数据库中使用外键来表示一对多,使用中间表和两边的外键来表示多对多,而一对一的话有三种表示方式:一种是使用相同的主键值,第二种是使用单边的外键,第三种就是使用双边外键。

1.主键关联

比如我们在做一个ER系统时,设计了一个Employee表保存员工的基本信息(主表),另外有一个EmployeePhoto表(外表),用于保存员工的证件照,员工和照片之间就是一对一的关系。

public class Employee:Entity
{
 public virtual string EmployeeNumber { get; set; }
 public virtual string Name { get; set; }
 public virtual EmployeePhoto EmployeePhoto { get; set; }
}
public class EmployeePhoto:Entity
{
 /// <summary> /// 员工照片,应该是二进制数据,这里只是一个例子,为了方便,所以用String类型 /// </summary> public virtual string Photo { get; set; }

 public virtual Employee Employee { get; set; }
}

下面是FluentNHibernate的Mapping配置:

public class EmployeeMap : ClassMap<Employee>
{
 public EmployeeMap()
 {
 Table("EMPLOYEE");
 Id(x => x.Id, "EMPLOYEE_ID").GeneratedBy.HiLo("1000000000");
 Map(x => x.EmployeeNumber, "EMPLOYEE_NUMBER").Not.Nullable();
 Map(x => x.Name, "NAME").Not.Nullable();
 HasOne(x => x.EmployeePhoto).Cascade.All();
 }
}
public class EmployeePhotoMap : ClassMap<EmployeePhoto>
{
 public EmployeePhotoMap()
 {
 Table("EMPLOYEE_PHOTO");
 Id(x => x.Id, "EMPLOYEE_ID").GeneratedBy.Foreign("Employee");
 Map(x => x.Photo, "PHOTO").Not.Nullable();
 HasOne(x => x.Employee).Cascade.None().Constrained();
 }
}

这里需要注意的是EmployeePhoto的主键,不再是普通的生成方式,而是要选择通过Employee做外键生成。

关于NHibernate 的one to one标签上的constrained="true",该标签在外表上设置,千万不要在主表上设置。就是说明这个表的主键与另一个表的主键建立外键约束,也就是说在生成SQL脚本时,会为这个表创建外键,如果不加,是不会创建外键的。另外还有一个作用,就是在查询外表时,如果没有设置该属性,那么就会Join主表,而设置了该属性,就只需要查询外表。

在主键关联的情况下,如果从主表中移除从表的引用,这个时候保存主表,是不会删除从表的,也不会删除这个一对一的关系的。也就是说,我们不能单独保留Employee和Photo表,同时还要去掉两者之间的关系。

2.单向外键关联

比如我们做个中学的管理系统,设计了一个Class表保存班级,另一个Classroom表保存教室,班级和教室是一对一的关系,一个班级有且仅有一个教室,一个教室属于0到1个班级。

public class Class : Entity,IPermanent
{
 public virtual bool IsDeleted { get; set; }
 public virtual string ClassName { get; set; }
 public virtual int StudentCount { get; set; }
 public override string ToString()
 {
 return "Class Id:" + Id + " Name:" + ClassName;
 }
 public virtual Classroom Classroom { get; set; }
}
public class Classroom:Entity,IPermanent
{
 public virtual string Building { get; set; }
 public virtual string RoomNumber { get; set; }
 public override string ToString()
 {
 return "Classroom[" + Id + "] " + Building + " " + RoomNumber;
 }
 public virtual bool IsDeleted { get; set; }
 public virtual Class Class { get; set; }
}

Mapping的代码是:

public class ClassMap : ClassMap<Class>
{
 public ClassMap()
 {
 Table("CLASS");
 Id(x => x.Id, "CLASS_ID").GeneratedBy.HiLo("1000000000");
 Map(x => x.ClassName, "CLASS_NAME").Not.Nullable();
 Map(x => x.StudentCount, "STUDENT_COUNT").Not.Nullable();
 Map(x => x.IsDeleted, "IS_DELETED");
 References(x => x.Classroom, "CLASSROOM_ID").Cascade.All();
 ApplyFilter<IsDeletedFilter>("IS_DELETED = :DeleteFlag");
 }
}
public class ClassroomMap : ClassMap<Classroom>
{
 public ClassroomMap()
 {
 Table("CLASSROOM");
 Id(x => x.Id, "CLASSROOM_ID").GeneratedBy.HiLo("1000000000");
 Map(x => x.Building, "BUILDING");
 Map(x => x.RoomNumber, "ROOM_NUMBER");
 Map(x => x.IsDeleted, "IS_DELETED");
 HasOne(x => x.Class).PropertyRef(r => r.Classroom);
 ApplyFilter<IsDeletedFilter>("IS_DELETED = :DeleteFlag");
 }
}

这里两个表中只需要有一个表持有对方的主键作为外键即可,我们可以在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。这是非常不推荐的方式,一来导致数据维护重复,二来导致数据可能存在不一致。所以,这里我就不再累述这种方案的实现了。

示例代码下载:

http://files.cnblogs.com/studyzy/One2OneTest.7z

时间: 2024-08-31 10:04:48

One to One 的数据库模型设计与NHibernate配置的相关文章

ASP.NET MVC+EF框架+EasyUI实现权限管理系列(15)-用户登录详细错误和权限数据库模型设计

原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列(15)-用户登录详细错误和权限数据库模型设计     ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇)   (1):框架搭建    (2):数据库访问层的设计Demo    (3):面向接口编程   (4 ):业务逻辑层的封装    (5):前台Jquery easyUI实现    (6):EF上下文实例管理    (7):DBSession的封装   (8):DBSession线程内唯一     (9)

CodeIgniter针对数据库的连接、配置及使用方法_php实例

本文实例讲述了CodeIgniter针对数据库的连接.配置及使用方法.分享给大家供大家参考,具体如下: 1. 数据库: create database test; create table users( id int not null, name varchar(10), pwd varchar(10), email varchar(20) ) insert into users values(1,'shunping','shunping','aa@163.com'); insert into

急!!MFC创建数据库支持应用时配置数据源失败。

问题描述 急!!MFC创建数据库支持应用时配置数据源失败. 我在控制面板的管理工具中ODBC数据源配置好了,但是创建MFC程序的时候添加数据库支持 时,提示错误01000和80001,急死了,找不到类似的问题,求大神 连接失败: SQLState: '01000' SQL Server 错误: 14 [Microsoft][ODBC SQL Server Driver][Shared Memory]ConnectionOpen (Invalid Instance()). 连接失败: SQLSta

mysql-怎么用个人电脑做一个mySQL数据库服务器,需要配置什么?需要怎么配置?

问题描述 怎么用个人电脑做一个mySQL数据库服务器,需要配置什么?需要怎么配置? 怎么用个人电脑做一个mySQL数据库服务器,需要配置什么?需要怎么配置? 解决方案 就是下载安装包,然后安装配置.http://wenku.baidu.com/link?url=6N4Lv4mHbN_6dzJlfc2Zfi9Te2605coZq9r5SErdcN8t5D1aTfw5lOuznO_ijUQXjjgTbU_fU_gJLEyT5v0tkefFCBWIZrIA7CvOEY7xKim 解决方案二: 安装好,

Oracle11g数据库win8.1系统安装配置图文教程_oracle

1. Oracle11g安装 http://pan.baidu.com/s/1gfa3e63:这里是我在Oracle官网下载好了Windows系统64位的安装包,有2个zip文件.不想去官网找下载地址的童鞋可以直接用这个百度云盘的下载链接. 1.去www.oracle.com下载最新的oracle11g安装包的压缩文件,有2个压缩文件,都需要下载,下载完成以后需要解压缩在同一个目录下. 在开始oracle数据安装之前建议:1.关闭本机的病毒防火墙.2.断开互联网. 这样可以避免解压缩丢失文件和安

MySQL数据库服务器集群配置详解介绍

一.介绍 这篇文档旨在介绍如何安装配置基于2台服务器的MySQL数据库集群.并且实现任意一台服务器出现问题或宕机时MySQL依然能够继续运行. 注意! 虽然这是基于2台服务器的MySQL集群,但也必须有额外的第三台服务器作为管理节点,但这台服务器可以在集群启动完成后关闭.同时需要注意的是 并不推荐在集群启动完成后关闭作为管理节点的服务器.尽管理论上可以建立基于只有2台服务器的MySQL集群,但是这样的架构,一旦一台服务器宕机之后集 群就无法继续正常工作了,这样也就失去了集群的意义了.出于这个原因

mysql数据库客户端访问权限配置

mysql数据库客户端访问权限配置 借鉴这个百度经验. 用root登录mysql之后 1.use mysql 2.select host,user,password from user; 修改host列的用户,建议修改localhost用户,如图五所示 执行命令如下: update user set host = '%' where host = 'localhost'; select host,user,password from user; flush privileges; 再从客户端连接

【spring boot】12.spring boot对多种不同类型数据库,多数据源配置使用

2天时间,终于把spring boot下配置连接多种不同类型数据库,配置多数据源实现! ====================================================================================================== spring boot对多种不同类型数据库,多数据源配置使用 多数据源配置相关官方API:https://docs.spring.io/spring-boot/docs/current/api/ 环境如下:

SQL Server 2016的数据库范围内的配置详解

SQL Server 2016真的让人眼前一亮.几天前微软就提供了RCO(候选发布版)版本的下载.我已经围观了一圈RCO版本,其中一个最拽的功能是数据库范围内的配置(Database Scoped Configuration),在今天的文章里我想谈谈它.补充几句:装好之后,居然发现没有SSMS,崩溃中,原来是在向导中就有独立的安装程序,好吧! 这配色,真是低调有内涵. 另外,如过你的电脑已经安装了就[Microsoft Visual Studio 2010 Shell(独立)Redistribu