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 DataSourcespatest_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的問題