主键的选择 和 如何减少多外键

这个灵感来源于多日问题的集结:


还是从实例开始:
假定有以下模块:
新闻模块
艺术家模块
艺术实体模块(书法、绘画..)
评论模块

现在有这样一个需求,新闻模块中的新闻需要评论、艺术家模块下的艺术家需要评论、
各种艺术实体也需要评论,而评论又单独作为一个模块,他们之间的关系肯定是靠外键来维护,而且外键必定是在评论表上。
那么我们该怎么设定这个外键呢?
第一种做法:它用于不同表主键有重复。为评论表建立一个类型表,这些类型表明它是哪些表评论,再在主表即评论表中加一个外键,指向具体某个类型(某个表)的某条记录!也就是说需要多建立一个表,并且主表中需要两个外键!此种做法比第一种做法有了很大的改进!
第二种做法需要一个前提:这些需要评论的表,他们的主键要唯一(oracle可以让这些表共用一个序列来实现,其他数据库该如何实现?好像不太好实现吧!),这样的话,我们在评论中只需要加一个字段来标识为外键。

如果要使用第二种方式,我们就必须保证所有要与评论表关联的表的主键唯一性!


从根本上讲,外键分3种:一种像是新闻表与新闻类型表(国际、国内…)这样的表关系,临外一种就像评论与其他表这样的表关系,还有一种就像图书与图书章节这样的表关系。那么我们什么时候第一种的外键,什么时候用第二种的外键,什么时候使用地第三种的外键?是这样的,当我们添加新闻的时候,你需要选择该新闻所属的类型或者不选择!但当我们添加章节的时候你就必须选择它所属的书是哪一个!恩,,明白了,新闻与新闻类型之间的外键非常倾向于类型关系,而图书与图书章节之间的依赖关系非常的强,他们之间并没有类型关系之说!对于评论则介于这两者关系之间,你说评论分类型吗?一般不分,但有时候也分。如果有类型关系你就搞一个类型外键,如果不是类型关系,你就再弄一个字段作为外键,比如当某一个孩子在浏览某一个新闻之后发表了评论此时你就把该新闻的主键放到评论这个表的外键上不就可以了。


我仅用过hibernate,就拿hibernate中的主键生成策略来说事把!而常用的有

foreign(不考虑,它主要用于多表共用一个主键)

native:根据底层数据库的能力选择identity, sequence 或者hilo中的一个。这个很难保证 主键唯一,只有oracle中多表共用一个序列能实现,其他数据库就无能为力了,所以cut掉

assigned:由程序员指定,这个也比较可取,但是本人比较懒,故不经常用!

uuid:用一个128-bit的UUID算法生成字符串类型的标识符, 这在一个网络中是唯一的(使用了IP地址)。UUID被编码为一个32位16进制数字的字符串。它其实也是由应用程序来完成的,不过这个应用程序的书写不是由程序员来完成,而是由hibernate自己来完成!

通过上述的说明我们发现使用uuid是最完美的!!!!


/************************华丽的分割线********************/


说到这里,我们再谈谈“外键”。使用外键,是因为两张表中的某两条记录之间存在了“指向”关系。
而这个指向关系可以是表级别的指向还可以是代码级别的指向!什么意思?

举个例子:“学生甲记录”中有一个字段classid指向“班级A的主键”。

表级别的指向是说,这个外键关系在表中得到了体现:拿上面的例子就是说学生甲所在的表学生表有一个外键!此时如果这个外键可以为空,那么这个学生表中的所有学生记录的classid的值要么是null,要么是班级A所在表即班级表中所有记录的主键值之一,不可能是其他值。
代码级别的指向是说,这个外键关系并没有在表中得到体现,还拿上面的例子说事,学生甲所在的表学生表没有外键,此时这个学生表中的所有学生记录的classid的值可以是null,可以是班级A所在表即班级表中所有记录的主键值之一,也可以是其他任何classid类型的值!但是我们最终要达到的效果是“表级别”所达到的效果即:当这个外键可以为空时,学生表中的所有学生记录的classid的值要么是null,要么是班级A所在表即班级表中所有记录的主键值之一,不能是其他值。所以要靠程序员用程序来控制!说白了,代码级别的指向没有使用外键!

那么采用哪种方式比较好呢?表级别的指向,还是代码级别的指向?

我们说使用了“表级别的指向”即真正的使用了外键,可以保证“当这个外键可以为空时,学生表中的所有学生记录的classid的值要么是null,要么是班级A所在表即班级表中所有记录的主键值之一,不能是其他值。”
换句话就是说:
使用表级别的指向,当我们在插入一条学生记录的时候,数据库会检查classid的值,如果classid=null,看外键是否允许为空,如果要求不能为空,则插入失败;而使用代码级别的指向时,数据库不检查,直接插入;如果classid不为null,使用表级别的指向,数据库会检查classid的值是否是class表中所有记录主键之一,如果不是,则插入失败,而使用代码级别的指向时,数据库不检查,直接插入;
如果我们能够在代码级别控制好学生的classid,要么为null,要么只能是班级表所有记录的主键之一,岂不更好!这样看的话 代码级别的指向 仿佛好一些!!它能大幅度的降低数据库表表间的复杂关系!这个非常重要!

我们再回头看看hibernate:
如果要实现“表级别的指向”即真正使用外键,就必须在实体类中建立对象的引用,例如:Classes classes; Set students,并在映射文件中进行配置相应的关系!
如果要实现代码级别的控制,我们只需要一个“对象标识符”类型的属性即可,例如int classid即可完事,而且在一的一端类里(class)也不需要额外的属性!
但是,如果从延迟加载的角度来看,使用了“代码级别的控制”时就失去了延迟加载这个hibernate非常重要的亮点!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值