備注:
1.查詢員工表的時候,可能傳回很多條,每一條員工資料,有可能需要查詢員工所在的部門的表資訊;
查詢訂單的時候,可能傳回很多條,每一條訂單資料,有可能需要查詢訂單對應的産品的具體資訊,這個時候需要查詢産品表;
1.MyBatis關聯查詢的方式
1.1.嵌套結果
package com.gupaoedu.domain.associate;
import com.gupaoedu.domain.Author;
import java.io.Serializable;
public class BlogAndAuthor implements Serializable {
Integer bid; // 文章ID
String name; // 文章标題
Author author; // 作者
public Integer getBid() {
return bid;
}
public void setBid(Integer bid) {
this.bid = bid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
@Override
public String toString() {
return "BlogAndAuthor{" +
"bid=" + bid +
", name='" + name + '\'' +
", author=" + author +
'}';
}
}
<!-- 根據文章查詢作者,一對一查詢的結果,嵌套查詢 -->
<!--根據selectBlogWithAuthorResult查詢的字段去比對,其中a.author_id , a.author_name比對Author類型的author_id和author_name-->
<resultMap id="BlogWithAuthorResultMap" type="com.gupaoedu.domain.associate.BlogAndAuthor">
<id column="bid" property="bid" jdbcType="INTEGER"/>
<result column="name" property="name" jdbcType="VARCHAR"/>
<!-- 聯合查詢,将author的屬性映射到ResultMap -->
<association property="author" javaType="com.gupaoedu.domain.Author">
<id column="author_id" property="authorId"/>
<result column="author_name" property="authorName"/>
</association>
</resultMap>
<!-- 根據文章查詢作者,一對一,一一比對,一一對應,嵌套結果,無N+1問題 -->
<select id="selectBlogWithAuthorResult" resultMap="BlogWithAuthorResultMap" >
select b.bid, b.name, b.author_id, a.author_id , a.author_name
from blog b
left join author a
on b.author_id=a.author_id
where b.bid = #{bid, jdbcType=INTEGER}
</select>
1.2.嵌套查詢
<!-- 另一種聯合查詢(一對一)的實作,但是這種方式有“N+1”的問題 -->
<resultMap id="BlogWithAuthorQueryMap" type="com.gupaoedu.domain.associate.BlogAndAuthor">
<id column="bid" property="bid" jdbcType="INTEGER"/>
<result column="name" property="name" jdbcType="VARCHAR"/>
<association property="author" javaType="com.gupaoedu.domain.Author" column="author_id" select="selectAuthor"/>
</resultMap>
<!-- 嵌套查詢 -->
<select id="selectAuthor" parameterType="int" resultType="com.gupaoedu.domain.Author">
select author_id authorId, author_name authorName
from author where author_id = #{authorId}
</select>
舉例說明
selectAuthor 定義在下面,這裡注意,SQL查詢結束,不管你用不用Author對象中的方法和屬性,
都會觸發selectAuthor,也就是下面的SQL查詢,這樣實際上會造成資源浪費
@Test
public void testSelectBlogWithAuthorQuery() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
BlogAndAuthor blog = mapper.selectBlogWithAuthorQuery(1);
System.out.println("-----------:"+blog.getClass());
//<!-- 延遲加載的全局開關。當開啟時,所有關聯對象都會延遲加載。預設 false -->
// <setting name="lazyLoadingEnabled" value="false"/>
// System.out.println("-----------getAuthor:"+blog.getAuthor().toString());
}
通過運作結果發現,即使我們沒有使用Author對象的方法和屬性,
也會觸發第二個Author對象的SQL查詢,這就是所謂的N+1問題
1.2.1.解決方案1-延遲加載全局開關:lazyLoadingEnabled
再次執行上面的Test,不會再查詢Author對象
但是一旦觸發.Author對象的調用,就會延遲加載查詢selectAuthor,如下
由于 BlogAndAuthor blog對象中有Author對象,是以一旦列印blog,即觸發了selectAuthor
1.2.2. 侵略性的懶加載 aggressiveLazyLoading = true
aggressiveLazyLoading 中文可以這麼了解:侵略性的懶加載,
不管怎麼樣[不管你的lazyLoadingEnabled配置是否開啟],關聯的查詢selectAuthor都會查詢
@Test
public void testSelectBlogWithAuthorQuery() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
BlogAndAuthor blog = mapper.selectBlogWithAuthorQuery(1);
System.out.println("-----------:"+blog.getClass());
// 如果開啟了延遲加載,會在使用的時候才發出SQL
// equals,clone,hashCode,toString也會觸發延遲加載
// System.out.println("-----------調用toString方法:"+blog);
// System.out.println("-----------getAuthor:"+blog.getAuthor().toString());
// 如果 aggressiveLazyLoading = true ,也會觸發加載,否則不會
System.out.println("-----------getName:"+blog.getName());
}
延遲加載能力的對象所用到的代理工具
<!-- Mybatis 建立具有延遲加載能力的對象所用到的代理工具,預設JAVASSIST -->
<!--<setting name="proxyFactory" value="CGLIB" />-->