MyBatis Lazy Loading

MyBatis的Lazy Loading能夠實現延遲查詢Bean裏的嵌套成員類,控制lazy loading的<settings>屬性有java

lazyLoadingEnabled: lazy loading開關,默認爲truemysql

aggressiveLazyLoading: 侵略性 lazy loading 開關, 默認爲true, 這個屬性比較搞笑,若是爲true則當你訪問任何一個屬性都會加載全部的其餘lazy load屬性,即便你根本沒有調用哪一個lazy load屬性,說白了就是aggressiveLazyLoading=true,則lazy load等於沒用,因此要使用lazy load仍是將其設爲falsesql

一個使用lazyload的例子apache

ResultMapsession

    <!-- 使用子查詢的方式查詢成員變量 -->
    <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>

查詢Mappermybatis

    <!-- 測試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>

測試代碼app

    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 DataSourcespa

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|beijingcode

拋開上面註釋中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的問題

相關文章
相關標籤/搜索