官 方文檔之所以不建議使用這種嵌套的select語句的形式,是因爲這會導致所謂的“N+1問題”。這個問題,無論是association元素還是 collection元素都會遇到,本文以更爲典型的collection元素爲例。在本系列所使用的示例場景下,當需要查詢教師及其所指導的學生(一個 教師可指導多個學生)信息時,我們會這麼做:先用一條SQL語句(“N+1問題”中的1)查詢教師的信息,即
select * from teacher
此時可查詢出多條(記爲N)教師記錄。爲了進一步查詢出教師指導的學生的信息,需要針對每一條教師記錄,生成一條SQL語句,即
select * from student where supervisor_id=?
以 上SQL語句中的“?”就代表了每個教師的id。顯而易見,這樣的語句被生成了N條(“N+1問題”中的N)。這樣在整個過程中,就總共執行了N+1條 SQL語句,即N+1次數據庫查詢。而數據庫查詢通常是應用程序性能的瓶頸,一般應儘量減少數據庫查詢的次數,那麼這種方式就會大大降低系統的性能。
爲了解決這個問題,可採取兩種方法。第一種方法是使用一條SQL語句,把教師及其指導的學生的信息一次性地查詢出來。在《MyBatis collection的兩種形式》和《MyBatis association的兩種形式》一文中,嵌套的resultMap形式用的就是這種方法。
1、MyBatis的延遲加載機制需要使用cglib包,因此應向工程中添加此包,本文使用的是cglib-nodep-2.2.3.jar。
2、在MyBatis的核心配置文件(在本示例中是configuration.xml)裏的settings元素中添加以下配置:
< settingname = "lazyLoadingEnabled" value = "true" /> < settingname = "aggressiveLazyLoading" value = "false" /> |
現 在來對延遲加載進行測試。首先改造類CollectionDemo.java,讓它先遍歷查找到的教師,然後遍歷某個教師指導的學生的信息(本代碼修改自 本系列的上篇博文《MyBatis多參數傳遞之Map方式示例》,相關知識可參考此文章),相關代碼如下:
//分頁查詢教師信息 List<Teacher>
teachers = mapper.findTeacherByPage(params); if (teachers
== null ) { System.out.println( "未找到相關教師信息。" ); } else { Object[]
t = teachers.toArray(); System.out.println( "**********************************************" ); for ( int
i = 0 ;
i < t.length; i++) { teacher
= (Teacher)t[i]; System.out.println( "教師姓名:"
+ "
"
+ teacher.getName()); System.out.println( "教師職稱:"
+ "
"
+ teacher.getTitle()); System.out.println( "**********************************************" ); } //遍歷當前teacher對象指導的學生 for (Student
s : teacher.getSupStudents()) { System.out.println(
s.getName() + "
"
+ s.getGender () + "
"
+ s.getGrade() +
"
"
+ s.getMajor()); } } |
爲了更清晰地觀察程序執行的細節,應修改Log4j的配置文件log4j.properties,相關配置片段如下(關於MyBatis Log4j用法,可參考本系列的博文:MyBatis日誌之Log4j示例):
#全局日誌配置 log4j.rootLogger=DEBUG,
stdout log4j.logger.com.abc.mapper=DEBUG #關於MyBatis
Log4j用法,可參考筆者博客: #http: //legend2011.blog.51cto.com/3018495/999944 #log4j.logger.com.abc.mapper.TeacherMapper=DEBUG log4j.logger.com.abc.mapper.TeacherMapper.findTeacherByPage=TRACE #因查詢指導的學生信息用到了SQL語句getStudents,因此如下配置 #可打印出執行學生查詢的細節 log4j.logger.com.abc.mapper.StudentMapper.getStudents=TRACE |