需求
在業務中, entity實體之間難免出現繼承關係,映射到庫表時如何設計?
下面將介紹這幾種設計方案並對比優缺點:
- EAV表(entity-Attribute-Value)
- 單表繼承
- 實體表繼承
- 類表繼承
- 半結構化設計
(技術選型時需要結合具體業務分析再決定方案)
設計方案
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');
需要查多條記錄到內存再慢慢組裝。
這種存取方式的優缺點:
優勢
- 靈活,增刪字段不需要修改庫表結構。不管entity是否存在繼承關係,都可以使用EAV方式存儲。
- 庫表設計簡單(id、attr_name、value)
劣勢
- 對象的屬性名沒有約束(mysql沒法限定用戶名就叫username,業務可能不小心存爲userName)
- 屬性值的類型無法約束(age沒法限定是int類型,業務可能不小心存了個‘abc’進去)
- 獲取單個對象數據繁瑣(查詢一個完整對象,需要獲取多條記錄,還不能直接映射爲User對象)
- 無法使用完整性檢查特性(沒法使用外鍵)
如果業務在新增屬性時不需要修改庫表結構,可能數據庫採用的就是EAV設計。
(一般業務都建議使用結構化庫表設計,畢竟EAV設計的缺點比較多,但如果業務的屬性頻繁變化,比如埋點事件上報等,可以考慮這種方案。)
單表繼承
簡介
將entity繼承體系的所有屬性都存儲到一個表中,
實體屬性如下:
庫表設計如下:
user表:
id | username | password | age | subject | student_number |
---|---|---|---|---|---|
1 | teacher1 | xxx | 40 | 數學 | |
2 | student1 | xxx | 10 | 12341234 |
優勢
- 庫表設計簡單
- 不需要鏈表查詢即可獲取到子類的完整信息
- 規避了EAV設計的很多缺陷
劣勢
- 添加子類的屬性時需要修改user表(鎖表,數據量大時影響很大)
- 對於子類,表中出現無關的屬性,比如教師的行出現了student_number, 學生的行出現了subject
使用場景:
繼承體系中子類屬性較少的情況。比如可預見的時間內子類的屬性都比較少時可以使用這種方式,畢竟查詢簡單,不需要聯表查詢。
實體表繼承
簡介
實體的屬性是完整的,實體表繼承,就是說每個實體都對應一個完整的表。
比如用戶繼承體系中:
那麼庫表中,teacher表擁有完整的屬性。student表也擁有完整屬性,如圖:
這種設計的優缺點:
優勢
- 獲取完整對象不需要聯表查詢
- 表中沒有無關屬性(跟單表繼承的對比)
劣勢
- 在基類添加屬性時需要修改多個表(比如在User類添加birthday屬性,則需要在teacher/student表都添加column)
- 表的結構鬆散,看不出類的繼承關係
類表繼承
簡介
類的體系結構如下, 那麼類表繼承的表結構也如下,就是將類的繼承結構映射到庫表上。
那麼在庫表設計時也有三張表:user表、teacher表、student表(teacher表和student表有外鍵),如下:
優勢
- 庫表的層次結構清晰,庫表直接反應了繼承關係
- 爲子類添加屬性時不需要修改基類表(user表),爲基類添加屬性時不需要同時爲多個表添加column
- 查詢teacher、student的基類信息時不需要查詢多個表(對比實體表繼承方案)
劣勢
4. 獲取對象完整數據需要聯表查詢(在表數據量大時聯表查詢性能差)
半結構化設計
簡介
使用一張表存儲整個繼承體系,但是基類的每個屬性映射到表的對應column,子類的所有屬性則使用一個json/xml類型的column來存儲。舉例:
對於下面的類:
設計庫表如下:
other_properties字段,對於teacher對象來說存{subject=english}, 對於student對象存:{student_number=123}.
那麼在查詢時將整條記錄查詢到內存後再轉爲完整對象。
優勢
- 可擴展性強,添加子類屬性時不需要添加column,添加基類屬性時纔會添加column
- 查詢簡單,每行對應一個對象的完整信息,不需要聯表查詢
劣勢
- 非結構化部分依舊有EAV設計的問題
- 對象的屬性名沒有約束(mysql沒法限定用戶名就叫username,業務可能不小心存爲userName)
- 屬性值的類型無法約束(age沒法限定是int類型,業務可能不小心存了個‘abc’進去)
- 獲取單個對象數據繁瑣(查詢一個完整對象,需要獲取多條記錄,還不能直接映射爲User對象)
- 無法使用完整性檢查特性(沒法使用外鍵)
- 對於非結構化部分無法使用DB的聚合函數(比如sum、count等)
總結
歡迎點評和點贊…