MyBatis中的N+1問題

官 方文檔之所以不建議使用這種嵌套的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形式用的就是這種方法。

      第二種方法是使用MyBatis的延遲加載機制(延遲加載示例源碼下載),步驟如下:

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
發佈了37 篇原創文章 · 獲贊 5 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章