一:引言
在學習完前面的mybatis基本語法后,大家都有個認知,這個Mybatis太強大了,比之前使用JDBC寫方便多了,但是你們當初在使用原生JDBC寫SQL查詢的時候有沒有遇到過多表查詢呢?肯定大部分人都遇到過,我剛學JDBC的時候遇到多表查詢我懵了,不知道如何應對,所以我就默默的執行2條SQL語句,分別查詢不同的表,然后對這2個查詢出來的數據通過java代碼控制,使封裝到對象中,這個簡直要崩潰的感覺,但是學完Mybatis多表查詢后,你會愛上Mybatis的
准備工作:我已經准備好了一對一、一對多、多對多的數據表以及基本的Mybatis的搭建,后面的一些操作我會圍繞這上面的數據表開展講解,希望可以理解
二:Mybatis的高級映射
在前面的Mybatis基本使用中映射做過了初步的解釋,但是在這我要把Mybatis的映射的參數做個詳細的介紹,其實高級映射是通過resultMap標簽完成的,而且也是最重要的部分,是為多表查詢做個鋪墊,所以搞懂Mybatis高級映射是很有必要的!
其實resultMap元素有很多子元素和一個值得討論的結構。下面是 resultMap 元素的概念視圖
constructor – 類在實例化時,用來注入結果到構造方法中
idArg – ID 參數;標記結果作為 ID 可以幫助提高整體效能
arg – 注入到構造方法的一個普通結果
id – 一個 ID 結果;標記結果作為 ID 可以幫助提高整體效能
result – 注入到字段或 JavaBean 屬性的普通結果
association – 一個復雜的類型關聯;許多結果將包成這種類型嵌入結果映射 – 結果映射自身的關聯,或者參考一個
collection – 復雜類型的集,嵌入結果映射 – 結果映射自身的集,或者參考一個
discriminator – 使用結果值來決定使用哪個結果映射
case – 基於某些值的結果映射,嵌入結果映射 – 這種情形結果也映射它本身,因此可以包含很多相同的元素,或者它可以參照一個外部的結果映射。
1:id和result
<id column="sid" property="id"></id>
<result column="sname" property="name"></result>

1 ①:property 2 映射到列結果的字段或屬性。如果匹配的是存在的,和給定名稱相同 3 的 JavaBeans 的屬性,那么就會使用。否則 MyBatis 將會尋找給定名稱 4 的字段。這兩種情形你可以使用通常點式的復雜屬性導航。比如,你 5 可以這樣映射一些東西:“username” 6 ②:column 7 從數據庫中得到的列名,或者是列名的重命名標簽。這也是通常和會 8 傳遞給 resultSet.getString(columnName)方法參數中相同的字符串。 9 ③:javaType 10 一個 Java 類的全限定名,或一個類型別名(參加上面內建類型別名 11 的列表)。如果你映射到一個 JavaBean,MyBatis 通常可以斷定類型。 12 然而,如果你映射到的是 HashMap,那么你應該明確地指定 javaType 13 來保證所需的行為。 14 ③:jdbcType 15 在這個后會列出所支持的 JDBC 類型列表中的類型。JDBC 類型是僅 16 僅需要對插入,更新和刪除操作可能為空的列進行處理。這是 JDBC 17 的需要,而不是 MyBatis 的。如果你直接使用 JDBC 編程,你需要指定 18 這個類型-但僅僅對可能為空的值。 19 ④:typeHandler 20 我們在前面討論過默認的類型處理器。使用這個屬性,你可以覆蓋默 21 認的類型處理器。這個屬性值是類的完全限定名或者是一個類型處理 22 器的實現,或者是類型別名

BIT FLOAT CHAR TIMESTAMP OTHER UNDEFINED
TINYINT REAL VARCHAR BINARY BLOB NVARCHAR
SMALLINT DOUBLE LONGVARCHAR VARBINARY CLOB NCHAR
INTEGER NUMERIC DATE LONGVARBINARY BOOLEAN NCLOB
BIGINT DECIMAL TIME NULL CURSOR
2:構造方法
<constructor>
<idArg column="id" javaType="int"/>
<arg column=”username” javaType=”String”/>
</constructor>
三:Mybatis多表查詢之一對一
什么是一對一呢?我先拿現實的例子來說,拋棄其它特殊情況,在我們上學的年代,學校總會統計每個學生的家庭狀況,那我們也是可以認為一個學生有一個家庭信息,一個家庭信息里面有一個學生,兩邊是一對一關系,雖然我這個例子舉得不是太好,但是產品和訂單的例子就會很好的體現出來,比如一個產品對應一個訂單,一個訂單對應一個產品。
在Mybatis里面它不叫一對一查詢,叫關聯查詢,關聯元素處理“有一個”類型的關系,而且關聯中不同的是你需要告訴 MyBatis 如何加載關聯。MyBatis 在這方面會有兩種不同的分別是:嵌套查詢(通過執行另外一個 SQL 映射語句來返回預期的復雜類型)和嵌套結果(使用嵌套結果映射來處理重復的聯合結果的子集)
①:嵌套查詢
select屬性:
使用嵌套結果映射來處理重復的聯合結果的子集
我現在的要求是學生和家庭是一對一關聯關系,這就好辦了,我可以用嵌套查詢來完成,那我們現在來創建2個實體類吧

//學生實體類 注意要把private int fid;改為private Family family //因為是關聯關系,我要在查到學生對象的時候通過外鍵把家庭也查詢出來 //放到學生對象里面 public class Student { private int id; //id private String name; //姓名 private String sex; //性別 private int age; //年齡 private double credit; //成績/學分 private double money; //零花錢 private String address; //住址 private String enrol; //入學時間 private Family family; //外鍵 家庭 private int tid; //外鍵 老師id //構造器/set/get/toString 你們補充一下 } //家庭對象 public class Family { private int id; //家庭主鍵 private int member; //成員個數 private String guardian; //監護人 private String tel; //監護人號碼 private String dad; //爸爸姓名 private String mom; //媽媽姓名 private String address; //家庭住址 //構造器/set/get/toString 你們補充一下 }

<!--配置家庭映射關系--> <resultMap id="familyMapper" type="family"> <id column="fid" property="id"></id> <result column="fmember" property="member"></result> <result column="fguardian" property="guardian"></result> <result column="ftel" property="tel"></result> <result column="fdad" property="dad"></result> <result column="fmom" property="mom"></result> <result column="faddress" property="address"></result> </resultMap> <!--配置學生類映射關系--> <resultMap id="studentMapper" type="student"> <id column="sid" property="id"></id> <result column="sname" property="name"></result> <result column="ssex" property="sex"></result> <result column="sage" property="age"></result> <result column="scredit" property="credit"></result> <result column="smoney" property="money"></result> <result column="saddress" property="address"></result> <result column="senrol" property="enrol"></result> <!--<result column="fid" property="fid"></result>--> <result column="tid" property="tid"></result> <association column="fid" property="family" select="findByIdInFamily" javaType="family"></association> </resultMap> <!-- column:當前student表中連接family的外鍵字段名稱 property:當前封裝family對象的student對象里的名稱 javaType:當前封裝的對象類型 大家會看到里面有個association標簽 但是在后面添加一個select屬性就變成嵌套查詢了 而且select="findByIdInFamily" 所以執行到這一步就找findByIdInFamily查詢標簽的ID --> <!--首先我們得建立一個查詢學生的SQL語句和標簽--> <!--查詢全部學生--> <select id="findAll" resultMap="studentMapper"> select * from student; </select> <!--可是現在的問題是數據庫字段和對象屬性不匹配 所以還要再上面定義映射關系 看上面--> <!--查詢單個家庭信息--> <select id="findByIdInFamily" parameterType="Integer" resultMap="familyMapper"> select * from family where fid=#{id}; </select>
注意:嵌套查詢中被封裝的對象(如上面的Family)一定要字段名和屬性名一樣,如果不一樣,一定要手動映射,否則查詢出來的family為空。雖然這種方式很簡單,但是對於大型數據集合和列表將不會表現很好。問題就是我們熟知的“N+1 查詢問題”。就是說如果我查詢學生的數據有5000條,那么mybatis查詢到學生的每條數據后都會去執行一個嵌套查詢,獲取指定id去查詢關聯的對象(family),5000條數據就會查詢family表5000次+1次查詢學生全部數據,但是這種嵌套查詢也是有一種好處,能延遲加載這樣的查詢就是一個好處,因此你可以分散這些語句同時運行的消耗。然而,如果你加載一個列表,之后迅速迭代來訪問嵌套的數據,你會調用所有的延遲加載,這樣的行為可能是很糟糕的。
②:嵌套結果
嵌套結果查詢是我們經常使用到的,這是結果映射的 ID,可以映射關聯的嵌套結果到一個合適的對象圖中。這是一種替代方法來調用另外一個查詢語句。這允許你聯合多個表來合成到一個單獨的結果集。這樣的結果集可能包含重復,數據的重復組需要被分解,合理映射到一個嵌套的對象圖。為了使它變得容易,MyBatis 讓你“鏈接”結果映射,來處理嵌套結果。
<!--配置學生類映射關系--> <resultMap id="studentMapper" type="student"> <id column="sid" property="id"></id> <result column="sname" property="name"></result> <result column="ssex" property="sex"></result> <result column="sage" property="age"></result> <result column="scredit" property="credit"></result> <result column="smoney" property="money"></result> <result column="saddress" property="address"></result> <result column="senrol" property="enrol"></result> <result column="tid" property="tid"></result> <association property="family" javaType="family" column="fid"> <id column="fid" property="id"></id> <result column="fmember" property="member"></result> <result column="fguardian" property="guardian"></result> <result column="ftel" property="tel"></result> <result column="fdad" property="dad"></result> <result column="fmom" property="mom"></result> <result column="faddress" property="address"></result> </association> </resultMap> <!--property:bean對象名稱 javaType:指定關聯的類型 column:數據庫字段名稱 最少指定前2個--> <!--查詢全部學生--> <select id="findAll" resultMap="studentMapper"> select s.*,f.fmember,f.fguardian,f.ftel,f.fdad,f.fmom,f.faddress from student s inner join family f using(fid) </select> <!--using:表示主鍵和外鍵一樣可以不用on xx=xx -->
四:Mybatis多表查詢之一對多查詢
什么是一對多呢?其實可以很容易理解,在學校每個學生都有輔導員,而且每個輔導員就有多個學生,通常輔導員是管理一個班學生的,所以呀,老師對學生就是一對多關系,我上面的表也可以很好的表現出來,teacher(輔導員)student(學生)
在Mybatis對與一對多稱為集合查詢 ,其實和關聯查詢差不多,集合查詢是把映射嵌套的結果封裝到List中,也可以分為集合嵌套查詢和集合集合嵌套結果,爭奪下面的案例我首先把teacher類和student類給改造一下

//輔導員對象 public class Teacher { private int id; //id private String name; //姓名 private String sex; //性別 private int age; //年齡 private double salary; //工資 private String address; //住址 private List<Student> students; //學生對象 //構造器/set/get/toString 你們補充一下 } //學生對象 public class Student { private int id; //id private String name; //姓名 private String sex; //性別 private int age; //年齡 private double credit; //成績/學分 private double money; //零花錢 private String address; //住址 private String enrol; //入學時間 //構造器/set/get/toString 你們補充一下 } //輔導員接口 public interface TeacherDao { //查詢全部老師信息 List<Teacher> findAll(); } //輔導員xml配置 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cn.xw.dao.TeacherDao"> <resultMap id="teacherMapper" type="teacher"> <id column="tid" property="id"></id> <result column="tname" property="name"></result> <result column="tsex" property="sex"></result> <result column="tsalary" property="salary"></result> <result column="taddress" property="address"></result> </resultMap> <!--查詢全部輔導員信息--> <select id="findAll" resultMap="teacherMapper"> select * from teacher; </select> </mapper>
①:集合的嵌套查詢
<!--輔導員關系映射--> <resultMap id="teacherMapper" type="teacher"> <id column="tid" property="id"></id> <result column="tname" property="name"></result> <result column="tsex" property="sex"></result> <result column="tage" property="age"></result> <result column="tsalary" property="salary"></result> <result column="taddress" property="address"></result> <collection property="students" ofType="student" select="findById" column="tid"></collection> </resultMap> <!--查詢全部輔導員信息--> <select id="findAll" resultMap="teacherMapper"> select * from teacher; </select> <!--學生關系映射--> <resultMap id="studentMapper" type="student"> <id column="sid" property="id"></id> <result column="sname" property="name"></result> <result column="ssex" property="sex"></result> <result column="sage" property="age"></result> <result column="scredit" property="credit"></result> <result column="smoney" property="money"></result> <result column="saddress" property="address"></result> <result column="senrol" property="enrol"></result> </resultMap> <!--查詢單個學生--> <select id="findById" parameterType="Integer" resultMap="studentMapper"> select * from student; </select>
②:集合的嵌套結果
<!--輔導員關系映射--> <resultMap id="teacherMapper" type="teacher"> <id column="tid" property="id"></id> <result column="tname" property="name"></result> <result column="tsex" property="sex"></result> <result column="tage" property="age"></result> <result column="tsalary" property="salary"></result> <result column="taddress" property="address"></result> <collection property="students" ofType="student" column="tid"> <id column="sid" property="id"></id> <result column="sname" property="name"></result> <result column="ssex" property="sex"></result> <result column="sage" property="age"></result> <result column="scredit" property="credit"></result> <result column="smoney" property="money"></result> <result column="saddress" property="address"></result> <result column="senrol" property="enrol"></result> </collection> </resultMap> <!--查詢全部輔導員信息--> <select id="findAll" resultMap="teacherMapper"> select s.*,t.tid,tname,t.tsex,t.tage,t.tsalary,t.taddress from teacher t left join student s using(tid) ; </select>
五:總結(必看細節之我的錯誤總結)
1:自我認知
對於我剛接觸Mybatis的多表操作遇到了好多錯誤bug,俗話說遇到的錯誤越多越要開心,因為這是對你以后的成長,我也覺得這句話有點道理,通過這2天的操作,我對Mybatis的多表操作不能說全部都懂吧,但是在日常對數據進行簡單的增刪改查應該問題不大。說實話我使用Mybatis多表操作遇到過好幾個小時都沒解決的bug,查過好多資料,也發現網上和我出現過一模一樣的問題,如 CSDN的一個人發的問題 其實那些后面評論的解決方法都是不可行的,不能真正解決此問題,接下來我就和大家談談我遇到的一些問題吧,針對mybatis的多表操作
2:遇到的問題及注意事項
①:在做一對一關聯查詢的時候,查詢出來的關聯對象一直為空?
其實這個錯誤和我在上面發的CSDN的錯誤差不多,我先把我的代碼展示出來,錯誤的代碼

<!--配置學生類映射關系--> <resultMap id="studentMapper" type="student"> <id column="sid" property="id"></id> <result column="sname" property="name"></result> <result column="ssex" property="sex"></result> <result column="sage" property="age"></result> <result column="scredit" property="credit"></result> <result column="smoney" property="money"></result> <result column="saddress" property="address"></result> <result column="senrol" property="enrol"></result> <result column="tid" property="tid"></result> <association property="family" javaType="family" column="fid" select="findByIdInFamily"></association> </resultMap> <!--查詢全部學生--> <select id="findAll" resultMap="studentMapper"> select * from student; </select> <!--查詢單個家庭信息 通過關聯查詢查詢出來--> <select id="findByIdInFamily" parameterType="Integer" resultType="family"> select * from family where fid=#{id}; </select> <!-- 為什么family都是為空呢? Student{id=1, name='王生安', sex='女', age=22, credit=11.0, money=401.1, address='安徽六安', enrol='2019-01-08', family=null, tid=4} Student{id=2, name='李鑫灝', sex='女', age=24, credit=4.0, money=902.8, address='安徽合肥', enrol='2019-03-17', family=null, tid=2} Student{id=3, name='薛佛世', sex='女', age=21, credit=52.0, money=532.1, address='安徽蚌埠', enrol='2018-10-16', family=null, tid=2}-->
其實遇到這個問題是最煩人的,即不報錯,也在網上很難找到,如果有個師傅帶着話,相信很快解決,廢話不多說了
問題分析:
首先我的數據庫字段和對象字段不一樣,這個是最關鍵的,在做一對一關聯查詢,我忽略了對family字段的映射,這就直接導致出現空,但是上文CSDN發布的問題是他直接在關聯查詢標簽里面寫映射關系,他的思路是對的,但是他忘了他那個是嵌套查詢,如果是嵌套結果查詢,對sql語句重寫,語法更改一下是沒有問題的

<!--配置學生類映射關系--> <resultMap id="studentMapper" type="student"> <id column="sid" property="id"></id> <result column="sname" property="name"></result> <result column="ssex" property="sex"></result> <result column="sage" property="age"></result> <result column="scredit" property="credit"></result> <result column="smoney" property="money"></result> <result column="saddress" property="address"></result> <result column="senrol" property="enrol"></result> <result column="tid" property="tid"></result> <association property="family" javaType="family" column="fid" select="findByIdInFamily"></association> </resultMap> <!--查詢全部學生--> <select id="findAll" resultMap="studentMapper"> select * from student; </select> <!--映射家庭關系--> <resultMap id="familyMapper" type="family"> <id column="fid" property="id"></id> <result column="fmember" property="member"></result> <result column="fguardian" property="guardian"></result> <result column="ftel" property="tel"></result> <result column="fdad" property="dad"></result> <result column="fmom" property="mom"></result> <result column="faddress" property="address"></result> </resultMap> <!--查詢單個家庭信息 通過關聯查詢查詢出來--> <select id="findByIdInFamily" parameterType="Integer" resultMap="familyMapper"> select * from family where fid=#{id}; </select>
注意事項:
①:其實我們在寫這些關聯查詢的時候,實體類是不用把外鍵字段納入實體類里面,如果粗心的話,操作不當會導致錯誤,我們如果一個對象包含另一個對象就直接使用對象作為類型
②:一對多和多對多本質是是一樣的,一對多是對一方設置數據為List集合而多對多則是雙向的