在使用Mybatis時(shí),大量的查詢要寫分頁,寫分頁需要先寫一個(gè)查詢count語句,然后再寫一個(gè)真正分頁查詢的語句,代碼不僅僅大量重復(fù)而且完全沒有必要。于是希望只寫一個(gè)select語句,count由插件根據(jù)select語句自動(dòng)完成。
mybatis 有個(gè)分頁插件 pagehelper,它本質(zhì)上是對(duì) mybatis 執(zhí)行流程進(jìn)行了增強(qiáng),添加了 limit 以及 count 查詢,屬于物理分頁。
使用起來也很簡單,分為三步:引包、配置、使用
- 引包
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.4</version>
</dependency>
- 配置
配置分頁攔截器,PageHelper的原理是基于攔截器實(shí)現(xiàn)的。
方式一:在sqlSessionFactory的bean里配置一個(gè)property
方式二:直接配置一個(gè)plugins
方式一:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations">
<array>
<value>classpath*:com/parking/dao/*.xml</value>
</array>
</property>
<property name="typeAliasesPackage" value="com.test.domain.*"/>
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<value>
helperDialect=mysql
</value>
</property>
</bean>
</array>
</property>
</bean>
方式二:
<plugins>
<!-- com.github.pagehelper為PageHelper類所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
<!-- 該參數(shù)默認(rèn)為false -->
<!-- 設(shè)置為true時(shí),會(huì)將RowBounds第一個(gè)參數(shù)offset當(dāng)成pageNum頁碼使用 -->
<!-- 和startPage中的pageNum效果一樣-->
<property name="offsetAsPageNum" value="true"/>
<!-- 該參數(shù)默認(rèn)為false -->
<!-- 設(shè)置為true時(shí),使用RowBounds分頁會(huì)進(jìn)行count查詢 -->
<property name="rowBoundsWithCount" value="true"/>
<!-- 設(shè)置為true時(shí),如果pageSize=0或者RowBounds.limit = 0就會(huì)查詢出全部的結(jié)果 -->
<!-- (相當(dāng)于沒有執(zhí)行分頁查詢,但是返回結(jié)果仍然是Page類型)
<property name="pageSizeZero" value="true"/>-->
<!-- 分頁參數(shù)合理化,默認(rèn)false禁用 -->
<!-- 啟用合理化時(shí),如果pageNum<1會(huì)查詢第一頁,如果pageNum>pages會(huì)查詢最后一頁 -->
<!-- 禁用合理化時(shí),如果pageNum<1或pageNum>pages會(huì)返回空數(shù)據(jù) -->
<property name="reasonable" value="true"/>
<!-- 為了支持startPage(Object params)方法 -->
<!-- 增加了一個(gè)`params`參數(shù)來配置參數(shù)映射,用于從Map或ServletRequest中取值 -->
<!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默認(rèn)值 -->
<!-- 不理解該含義的前提下,不要隨便復(fù)制該配置
<property name="params" value="pageNum=start;pageSize=limit;"/> -->
</plugin>
</plugins>
<!-- 注意 plugins 在mybatis-config.xml文件中的位置,符合mybatis-3-config.dtd 中指定的順序 -->
<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?,
objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>
- 使用
PageHelper.startPage 靜態(tài)方法調(diào)用:
//獲取第1頁,10條內(nèi)容,默認(rèn)查詢總數(shù)count
PageHelper.startPage(1, 10);
List<Country> list = countryMapper.selectAll();
//用PageInfo對(duì)結(jié)果進(jìn)行包裝
PageInfo page = new PageInfo(list);
//測試PageInfo全部屬性
//PageInfo包含了非常全面的分頁屬性
assertEquals(1, page.getPageNum());
assertEquals(10, page.getPageSize());
assertEquals(1, page.getStartRow());
assertEquals(10, page.getEndRow());
assertEquals(183, page.getTotal());
assertEquals(19, page.getPages());
assertEquals(1, page.getFirstPage());
assertEquals(8, page.getLastPage());
assertEquals(true, page.isFirstPage());
assertEquals(false, page.isLastPage());
assertEquals(false, page.isHasPreviousPage());
assertEquals(true, page.isHasNextPage());
pagehelper.PNG
更多詳細(xì)的參數(shù)配置及使用方法,請(qǐng)參考官方文檔。
這樣實(shí)現(xiàn)分頁安全嗎?下面摘自項(xiàng)目原文:
什么時(shí)候會(huì)導(dǎo)致不安全的分頁?
PageHelper 方法使用了靜態(tài)的 ThreadLocal 參數(shù),分頁參數(shù)和線程是綁定的。
只要你可以保證在 PageHelper 方法調(diào)用后緊跟 MyBatis 查詢方法,這就是安全的。因?yàn)?PageHelper 在 finally 代碼段中自動(dòng)清除了 ThreadLocal 存儲(chǔ)的對(duì)象。
如果代碼在進(jìn)入 Executor 前發(fā)生異常,就會(huì)導(dǎo)致線程不可用,這屬于人為的 Bug(例如接口方法和 XML 中的不匹配,導(dǎo)致找不到 MappedStatement 時(shí)), 這種情況由于線程不可用,也不會(huì)導(dǎo)致 ThreadLocal 參數(shù)被錯(cuò)誤的使用。
但是如果你寫出下面這樣的代碼,就是不安全的用法:
PageHelper.startPage(1, 10);
List<Country> list;
if(param1 != null){
list = countryMapper.selectIf(param1);
} else {
list = new ArrayList<Country>();
}
這種情況下由于 param1 存在 null 的情況,就會(huì)導(dǎo)致 PageHelper 生產(chǎn)了一個(gè)分頁參數(shù),但是沒有被消費(fèi),這個(gè)參數(shù)就會(huì)一直保留在這個(gè)線程上。當(dāng)這個(gè)線程再次被使用時(shí),就可能導(dǎo)致不該分頁的方法去消費(fèi)這個(gè)分頁參數(shù),這就產(chǎn)生了莫名其妙的分頁。
上面這個(gè)代碼,應(yīng)該寫成下面這個(gè)樣子:
List<Country> list;
if(param1 != null){
PageHelper.startPage(1, 10);
list = countryMapper.selectIf(param1);
} else {
list = new ArrayList<Country>();
}
這種寫法就能保證安全。
如果你對(duì)此不放心,你可以手動(dòng)清理 ThreadLocal 存儲(chǔ)的分頁參數(shù),可以像下面這樣使用:
List<Country> list;
if(param1 != null){
PageHelper.startPage(1, 10);
try{
list = countryMapper.selectAll();
} finally {
PageHelper.clearPage();
}
} else {
list = new ArrayList<Country>();
}
這么寫很不好看,而且沒有必要。