Mybatis分頁外掛與limit分頁
1.mybatis自帶的分頁RowBounds;
Mybatis提供了一個簡單的邏輯分頁使用類RowBounds(物理分頁當然就是我們在sql語句中指定limit和offset值),在DefaultSqlSession提供的某些查詢介面中我們可以看到RowBounds是作為引數用來進行分頁的,如下介面:
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds)
RowBounds原始碼如下:
public class RowBounds { /* 預設offset是0**/ public static final int NO_ROW_OFFSET = 0; /* 預設Limit是int的最大值,因此它使用的是邏輯分頁**/ public static final int NO_ROW_LIMIT = Integer.MAX_VALUE; public static final RowBounds DEFAULT = new RowBounds(); private int offset; private int limit; public RowBounds() { this.offset = NO_ROW_OFFSET; this.limit = NO_ROW_LIMIT; } public RowBounds(int offset, int limit) { this.offset = offset; this.limit = limit; } public int getOffset() { return offset; } public int getLimit() { return limit; } }
邏輯分頁的實現原理:
在DefaultResultSetHandler中,邏輯分頁會將所有的結果都查詢到,然後根據RowBounds中提供的offset和limit值來獲取最後的結果,DefaultResultSetHandler實現如下:
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException { DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>(); //跳過RowBounds設定的offset值 skipRows(rsw.getResultSet(), rowBounds); //判斷資料是否小於limit,如果小於limit的話就不斷的迴圈取值 while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) { ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null); Object rowValue = getRowValue(rsw, discriminatedResultMap); storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()); } } private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) throws SQLException { //判斷資料是否小於limit,小於返回true return !context.isStopped() && context.getResultCount() < rowBounds.getLimit(); } //跳過不需要的行,應該就是rowbounds設定的limit和offset private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException { if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) { if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) { rs.absolute(rowBounds.getOffset()); } } else { //跳過RowBounds中設定的offset條資料 for (int i = 0; i < rowBounds.getOffset(); i++) { rs.next(); } } }
總結:Mybatis的邏輯分頁比較簡單,簡單來說就是取出所有滿足條件的資料,然後捨棄掉前面offset條資料,然後再取剩下的資料的limit條
2.mybatis外掛或者直接書寫sql進行分頁
(1).通過自己的封裝SQL根據beginNum(開始條數)和endNum(需要的條數)來進行分頁
(2).PageHelper分頁外掛
分頁外掛的原理就是實現mybatis的攔截器,實現裡面的方法:
--> mybatis自帶分頁RowBounds: //邏輯分頁
Java:
RowBounds rb=new RowBounds(offset, limit); //offset(從多少條開始);limit(獲取多少條)
SqlSession sqlSession=sqlSessionFactory.openSession();//sqlSessionFactory通過讀取mybatis配置檔案的輸入流然後通過new SqlSeesionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));最終得到SqlSessionFactory;
List<Student> studentlist=sqlSession.selectList("xx.xx.Mapper.findStudent",null,rb);//第一個引數為具體Mapper檔案的下的findStudent的ID,第二個引數為提供的條件引數,第三個引數為我們要進行對獲取學生進行分頁
sqlSession.close();
return studentlist;
Mapper:
<select id="findStudent" resultType="Student">
select * from Student
</select>
備註:通過以上例子,很明顯的看出,在分頁的時候,我們是把所有的資料都查詢出來,然後通過RowBounds進行在記憶體分頁.通過原始碼檢視,也是通過ResuleSet結果集進行分頁;
--> mybatis自寫sql或者通過分頁外掛PageHelper: //物理分頁
(1).mybatis PageHelper分頁外掛
Mapper:
<select id="findStudent" resultType="Student">
select * from Student
</select>
Dao層-StudentDao:
List<Student> findStudent();
Service層:
PageHelper.startPage(pageNum,pageSize);//pageNum 頁數 pageSize 數量
List<Student> stu=studentDao.findStudent(); //studentDao @Autowried註解獲取; 在執行查詢資料時,就會自動執行2個sql;執行上述Mapper下的ID為findStudent的sql 自動執行分頁,通過PageHelper進行識別是何資料庫拼接分頁語句,若是mysql,自動通過limit分頁,若是oracle自動通過rownum進行分頁,另一個會自動拼接Mapper下不存在的ID為findStudent_COUNT,查詢的總數;可以通過列印的日誌進行跟蹤;
PageInfo<Student> page = new PageInfo<Student>(stu); //自動封裝總數count以及分頁,資料返回頁面
return page;//返回分頁之後的資料
(2).mybatis 自行使用SQL進行分頁
例:
SQL程式碼(mysql資料庫):
A:select * from Student LIMIT #{beginNum,jdbcType=INTEGER},#{endNum,jdbcType=INTEGER} //beginNum開始條數;endNum需要的條數
B:select count(0) from Student
JAVA:
Map<String,Object> map=new HashMap<String,Object>();
map.put("beginNum",beginNum);
map.put("endNum",endNum); //從第beginNum條開始,讀取後面endNum條
List<Student> studentlist=studentDao.findStudent(Map<String,Object> map); //studentDao學生dao層
Integer count=studentDao.count();//獲取總數返回頁面
//需要手動進行封裝總數以及分頁資訊,資料返回頁面;
備註:檢視如上例子程式碼,我們就發現了是直接通過SQL進行在資料庫中直接分頁,得到的資料就是我們想要分頁之後的資料,就是物理分頁;
下面有個例項是在我用外掛是遇到的:
使用分頁外掛在查詢的時候會分成兩個sql語句來執行的,一個是查詢出總共有多少條資料,在查出對應的頁碼資料。
如下面的sql語句在執行的時候會分出兩個sql:
SELECT iiid,title,detail,makeDate,paperMediaSource,infoUrl,keyWord,(SELECT imgUrl
FROM INFO_IMG img WHERE img.iiid=item.iiid) AS imgUrl
from info_item item WHERE item.makeDate<=#{expireDate} AND ChanNum='010'
AND InfoCls='001179' ORDER BY item.makeDate DESC分成:
1、SELECT count(*) FROM info_item item WHERE item.makeDate <= ? AND ChanNum = '010' AND InfoCls = '001179'
2、SELECT TOP 20 iiid, title, detail, makeDate, paperMediaSource, infoUrl, keyWord, imgUrl
FROM (SELECT ROW_NUMBER() OVER ( ORDER BY item.makeDate DESC) PAGE_ROW_NUMBER, iiid, title, detail,
makeDate, paperMediaSource, infoUrl, keyWord, (SELECT imgUrl FROM INFO_IMG img WHERE img.iiid = item.iiid)
AS imgUrl FROM info_item item WHERE item.makeDate <= '2018-08-30 23:59:59' AND ChanNum = '010' AND InfoCls = '001179')
AS PAGE_TABLE_ALIAS WHERE PAGE_ROW_NUMBER > 0 ORDER BY PAGE_ROW_NUMBER