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的问题

相关文章
相关标签/搜索