MyBatis多表聯查 引出 RDB表關係映射問題

一. 關係型數據庫表關係回顧:

1. 一對一: 丈夫表和妻子表是典型的一對一關係;

    RDB中的實現方式: 分別創建丈夫表和妻子表, 將對方表主鍵設爲外鍵, 因爲主鍵的唯一性, 保證了一對一關係;

2. 一對多: 公司和員工一般是一對多關係; (注意: 其實一對多表反過來看是一對一關係, 即一個員工只屬於一個公司)

    RDB中的實現方式: 分別創建公司.表和員工表, 將公司表主鍵設置爲員工表外鍵;

3. 多對多: 訂單和產品是典型的多對多關係;

 

    RDB中實現方式: 因爲RDB中表關係只能靠外鍵實現, 實際單一外鍵只能實現一對多關係, 故需要創建中間表, 以外鍵的 "乘積" 來

實現多對多的關係, 如下圖:

其中中間表取兩表主鍵爲聯合主鍵, 並分別作爲關聯兩表的外鍵; 

 

二. 原生MyBatis中的查表和結果封裝:

1. 查詢: 查詢時, 查詢參數必須封裝爲單個參數, 可以是基本數據類型也可以是POJO/ 數組/集合等 (不包括Map);

2. 返回值: 查詢的單條結果只能封裝到單個對象中, 當多條數據時自動作風扎UN個到List中, 故無論需要什麼數據, 查詢後單條結果都要進行封裝;

 

三. MyBatis不同表關係的查詢方法:

    在Java中, 我們將每張表的字段作爲 POJO 的成員屬性, 創建不同的POJO, 此時可以體現:

 

1. 一對一關係查詢: 查詢某男人及其妻子的全部信息:

POJO: Husband類 Wife類

需求引出問題: 查詢雙方所有信息, 需要進行二表聯查, 查詢結果無法直接封裝在Hunband或者Wife對象中;

問題解決: 

    方式1: 新建類Hunband_Wife, 封裝二表聯查後的所有屬性, 此類並不具有實際意義, 僅爲封裝數據而存在, 違背OO思想, 不推薦;

    方式2: 實際妻子表的主鍵id值在丈夫表中僅是一個映射 ( 理解爲操作系統桌面快捷方式, 實際文件並未放到桌面, 當我們找到桌面對應文件夾時實際其中並不直接包含相應文件) , 我們需要將真實的Wife對象封裝到Hunband對象中, 故在Hunband類中新增Wife實體作爲成員屬性 (同樣,Wife類中新增Hunband實體作爲成員屬性), 如下圖:  

查詢結果的多層封裝需要用到MyBatis映射文件中的 resultMap 標籤中的 association 子標籤, 該標籤可以將多表查詢的一部分封裝成實體成員屬性對象 (association子標籤可以有多個,即類中可以定義多個實體成員屬性 ), 用法如下:

	<resultMap id="mapOfFindAll" type="com.wen.domain.Hunband">
		<id property="hid" column="hid"></id>
		<result property="hname" column="hname"></result>
		<result property="wid" column="wid"></result>
		<association property="wife" javaType="com.wen.domain.Wife">
			<id property="wid" column="wid"></id>
			<result property="wname" column="wname"></result>
			<result property="hid" column="hid"></result>
		</association>
	</resultMap>

 

2. 一對多關係查詢: 查詢某企業及其所有員工的信息

POJO: Company類, Staff類

需求引出問題: 查詢該公司的所有員工,屬於一對多關係,無法直接封裝在實體類中, 也無法直接定義實體成員屬性;

問題解決: 此時應該定義一個集合作爲Company成員屬性, 集合泛型爲Staff, 即實現一個公司對應多個員工的目的:

但是,數據庫二表聯查結果表是二維表結構, 數據條數等於員工數, 需要對公司信息進行去重, 並將所有員工信息封裝後放入List, 此時用到MyBatis映射文件中的 resultMap 標籤中的 collection 子標籤, 用法如下:

	<resultMap id="mapOfFindAll" type="com.wen.domain.Company">
		<id property="cid" column="cid"></id>
		<result property="cname" column="cname"></result>
		<result property="caddress" column="caddress"></result>
		<collection property="staffList" ofType="list" javaType="com.wen.domain.Staff">
			<id property="sid" column="sid"></id>
			<result property="sname" column="sname"></result>
		</collection>
	</resultMap>

 

3. 多對多關係查詢: 分爲兩類:

一類是中間表無聯合主鍵之外的字段信息(如老師和學生), 一類是中間表還有聯合主鍵之外的字段信息 (如: 用戶和遊戲角色,此時中間表會存在一個角色等級的信息    又如: 訂單和產品,此時中間表會存在訂單中某一產品的數量信息);

 

A. 中間表無其他字段 (老師和班級):

    情況分析: 該種場景下, 一般不需要以中間表作爲數據源查詢關聯信息, 故使用情景可直接分爲兩個一對多查詢, 只不過將sql語句從二表聯查增加到了三表聯查;

    此時實體屬性不通過中間表對象進行關聯, 如下:

    表關係:

    實體定義:

    查詢某個老師的所教授班級時: MyBatis的resultMap定義:

	<resultMap id="mapOfFindClass" type="com.wen.domain.Teacher">
		<id property="tid" column="tid"></id>
		<result property="tname" column="tname"></result>
		<collection property="classList" ofType="list" javaType="com.wen.domain.Class">
			<id property="cid" column="cid"></id>
			<result property="cname" column="cname"></result>
		</collection>
	</resultMap>

但是,該種方法限制了中間表字段的可擴展性, 比如: 現在學校舉辦活動, 將每個老師帶的每個班效果做評比打分嗎此時便要增加中間表字段: 分數;

 

B. 中間表有其它字段 (玩家和遊戲角色):

    情況分析: 該種情況下, 可能需要以中間表作爲基礎進行查詢, 比如遊戲角色等級排行榜的實現;

    此時玩家和遊戲角色的類通過中間表對象進行關聯, 如下:

    表關係:

    定義實體:

    查詢某一等級的所有用戶所持遊戲角色信息: MyBatis的resultMap定義:

	<resultMap id="mapOfAllP_R" type="com.wen.domain.playRole">
		<result property="level" column="level"></result>
		<association property="role" javaType="com.wen.domain.Role">
			<id property="rid" column="rid"></id>
			<result property="rname" column="rname"></result>
		</association>
		<association property="player" javaType="com.wen.domain.Player">
			<id property="pid" column="pid"></id>
			<result property="pname" column="pname"></result>
		</association>
	</resultMap>

同理: 因爲兩張表和中間表對應POJO都定義了相應實體集合和實體成員屬性, 故: 通過resultMap聯合多表查詢語句可以查詢任何相關信息;


RDB表關係映射問題:

1. 基於有外鍵的表到相應關聯表的查詢實際是一對一關係, 所以爲了體現RDB中的外鍵, 需要在基本表的類中定義外鍵所在表對應的實體成員屬性;

2.基於被外鍵關聯表的查詢, 實際是一對多關係, 即一個主鍵可以被多次引用爲外鍵, 故需在基本表的類中定義集合, 泛型爲外鍵所在的表對應實體類;

3.基於多表中間表的查詢, 是實際意義上的多對多查詢, 此時在中間表的類中定義相應兩個關聯表的實體成員屬性即可;

 

所有支持面向對象的語言中, 包括以面向對象思維的數據傳遞語言(如JSON), 對象關係的維繫靠的是樹杈結構, 每一個實體成員屬性(或者集合)都是一個節點, 如下圖:

但是在RDB中, 對象關係(表關係)是以 "二維表加外鍵連接" 的方式實現的:

故: 通過上述分析和論述, 想實現以上兩種數據模型的完整關係映射, 達到只要有關係就可以封裝進一個對象的目的, 遵循以下原則即可:

1. 當對象id被作爲某表外鍵時, 需要定義一個某表對應實體爲泛型的集合作爲成員屬性;

2. 當引用了某表的id作爲外鍵時, 需要定義一個某表對應類作爲實體成員屬性;

3. 第2條優先於第1條, 即某字段同時爲主鍵和外鍵時, 僅定義實體成員屬性; 

前提: RDB表的設計遵循了三範式, 每張表對應一個實體類, 再根據表關係創建相應的實體或集合成員屬性;


轉載請標明出處: 划船一哥

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章