“GitHub:https://github.com/nateshao/ssm/tree/master/115-mybatis-associated-one-many
1. 關聯關系概述
為什么學習MyBatis關聯關系?
“實際的開發中,對數據庫的操作常常會涉及到多張表,這在面向對象中就涉及到了對象與對象之間的關聯關系。針對多表之間的操作,MyBatis提供了關聯映射,通過關聯映射就可以很好的處理對象與對象之間的關聯關系。所以,,這里將對MyBatis的關聯關系映射進行詳細的講解。
在關系型數據庫中,多表之間存在著三種關聯關系,分別為一對一、一對多和多對多,如下圖所示:
一對一:在任意一方引入對方主鍵作為外鍵;
一對多:在“多”的一方,添加“一”的一方的主鍵作為外鍵;
多對多:產生中間關系表,引入兩張表的主鍵作為外鍵,兩個主鍵成為聯合主鍵或使用新的字段作為主鍵。
在Java中,通過對象也可以進行關聯關系描述,如圖下圖所示:
2. 一對一
在現實生活中,一對一關聯關系是十分常見的。例如,一個人只能有一個身份證,同時一個身份證也只會對應一個人。
那么使用MyBatis是怎么處理圖中的這種一對一關聯關系的呢?
在前面所講解的< resultMap >元素中,包含了一個< association >子元素,MyBatis就是通過該元素來處理一對一關聯關系的。
在< association >元素中,通常可以配置以下屬性:
property:指定映射到的實體類對象屬性,與表字段一 一對應
column:指定表中對應的字段
javaType:指定映射到實體對象屬性的類型
select:指定引入嵌套查詢的子SQL語句,該屬性用于關聯映射中的嵌套查詢
fetchType:指定在關聯查詢時是否啟用延遲加載。該屬性有lazy和eager兩個屬性值,默認值為lazy(即默認關聯映射延遲加載)
MyBatis加載關聯關系對象主要通過兩種方式:嵌套查詢和嵌套結果。
第一種: 嵌套查詢是通過執行另外一條SQL映射語句來返回預期的復雜類型。
- 嵌套查詢是在查詢SQL中嵌入一個子查詢SQL;
- 嵌套查詢會執行多條SQL語句;
- 嵌套查詢SQL語句編寫較為簡單;
第二種: 嵌套結果是使用嵌套結果映射來處理重復的聯合結果的子集。
- 嵌套結果是一個嵌套的多表查詢SQL;
- 嵌套結果只會執行一條復雜的SQL語句;
- 嵌套結果SQL語句編寫比較復雜;
“雖然使用嵌套查詢的方式比較簡單,但是嵌套查詢的方式要執行多條SQL語句,這對于大型數據集合和列表展示不是很好,因為這樣可能會導致成百上千條關聯的SQL語句被執行,從而極大的消耗數據庫性能并且會降低查詢效率。
多學一招:MyBatis延遲加載的配置
使用MyBatis的延遲加載在一定程度上可以降低運行消耗并提高查詢效率。MyBatis默認沒有開啟延遲加載,需要在核心配置文件中的< settings >元素內進行配置,具體配置方式如下:
-
-
name ="lazyLoadingEnabled"value="true"/> -
name ="aggressiveLazyLoading"value="false"/>
在映射文件中,< association > 元素和< collection > 元素中都已默認配置了延遲加載屬性,即默認屬性fetchType="lazy"(屬性fetchType="eager"表示立即加載),所以在配置文件中開啟延遲加載后,無需在映射文件中再做配置。
使用< association >元素進行一對一關聯映射非常簡單,只需要參考如下兩種示例配置即可。
代碼實現:
- 第一種:
- --嵌套查詢:通過執行另外一條SQL映射語句來返回預期的特殊類型-->
- <selectid="findPersonById"parameterType="Integer"
- resultMap="IdCardWithPersonResult">
- SELECT*fromtb_personwhereid=#{id}
- select>
-
"Person" id="IdCardWithPersonResult"> -
"id" column="id"/> -
"name" column="name"/> -
"age" column="age"/> -
"sex" column="sex"/> - --一對一:association使用select屬性引入另外一條SQL語句-->
-
"card" column="card_id"javaType="IdCard" - select="com.nateshao.mapper.IdCardMapper.findCodeById"/>
- 第二種:
- --嵌套結果:使用嵌套結果映射來處理重復的聯合結果的子集-->
- <selectid="findPersonById2"parameterType="Integer"
- resultMap="IdCardWithPersonResult2">
- SELECTp.*,idcard.code
- fromtb_personp,tb_idcardidcard
- wherep.card_id=idcard.id
- andp.id=#{id}
- select>
-
"Person" id="IdCardWithPersonResult2"> -
"id" column="id"/> -
"name" column="name"/> -
"age" column="age"/> -
"sex" column="sex"/> -
"card" javaType="IdCard"> -
"id" column="card_id"/> -
"code" column="code"/>
Person.java
- @Data
- publicclassPerson{
- privateIntegerid;
- privateStringname;
- privateIntegerage;
- privateStringsex;
- privateIdCardcard;//個人關聯的證件
- }
IdCard.java
- @Data
- publicclassIdCard{
- privateIntegerid;
- privateStringcode;
- }
3. 一對多
開發人員接觸更多的關聯關系是一對多(或多對一)。例如,一個用戶可以有多個訂單,同時多個訂單歸一個用戶所有。
那么使用MyBatis是怎么處理這種一對多關聯關系的呢?
在前面所講解的< resultMap >元素中,包含了一個< collection >子元素,MyBatis就是通過該元素來處理一對多關聯關系的。
< collection >子元素的屬性大部分與< association>元素相同,但其還包含一個特殊屬性--ofType 。
ofType:ofType屬性與javaType屬性對應,它用于指定實體對象中集合類屬性所包含的元素類型。
代碼實現:
- /**
- *一對多
- */
- @Test
- publicvoidfindUserTest(){
- //1、通過工具類生成SqlSession對象
- SqlSessionsession=MybatisUtils.getSession();
- //2、查詢id為1的用戶信息
- Useruser=session.selectOne("com.nateshao.mapper."
- +"UserMapper.findUserWithOrders",1);
- //3、輸出查詢結果信息
- System.out.println(user);
- //4、關閉SqlSession
- session.close();
- }
UserMapper.xml
- --一對多:查看某一用戶及其關聯的訂單信息
- 注意:當關聯查詢出的列名相同,則需要使用別名區分-->
- <selectid="findUserWithOrders"parameterType="Integer"
- resultMap="UserWithOrdersResult">
- SELECTu.*,o.idasorders_id,o.number
- fromtb_useru,tb_orderso
- WHEREu.id=o.user_id
- andu.id=#{id}
- select>
-
"User" id="UserWithOrdersResult"> -
"id" column="id"/> -
"username" column="username"/> -
"address" column="address"/> - --一對多關聯映射:collection
-
ofType表示屬性集合中元素的類型,List
屬性即Orders類--> -
"ordersList" ofType="Orders"> -
"id" column="orders_id"/> -
"number" column="number"/>
User.java
- @Data
- publicclassUser{
- privateIntegerid;//用戶編號
- privateStringusername;//用戶姓名
- privateStringaddress;//用戶地址
-
privateList
ordersList;//用戶關聯的訂單 - }
Orders.java
- @Data
- publicclassOrders{
- privateIntegerid;//訂單id
- privateStringnumber;//訂單編號
- //關聯商品集合信息
-
privateList
productList; - }
運行結果:
- User(id=1,username=詹姆斯,address=克利夫蘭,ordersList=[Orders(id=1,number=1000011,productList=null),Orders(id=2,number=1000012,productList=null)])
4. 多對多
在實際項目開發中,多對多的關聯關系也是非常常見的。以訂單和商品為例,一個訂單可以包含多種商品,而一種商品又可以屬于多個訂單。
在數據庫中,多對多的關聯關系通常使用一個中間表來維護,中間表中的訂單id作為外鍵參照訂單表的id,商品id作為外鍵參照商品表的id。
在MyBatis中,多對多的關聯關系查詢,同樣可以使用前面介紹的< collection >元素進行處理(其用法和一對多關聯關系查詢語句用法基本相同)。
MybatisAssociatedTest.java
- /**
- *多對多
- */
- @Test
- publicvoidfindOrdersTest(){
- //1、通過工具類生成SqlSession對象
- SqlSessionsession=MybatisUtils.getSession();
- //2、查詢id為1的訂單中的商品信息
- Ordersorders=session.selectOne("com.nateshao.mapper."
- +"OrdersMapper.findOrdersWithPorduct",1);
- //3、輸出查詢結果信息
- System.out.println(orders);
- //4、關閉SqlSession
- session.close();
- }
OrdersMapper.xml
- --多對多嵌套結果查詢:查詢某訂單及其關聯的商品詳情-->
- <selectid="findOrdersWithPorduct2"parameterType="Integer"
- resultMap="OrdersWithPorductResult2">
- selecto.*,p.idaspid,p.name,p.pricefromtb_orderso,tb_productp,tb_ordersitemoiWHEREoi.orders_id=o.idandoi.product_id=p.idando.id=#{id}
- select>
- --自定義手動映射類型-->
-
"Orders" id="OrdersWithPorductResult2"> -
"id" column="id"/> -
"number" column="number"/> - --多對多關聯映射:collection-->
-
"productList" ofType="Product"> -
"id" column="pid"/> -
"name" column="name"/> -
"price" column="price"/>
Orders.java
- @Data
- publicclassOrders{
- privateIntegerid;//訂單id
- privateStringnumber;//訂單編號
- //關聯商品集合信息
-
privateList
productList; - }
Product.java
- @Data
- publicclassProduct{
- privateIntegerid;//商品id
- privateStringname;//商品名稱
- privateDoubleprice;//商品單價
-
privateList
orders;//與訂單的關聯屬性 - }
總結:
這篇文章首先對開發中涉及到的數據表之間以及對象之間的關聯關系作了簡要介紹,并由此引出了MyBatis框架中對關聯關系的處理;
然后通過案例對MyBatis框架處理實體對象之間的三種關聯關系進行了詳細講解。
通過本章的學習,我們可以了解數據表以及對象中所涉及到的三種關聯關系,并能夠使用MyBatis框架對三種關聯關系的查詢進行處理。MyBatis中的關聯查詢操作在實際開發中非常普遍,熟練掌握這三種關聯查詢方式有助于提高項目的開發效率。
原文鏈接:https://mp.weixin.qq.com/s/-kRhntzG8TIqL4HSNqwNCQ