2-19-怎么在数据库中表达面向对象中的继承关系
1. EAV表(entity-Attribute-Value)
2. 单表继承
3. 实体表继承
4. 类表继承
5. 半结构化设计
1. EAV表(entity-Attribute-Value)
createtable note_dictionary
(
dictionary_idnumber,
dictionary_type varchar2(50),--记录本的类型,就是那20多种记录本
dictionary_name varchar2(200)--记录本中的名称
);
--存储记录本的内容
createtable note_content
(
content_idnumber,
note_id number,--具体的一个记录本的id
dictionary_idnumber,--记录本中字段的id
contentvarchar2(1000)--字段的内容
);
优势
1. 灵活,增删字段不需要修改库表结构。不管entity是否存在继承关系,都可以使用EAV方式存储。
2. 库表设计简单(id、attr_name、value)
缺点:
1. 属性值的类型无法约束。如果是Date和number,只能用varchar2表示。统计也可能出现问题
2.不能加not null的约束和默认值。
3.后期维护数据较麻烦。
4.如果记录本之间是有关系,要建立关联关系,需要再加表,实现很复杂。
5.在一段时间后内容表会变得非常之笨重,系统中就属它数据量大。
6.牵一发而动全身。一处修改,无法确定其他地方是否会受影响。Java代码中使用了反射,debug很难调试,问题难定位
7.复杂查询都会变得复杂。
8.牺牲关系数据库本身的好处
9.无法使用完整性检查特性(外键)
2. 单表继承
将entity继承体系的所有属性都存储到一个表中,
实体属性如下:
库表设计如下:
user表:
优势
库表设计简单
不需要链表查询即可获取到子类的完整信息
规避了EAV设计的很多缺陷
劣势
添加子类的属性时需要修改user表(锁表,数据量大时影响很大)
对于子类,表中出现无关的属性,比如教师的行出现了student_number, 学生的行出现了subject
使用场景:
继承体系中子类属性较少的情况。比如可预见的时间内子类的属性都比较少时可以使用这种方式,毕竟查询简单,不需要联表查询。
3. 实体表继承
实体的属性是完整的,实体表继承,就是说每个实体都对应一个完整的表。
这种设计的优缺点:
优势
1. 获取完整对象不需要联表查询
2. 表中没有无关属性(跟单表继承的对比)
劣势
1. 在基类添加属性时需要修改多个表(比如在User类添加birthday属性,则需要在teacher/student表都添加column)
2. 表的结构松散,看不出类的继承关系
4.类表继承
那么在库表设计时也有三张表:user表、teacher表、student表(teacher表和student表有外键),如下:
优势
库表的层次结构清晰,库表直接反应了继承关系
为子类添加属性时不需要修改基类表(user表),为基类添加属性时不需要同时为多个表添加column
查询teacher、student的基类信息时不需要查询多个表(对比实体表继承方案)
劣势
获取对象完整数据需要联表查询(在表数据量大时联表查询性能差)
5. 半结构化设计
other_properties字段,对于teacher对象来说存{subject=english}, 对于student对象存:{student_number=123}
那么在查询时将整条记录查询到内存后再转为完整对象。
优势
可扩展性强,添加子类属性时不需要添加column,添加基类属性时才会添加column
查询简单,每行对应一个对象的完整信息,不需要联表查询
劣势
非结构化部分依旧有EAV设计的问题
对象的属性名没有约束(mysql没法限定用户名就叫username,业务可能不小心存为userName)
属性值的类型无法约束(age没法限定是int类型,业务可能不小心存了个‘abc’进去)
获取单个对象数据繁琐(查询一个完整对象,需要获取多条记录,还不能直接映射为User对象)
无法使用完整性检查特性(没法使用外键)
对于非结构化部分无法使用DB的聚合函数(比如sum、count等)