MyBatis Lazy Loading


MyBatis的Lazy Loading可以實現延遲查詢Bean里的嵌套成員類,控制lazy loading的<settings>屬性有

lazyLoadingEnabled: lazy loading開關,默認為true

aggressiveLazyLoading: 侵略性 lazy loading 開關, 默認為true, 這個屬性比較搞笑,如果為true則當你訪問任何一個屬性都會加載所有的其他lazy load屬性,即使你根本沒有調用哪個lazy load屬性,說白了就是aggressiveLazyLoading=true,則lazy load等於沒用,所以要使用lazy load還是將其設為false

一個使用lazyload的例子

ResultMap

    <!-- 使用子查詢的方式查詢成員變量 -->
    <resultMap id="articleResultMap2" type="Article">
        <id property="id" column="article_id" />
        <result property="title" column="article_title" />
        <result property="content" column="article_content" />
        <association property="user" column="user_id" select="dao.userdao.selectUserByID"/>
    </resultMap>

查詢Mapper

    <!-- 測試lazy load user -->
    <select id="selectArticleById2" parameterType="int" resultMap="dao.base.articleResultMap2">
        SELECT * FROM article WHERE id = #{id}
    </select>

    <select id="selectUserByID" parameterType="int" resultType="User"
            flushCache="false" useCache="true" timeout="10000" statementType="PREPARED">
        SELECT
            *
        FROM user
        WHERE id = #{id}
    </select>

測試代碼

    public String getArticle2(Model model) {
        Article article = articleDao.selectArticleById2(1);
        // 如果你在這里打一個斷點,你會發現還沒有執行到getUser()這一句article.user已經被查詢加載
        // 而如果你將 getUser() 那行注釋,則article.user在執行到這里也不會被加載
        // 我的理解是java是編譯型語言,mybatis可以根據編譯好的中間碼查看哪些屬性被調用
        // 然后在第一次執行sql的時候把后面將會調用到的延遲加載屬性都提前加載了
        // 另外,MyBatis有個更搞笑和騙人的地方是,如果你不在這里打斷點,它lazy load的子查詢就一定會出現在getUser()之后
        // 而如果這里打了斷點,則lazy load的子查詢語句會在selectArticleById2()這個方法就出現了
        System.out.println();
        // 如果aggressiveLazyLoading為true,則getContent()的時候就會執行查詢user的sql
        // 即使你根本沒有調用getUser(),也會將user屬性查詢出來,例如將getUser()那行注釋了
        System.out.println(article.getContent());
        System.out.println("Lazy loading ......");
        System.out.println(article.getUser());
        return "index";
    }

輸出

DEBUG - ooo Using Connection [jdbc:mysql://127.0.0.1:3306/mybatistest?characterEncoding=utf8, UserName=root@localhost, MySQL-AB JDBC Driver]
DEBUG - ==> Preparing: SELECT * FROM article WHERE id = ?
DEBUG - ==> Parameters: 1(Integer)
DEBUG - Cache Hit Ratio [dao.userdao]: 0.0
DEBUG - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6bbe7]
DEBUG - Returning JDBC Connection to DataSource

test_content
Lazy loading ......
DEBUG - Fetching JDBC Connection from DataSource
DEBUG - JDBC Connection [jdbc:mysql://127.0.0.1:3306/mybatistest?characterEncoding=utf8, UserName=root@localhost, MySQL-AB JDBC Driver] will not be managed by Spring
DEBUG - ooo Using Connection [jdbc:mysql://127.0.0.1:3306/mybatistest?characterEncoding=utf8, UserName=root@localhost, MySQL-AB JDBC Driver]
DEBUG - ==> Preparing: SELECT * FROM user WHERE id = ?
DEBUG - ==> Parameters: 1(Integer)
DEBUG - Returning JDBC Connection to DataSource
1|test1|19|beijing

拋開上面注釋中MyBatis各種奇怪的表現不說,MyBatis的Lazy Loading是基於子查詢select的,也是這段

<association property="user" column="user_id" select="dao.userdao.selectUserByID"/>

這個方式最大的問題是會產生N+1問題,假設article里的user是一個List<User>:

1. 使用一條SQL語句查詢Article類(the 1)

2. 使用N條SQL查詢Article里的List<User>

如果我們在查詢Article以后需要遍歷Article的List<User>,則會觸發所有user的Lazy Loading

也就是說我們也無法使用JOIN去使用Lazy Loading,從而避免n+1的問題


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM