对象继承关系的各种库表映射对比--《sql反模式》

需求

在业务中, entity实体之间难免出现继承关系,映射到库表时如何设计?
继承体系
下面将介绍这几种设计方案并对比优缺点:

  1. EAV表(entity-Attribute-Value)
  2. 单表继承
  3. 实体表继承
  4. 类表继承
  5. 半结构化设计
    (技术选型时需要结合具体业务分析再决定方案)

设计方案

EAV表设计

简介

EAV就是entity-Attribute-Value的意思(实体-属性-值),非结构化存储, 看图表:
user表:

idattr_nameattr_value
1usernamepeter
1gender
1birthday2000-01-01
1age19

比如想获取peter的用户信息:

select 
	*
 from 
 	user 
 where 
 	id in (select id from user where attr_name = 'peter');

需要查多条记录到内存再慢慢组装。

这种存取方式的优缺点:

优势

  1. 灵活,增删字段不需要修改库表结构。不管entity是否存在继承关系,都可以使用EAV方式存储。
  2. 库表设计简单(id、attr_name、value)

劣势

  1. 对象的属性名没有约束(mysql没法限定用户名就叫username,业务可能不小心存为userName)
  2. 属性值的类型无法约束(age没法限定是int类型,业务可能不小心存了个‘abc’进去)
  3. 获取单个对象数据繁琐(查询一个完整对象,需要获取多条记录,还不能直接映射为User对象)
  4. 无法使用完整性检查特性(没法使用外键)

如果业务在新增属性时不需要修改库表结构,可能数据库采用的就是EAV设计。
(一般业务都建议使用结构化库表设计,毕竟EAV设计的缺点比较多,但如果业务的属性频繁变化,比如埋点事件上报等,可以考虑这种方案。)

单表继承

简介

将entity继承体系的所有属性都存储到一个表中,
实体属性如下:
在这里插入图片描述
库表设计如下:
user表:

idusernamepasswordagesubjectstudent_number
1teacher1xxx40数学
2student1xxx1012341234

优势

  1. 库表设计简单
  2. 不需要链表查询即可获取到子类的完整信息
  3. 规避了EAV设计的很多缺陷

劣势

  1. 添加子类的属性时需要修改user表(锁表,数据量大时影响很大)
  2. 对于子类,表中出现无关的属性,比如教师的行出现了student_number, 学生的行出现了subject

使用场景
继承体系中子类属性较少的情况。比如可预见的时间内子类的属性都比较少时可以使用这种方式,毕竟查询简单,不需要联表查询。

实体表继承

简介

实体的属性是完整的,实体表继承,就是说每个实体都对应一个完整的表。
比如用户继承体系中:
在这里插入图片描述
那么库表中,teacher表拥有完整的属性。student表也拥有完整属性,如图:
库表设计
这种设计的优缺点:
优势

  1. 获取完整对象不需要联表查询
  2. 表中没有无关属性(跟单表继承的对比)

劣势

  1. 在基类添加属性时需要修改多个表(比如在User类添加birthday属性,则需要在teacher/student表都添加column)
  2. 表的结构松散,看不出类的继承关系

类表继承

简介

类的体系结构如下, 那么类表继承的表结构也如下,就是将类的继承结构映射到库表上。
类继承体系
那么在库表设计时也有三张表:user表、teacher表、student表(teacher表和student表有外键),如下:
库表继承体系
优势

  1. 库表的层次结构清晰,库表直接反应了继承关系
  2. 为子类添加属性时不需要修改基类表(user表),为基类添加属性时不需要同时为多个表添加column
  3. 查询teacher、student的基类信息时不需要查询多个表(对比实体表继承方案)

劣势
4. 获取对象完整数据需要联表查询(在表数据量大时联表查询性能差)

半结构化设计

简介

使用一张表存储整个继承体系,但是基类的每个属性映射到表的对应column,子类的所有属性则使用一个json/xml类型的column来存储。举例:
对于下面的类:
在这里插入图片描述
设计库表如下:
在这里插入图片描述
other_properties字段,对于teacher对象来说存{subject=english}, 对于student对象存:{student_number=123}.
那么在查询时将整条记录查询到内存后再转为完整对象。
优势

  1. 可扩展性强,添加子类属性时不需要添加column,添加基类属性时才会添加column
  2. 查询简单,每行对应一个对象的完整信息,不需要联表查询

劣势

  1. 非结构化部分依旧有EAV设计的问题
    1. 对象的属性名没有约束(mysql没法限定用户名就叫username,业务可能不小心存为userName)
    2. 属性值的类型无法约束(age没法限定是int类型,业务可能不小心存了个‘abc’进去)
    3. 获取单个对象数据繁琐(查询一个完整对象,需要获取多条记录,还不能直接映射为User对象)
    4. 无法使用完整性检查特性(没法使用外键)
  2. 对于非结构化部分无法使用DB的聚合函数(比如sum、count等)

总结

在这里插入图片描述
欢迎点评和点赞…

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值