1. 程式人生 > >MyBatis多表聯查 引出 RDB表關係對映問題

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表的設計遵循了三正規化, 每張表對應一個實體類, 再根據表關係建立相應的實體或集合成員屬性;


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