在我們的查詢數據庫的操作中,有兩種分頁方式,一種是邏輯翻頁(假翻頁), 一種是物理翻頁(真分頁)。
邏輯分頁的原理就是把所有數據都查詢出來,在內存中篩選數據。物理分頁是真正的分頁,比如MySQL使用Limit語句,Oracle使用rownum語句,SQL Server使用top語句。
邏輯翻頁
MyBatis中有一個邏輯翻頁對象RowBounds,里面主要有兩個屬性,offset和limit(從第幾條開始,查詢多少數據)。
我們可以在mapper接口方法上加上這個參數,不需要修改xml里面SQL語句。
public List<Blog> selectBlogList(RowBounds rowBounds);
使用:
int start = 10;
int pageSize = 5;
RowBounds rb = new RowBounds(start, pageSize);
List<Blog> list = mapper.selectBlogList(rb);
for (Blog blog : list) {
System.out.println(blog);
}
它的底層是對ResultSet的處理,它會舍棄掉前面offset條數據,然后再取剩下數據的limit條。
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
skipRows(rsw.getResultSet(), rowBounds);
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());
}
}
很明顯,如果數據量大的話,這種分頁效率會很低(和查詢到內存中再使用subList(start, end)沒有什么區別),所以我們一般會使用物理分頁。
物理分頁
物理分頁是真實的分頁,它是通過數據庫支持的語句來實現分頁功能的。
第一種簡單的辦法就是傳入參數(或者包裝成一個page對象), 在SQL語句中分頁。
<select id="selectBlogPage" parameterType="map" resultType="baseMap">
select * from tb_blog limit #{curIndex}, #{pageSize}
</select>
這種方法有一個問題就是我們要在Java代碼中計算起止序號,第二個問題就是每一個需要分頁的statement都要編寫limit語句,會造成Mapper映射器中有很多冗余代碼。
那我們就需要一種通用的方式,不需要去修改配置的任何一條SQL語句,只要在我們需要分頁的地方封裝一下分頁對象就可以了。
最常用的做法是使用分頁插件,這個是基于MyBatis的攔截器實現的,比如PageHelper。
PageHelper.startPage(pn, 10);
List<Employee> list = mapper.getAll();
PageInfo page = new PageInfo(list, 10);
return Msg.success().add("pageInfo", page);
PageHelper是根據MyBatis的攔截器實現的,簡單的說,它會根據PageHelper的參數,改寫我們的SQL,比如MySQL會生成limit,Oracle會生成rownum。