谈Apache OFbiz 会员模块表结构设计
數據庫表的結構設計可謂是ofbiz除技術框架之外,另一個非常值得學習的方向。這篇文章我們來談談ofbiz對電子商務會員表的設計。
PARTY
ofbiz對人、團體進行了抽象,稱之為party,翻譯為中文稱之為“會員”(但我覺得拋開領域,如果你也有相關的設計需求,在其他領域可能稱之為團體更合適)。會員在ofbiz被設計為一個抽象的概念(對應到面向對象設計中,你可以稱其為一個基類),它有兩個具體的延伸(繼承者):分別是PERSON以及PARTY_GROUP。數據庫的E-R圖:
這里PERSON,PARTY_GROUP分別表示“個人會員”、“組織會員”。Party只是一種抽象,它定義了可以被抽象為“會員”的對象所具有的基本特征。但 “個人”以及“組織”會員卻具備比 “基本會員”更多的特征,所以此處從Party延伸出兩張表來存儲這些額外的特征信息它們的主鍵都是PARTY表的PARTY_ID。
PARTY_TYPE
partyType定義了party的類型約束。E-R圖如下:
可以看到,PARTY_TYPE是擁有層級關系的(它的一個屬性PARENT_TYPE_ID自關聯了PARTY_TYPE的主鍵:PARTY_TYPE_ID,下面如果看到E-R圖上有自關聯到本身的,都表示這種關系,不再敖述)。
ofbiz提供的初始數據中有如下幾種party type:
構建成層級關系如下圖所示:
上面展示的兩張表:PERSON、PARTY_GROUP也是其中的兩個partyType,并且這些partyType都可以獨立擴展的,PERSON、PARTY_GROUP也是僅有的兩個擴展。這也是上面表結構中這兩個記錄的HAS_TABLE值為Y的原因。
PARTY_ROLE
就跟社會的“角色分工”一樣,一個會員在系統中也必定會擁有屬于自己的角色。而PARTY_ROLE表就是用于關聯會員與角色類型的關系表,很明顯會員與角色類型是多對多的關系(這里需要提及的是:ofbiz中只有角色類型,沒有角色,或者更準確點說,角色類型包含了角色)。
PARTY_RELATIONSHIP
上面我們看到的會員是一類“抽象”的實體。不管它表示的是個人,還是組織,它總是會跟其他會員發生關系,就好像一個人不可能脫離社會而孤立得存在著,他必然有自己的社會角色,并跟社會的其他“團體”產生聯系。這在ofbiz中被抽象為“partyRelationship”。我們來看它是如果表達“關系”這個語義的:
當你把這些所有的字段連起來,它幾乎能涵蓋所有的“會員關系”(要知道,有時會員關系會非常復雜,一個會員有時會存在于多個系統中)。
我們再回過頭來,看PARTY_RELATIONSHIP的表結構設計:
可以看到前五個鍵形成了聯合主鍵,其中前四個都是形如XXX_FROM,XXX_TO的ID標識。表示從“FROM”方往“TO”方建立關系。其中PARTY_ID_FROM與ROLE_TYPE_ID_FROM是“源”方;PARTY_ID_TO與ROLE_TYPE_ID_TO是“目標”方。
從上面圖中也可以看到,每個關系都帶有兩個DATETIME字段,分別表示:開始日期,截止日期。這說明關系是有“時段”這個屬性的。當然,如果沒有截止日期,可以看做是“永久”的。因此為了防止關系過了生效時段無法再次建立關系(因為主鍵不允許重復),所以選擇了聯合“FROM_DATE”作為聯合主鍵(后面如果再次建立相同的關系時,只要FROM_DATE不一樣,就視為一條新記錄)。
這里有必要說明一下,在ofbiz的數據庫設計中,大量采用了“時段”這個屬性來標識記錄的有效性。這樣的設計與邏輯刪除相比的好處是:它除了減少了刪除時因為外鍵約束等連帶關系導致的錯誤,還可以直接充當“歷史記錄”的作用,省去了對歷史表的維護,當然它的缺點就是:表中的記錄會比其他的設計多得多。
當然,From跟To只是為了標識兩者建立了關系,卻并未說明它們到底存在怎樣的關系,就好像——我跟你是朋友。這句話可以拆分為三部分:FROM方:我,TO方:你,關系是:朋友。上表中用一個字段表示了關系:PARTY_RELATIONSHIP_TYPE_ID(這只是一個外鍵,關聯著表PARTY_RELATIONSHIP_TYPE)。
在界面上新建一個關系(此處是從外部到自己的一個關系):
PARTY_RELATIONSHIP_TYPE
該表約束了關系的類型。比如:雇傭者、朋友、父、子、管理者,E-R圖:
從圖中可以看出,會員關系類型也擁有層次關系。表中還有兩個特別的字段:
- ROLE_TYPE_ID_VALID_FROM
- ROLE_TYPE_ID_VALID_TO
對每個關系類型,都可以擴展以獨立實現關系(被擴展后關系類型記錄的字段HAS_TABLE被標識為Y,否則默認為N),在ofbiz的初始化數據中,唯一被擴展的關系類型是:EMPLOYMENT。我們來看看EMPLOYMENT關系表的實現:
可以看到,它跟之前的PARTY_RELATIONSHIP的主鍵實現方式一樣。因此可以把它看做是:PARTY_RELATIONSHIP_TYPE_ID為EMPLOYMENT的PARTY_RELATIONSHIP的特殊實現。
在界面上建立一個關系類型:
PARTY_CLASSIFICATION_TYPE
為了便于管理,ofbiz對會員按各種維度進行分類,常見的分類的類型有:年收入、價值等級、產業、雇員數量等;PARTY_CLASSIFICATION_GROUP
會員并不會直接跟分類的類型產生關系,而是跟一個或多個分類組產生關聯關系。而分類組受分類類型約束。新建一個分類組:
PARTY_CLASSIFICATION
會員的分類相關表的關系圖:從表的關聯關系可以看出,會員分類跟分類組是多對多的關系,并且分類具有時效性。因此聯合FROM_DATE作外鍵。
將會員劃歸入一個會員分類:
CONTENT_MECH
從這張表開始,我們來看會員的聯系方式相關的表結構設計,這也是一部分非常棒的設計。這張表存儲了聯系方式基本信息。它引用了另一張表:CONTENT_MECH_TYPE作為外鍵,來表示該聯系方式的類型(通常的聯系方式類型有電話、郵箱、網址等)。
CONTENT_MECH_TYPE
可以看到聯系方式類型,也是具有層級結構(父子關系)的。
當我們想新建一個聯系方式時,首先必須先指定想創建的聯系方式的類型:
PARTY_CONTACT_MECH
毫無疑問,地址信息只有跟會員聯系起來,才能表示會員的地址。而會員跟地址是多對多的關系,理解這個關系時需要注意的是會員可以是任何團體、組織或者個人。那這里可能就會存在兩個不同的會員擁有同一個聯系方式的可能,比如:一個員工會員與一個該員工所屬的公司會員,它們可以都存在同一個聯系方式:公司的通訊地址。當然一個會員擁有多個聯系方式,這是很容易理解的。所以會員標識跟聯系方式標識之間是多對多的關系,并且跟前面的設計模式相似——聯系方式也有時效性,比如換電話號碼,換工作導致聯系方式變化等,所以聯合FROM_DATE作為聯合主鍵:當我們選擇聯系方式類型為電話號碼時,會出現如下的表單填寫:
如果你新建一個聯系方式的類型為電話號碼,那么電話號碼存儲在何處?此處又跟前面談到的HAS_TABLE字段有關(CONTACT_MECH_TYPE中也存在這個字段)。正常情況下,聯系方式關聯著聯系方式類型,普通的聯系方式的具體信息存儲在CONTACT_MECH的INFO_STRING屬性中。但有些聯系信息不是單純的像郵箱這樣只是一個字符串,比如像電話號碼、郵政編碼…它們都有具體的格式表示。所以這些特例用INFO_STRING這一個屬性存儲也不方便,因此可以獨立擴展該CONTACT_MECH_TYPE(將其HAS_TABLE字段設置為Y,這樣查詢該CONTACT_MECH信息的時候,就不采用INFO_STRING字段,而是采用擴展表中格式化的聯系方式)。
CONTACT_MECH_PURPOSE_TYPE
當我們點擊上面界面的保存按鈕之后,會更進一步得擴充聯系信息:在ofbiz中還存在一個稱之為“聯系目的”的東西,它是什么意思?
看到選項我們就會明白,說白了一個人的地址簿或者電話簿中的聯系方式可能有很多。它們沒有主次之分,只有目的不同。
PARTY_CONTACT_MECH_PURPOSE
上面談到了聯系目的,那么很自然它需要跟會員具體的某條聯系信息關聯起來才能稱之為:某個會員為了某種聯系目的存儲了一個“聯系方式”記錄。這里需要注意的是,它并沒有跟PARTY_CONTACT_MECH產生直接關聯(沒有外鍵關系),而是把PARTY_CONTACT_MECH的三個主鍵照搬過來,聯合CONTACT_MECH_PURPOSE_TYPE_ID形成四個組合主鍵,這是因為PARTY_CONTACT_MECH的聯合主鍵機制無法被其他表當做外鍵引用。因此,可以將PARTY_CONTACT_MECH_PURPOSE看作聯系信息模塊的聚合。這個怎么來理解?其實一個地址可以看成:某個會員(PARTY_ID),出于某種目的(CONTACT_MECH_PURPOSE_TYPE_ID),在某段時間內(FROM_DATE),保存了某個聯系方式(CONTACT_MECH_ID)。這種聯系方式的設計非常有彈性,因此在大部分情況下,這種抽象性能夠涵蓋大部分應用場景。
CONTENT_TYPE
會員內容的設計跟聯系方式類似。會員可以有一個類似文件空間在服務器上,可以供其保存文檔、圖片之類的東西。CONTENT_TYPE限定了會員可以存儲的內容類型:CONTENT
該表是它的具體存儲內容的地方,當然并不是唯一的,如果CONTENT_TYPE有一條記錄的HAS_TABLE值為Y,則那個記錄對應的表也用于存儲內容。內容表里的字段非常多,就不截圖了。跟之前的聯系信息類似,會員可以有多個內容,一個內容也可能從屬于多個會員。因為會員是個抽象的概念,對應到實體上可能會有重合,所以需要一個“目的”來修飾會員內容,它就是——PARTY_CONTENT_TYPE。
PARTY_CONTENT_TYPE
用于修飾會員內容的用途,當然這里它的表名叫type,事實上從數據記錄來看,來時充當了目的的作用。內容還跟其他一些表有關聯(主要是被引用關系,比如:CONTENT_ROLE等),此處因為跟本文主題沒太大關系,所以不再敖述。
總結
更高的抽象級別
ofbiz party模塊的設計,正如它所應用的場景:非常適用于電子商務系統會員信息相關的設計。當然ofbiz中其他相關的多個系統也同樣應用了這些表結構,這也意味著它有適用于一般行業、系統的通用性,這得益于這種設計的抽象級別比較高。它可以描述任何的組織、個體、他們的地址信息、他們之間的關系。特別是對會員“relationship”表的設計非常類似于《分析模式》中談到的責任模式:因此,如果你面臨組織結構比較復雜的業務場景時,比如群組、聯系人、個人、公司都可以成為系統的用戶,又或者一個非常大的跨國公司,擁有:總部、區域銷售辦公室、辦事處、分公司等各種組織形式時,這種設計就會派上用場。
數據庫表的繼承關系
從PARTY、PARTY_TYPE、PARTY_GROUP、PERSON這幾張表我們可以學習到數據庫表的“繼承”設計。時效性設計
不是真刪除、也不是邏輯刪除、而是失效(FROM_DATE, THUR_DATE)。這種方式可以代替“操作-操作歷史”的多表設計,轉而合并為獨立的一張表。原文發布時間為:2014-12-07
本文作者:vinoYang
本文來自云棲社區合作伙伴CSDN博客,了解相關信息可以關注CSDN博客。
總結
以上是生活随笔為你收集整理的谈Apache OFbiz 会员模块表结构设计的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Junit中测试插入等无返回值的操作要注
- 下一篇: scrollLeft,scrollWid