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

对象继承关系的各种库表映射方案对比

  • 需求
  • 设计方案
    • EAV表设计
      • 简介
    • 单表继承
      • 简介
    • 实体表继承
      • 简介
    • 类表继承
      • 简介
    • 半结构化设计
      • 简介
    • 总结

需求

在业务中, entity实体之间难免出现继承关系,映射到库表时如何设计?
对象继承关系的各种库表映射对比--《sql反模式》_第1张图片
下面将介绍这几种设计方案并对比优缺点:

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

设计方案

EAV表设计

简介

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

id attr_name attr_value
1 username peter
1 gender
1 birthday 2000-01-01
1 age 19

比如想获取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继承体系的所有属性都存储到一个表中,
实体属性如下:
对象继承关系的各种库表映射对比--《sql反模式》_第2张图片
库表设计如下:
user表:

id username password age subject student_number
1 teacher1 xxx 40 数学
2 student1 xxx 10 12341234

优势

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

劣势

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

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

实体表继承

简介

实体的属性是完整的,实体表继承,就是说每个实体都对应一个完整的表。
比如用户继承体系中:
对象继承关系的各种库表映射对比--《sql反模式》_第3张图片
那么库表中,teacher表拥有完整的属性。student表也拥有完整属性,如图:
对象继承关系的各种库表映射对比--《sql反模式》_第4张图片
这种设计的优缺点:
优势

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

劣势

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

类表继承

简介

类的体系结构如下, 那么类表继承的表结构也如下,就是将类的继承结构映射到库表上。
对象继承关系的各种库表映射对比--《sql反模式》_第5张图片
那么在库表设计时也有三张表:user表、teacher表、student表(teacher表和student表有外键),如下:
对象继承关系的各种库表映射对比--《sql反模式》_第6张图片
优势

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

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

半结构化设计

简介

使用一张表存储整个继承体系,但是基类的每个属性映射到表的对应column,子类的所有属性则使用一个json/xml类型的column来存储。举例:
对于下面的类:
对象继承关系的各种库表映射对比--《sql反模式》_第7张图片
设计库表如下:
对象继承关系的各种库表映射对比--《sql反模式》_第8张图片
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等)

总结


欢迎点评和点赞…

你可能感兴趣的:(mysql)