天天看點

Mybatis之嵌套結果,嵌套查詢和延遲加載

備注:

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());

    }      
Mybatis之嵌套結果,嵌套查詢和延遲加載
通過運作結果發現,即使我們沒有使用Author對象的方法和屬性,
也會觸發第二個Author對象的SQL查詢,這就是所謂的N+1問題      

1.2.1.解決方案1-延遲加載全局開關:lazyLoadingEnabled

Mybatis之嵌套結果,嵌套查詢和延遲加載

再次執行上面的Test,不會再查詢Author對象

Mybatis之嵌套結果,嵌套查詢和延遲加載

但是一旦觸發.Author對象的調用,就會延遲加載查詢selectAuthor,如下

由于 BlogAndAuthor blog對象中有Author對象,是以一旦列印blog,即觸發了selectAuthor

Mybatis之嵌套結果,嵌套查詢和延遲加載
Mybatis之嵌套結果,嵌套查詢和延遲加載

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" />-->