天天看點

Mybatis-PageHelper 官方使用文檔

使用方法

  1. 引入分頁插件

    引入分頁插件有下面2種方式,推薦使用 Maven 方式。

1). 引入 Jar 包

你可以從下面的位址中下載下傳最新版本的 jar 包

https://oss.sonatype.org/content/repositories/releases/com/github/pagehelper/pagehelper/

http://repo1.maven.org/maven2/com/github/pagehelper/pagehelper/

由于使用了sql 解析工具,你還需要下載下傳 jsqlparser.jar(需要和PageHelper 依賴的版本一緻) :

http://repo1.maven.org/maven2/com/github/jsqlparser/jsqlparser/

2). 使用 Maven

在 pom.xml 中添加如下依賴:

com.github.pagehelper pagehelper 最新版本 最新版本号可以從首頁檢視。

  1. 配置攔截器插件

    特别注意,新版攔截器是 com.github.pagehelper.PageInterceptor。 com.github.pagehelper.PageHelper 現在是一個特殊的 dialect 實作類,是分頁插件的預設實作類,提供了和以前相同的用法。

  2. 在 MyBatis 配置 xml 中配置攔截器插件

2. 在 Spring 配置檔案中配置攔截器插件 使用 spring 的屬性配置方式,可以使用 plugins 屬性像下面這樣配置: params=value1 3. 分頁插件參數介紹 分頁插件提供了多個可選參數,這些參數使用時,按照上面兩種配置方式中的示例配置即可。

分頁插件可選參數如下:

dialect:預設情況下會使用 PageHelper 方式進行分頁,如果想要實作自己的分頁邏輯,可以實作 Dialect(com.github.pagehelper.Dialect) 接口,然後配置該屬性為實作類的全限定名稱。

下面幾個參數都是針對預設 dialect 情況下的參數。使用自定義 dialect 實作時,下面的參數沒有任何作用。

helperDialect:分頁插件會自動檢測目前的資料庫連結,自動選擇合适的分頁方式。 你可以配置helperDialect屬性來指定分頁插件使用哪種方言。配置時,可以使用下面的縮寫值:

oracle,mysql,mariadb,sqlite,hsqldb,postgresql,db2,sqlserver,informix,h2,sqlserver2012,derby

特别注意:使用 SqlServer2012 資料庫時,需要手動指定為 sqlserver2012,否則會使用 SqlServer2005 的方式進行分頁。

你也可以實作 AbstractHelperDialect,然後配置該屬性為實作類的全限定名稱即可使用自定義的實作方法。

offsetAsPageNum:預設值為 false,該參數對使用 RowBounds 作為分頁參數時有效。 當該參數設定為 true 時,會将 RowBounds 中的 offset 參數當成 pageNum 使用,可以用頁碼和頁面大小兩個參數進行分頁。

rowBoundsWithCount:預設值為false,該參數對使用 RowBounds 作為分頁參數時有效。 當該參數設定為true時,使用 RowBounds 分頁會進行 count 查詢。

pageSizeZero:預設值為 false,當該參數設定為 true 時,如果 pageSize=0 或者 RowBounds.limit = 0 就會查詢出全部的結果(相當于沒有執行分頁查詢,但是傳回結果仍然是 Page 類型)。

reasonable:分頁合理化參數,預設值為false。當該參數設定為 true 時,pageNum<=0 時會查詢第一頁, pageNum>pages(超過總數時),會查詢最後一頁。預設false 時,直接根據參數進行查詢。

params:為了支援startPage(Object params)方法,增加了該參數來配置參數映射,用于從對象中根據屬性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用預設值, 預設值為pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero。

supportMethodsArguments:支援通過 Mapper 接口參數來傳遞分頁參數,預設值false,分頁插件會從查詢方法的參數值中,自動根據上面 params 配置的字段中取值,查找到合适的值時就會自動分頁。 使用方法可以參考測試代碼中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和 ArgumentsObjTest。

autoRuntimeDialect:預設值為 false。設定為 true 時,允許在運作時根據多資料源自動識别對應方言的分頁 (不支援自動選擇sqlserver2012,隻能使用sqlserver),用法和注意事項參考下面的場景五。

closeConn:預設值為 true。當使用運作時動态資料源或沒有設定 helperDialect 屬性自動擷取資料庫類型時,會自動擷取一個資料庫連接配接, 通過該屬性來設定是否關閉擷取的這個連接配接,預設true關閉,設定為 false 後,不會關閉擷取的連接配接,這個參數的設定要根據自己選擇的資料源來決定。

aggregateFunctions(5.1.5+):預設為所有常見資料庫的聚合函數,允許手動添加聚合函數(影響行數),所有以聚合函數開頭的函數,在進行 count 轉換時,會套一層。其他函數和列會被替換為 count(0),其中count列可以自己配置。

重要提示:

當 offsetAsPageNum=false 的時候,由于 PageNum 問題,RowBounds查詢的時候 reasonable 會強制為 false。使用 PageHelper.startPage 方法不受影響。

  1. 如何選擇配置這些參數

    單獨看每個參數的說明可能是一件讓人不爽的事情,這裡列舉一些可能會用到某些參數的情況。

場景一

如果你仍然在用類似ibatis式的命名空間調用方式,你也許會用到rowBoundsWithCount, 分頁插件對RowBounds支援和 MyBatis 預設的方式是一緻,預設情況下不會進行 count 查詢,如果你想在分頁查詢時進行 count 查詢, 以及使用更強大的 PageInfo 類,你需要設定該參數為 true。

注: PageRowBounds 想要查詢總數也需要配置該屬性為 true。

場景二

如果你仍然在用類似ibatis式的命名空間調用方式,你覺得 RowBounds 中的兩個參數 offset,limit 不如 pageNum,pageSize 容易了解, 你可以使用 offsetAsPageNum 參數,将該參數設定為 true 後,offset會當成 pageNum 使用,limit 和 pageSize 含義相同。

場景三

如果覺得某個地方使用分頁後,你仍然想通過控制參數查詢全部的結果,你可以配置 pageSizeZero 為 true, 配置後,當 pageSize=0 或者 RowBounds.limit = 0 就會查詢出全部的結果。

場景四

如果你分頁插件使用于類似分頁檢視清單式的資料,如新聞清單,軟體清單, 你希望使用者輸入的頁數不在合法範圍(第一頁到最後一頁之外)時能夠正确的響應到正确的結果頁面, 那麼你可以配置 reasonable 為 true,這時如果 pageNum<=0 會查詢第一頁,如果 pageNum>總頁數 會查詢最後一頁。

場景五

如果你在 Spring 中配置了動态資料源,并且連接配接不同類型的資料庫,這時你可以配置 autoRuntimeDialect 為 true,這樣在使用不同資料源時,會使用比對的分頁進行查詢。 這種情況下,你還需要特别注意 closeConn 參數,由于擷取資料源類型會擷取一個資料庫連接配接,是以需要通過這個參數來控制擷取連接配接後,是否關閉該連接配接。 預設為 true,有些資料庫連接配接關閉後就沒法進行後續的資料庫操作。而有些資料庫連接配接不關閉就會很快由于連接配接數用完而導緻資料庫無響應。是以在使用該功能時,特别需要注意你使用的資料源是否需要關閉資料庫連接配接。

當不使用動态資料源而隻是自動擷取 helperDialect 時,資料庫連接配接隻會擷取一次,是以不需要擔心占用的這一個連接配接是否會導緻資料庫出錯,但是最好也根據資料源的特性選擇是否關閉連接配接。

  1. 如何在代碼中使用

    閱讀前請注意看重要提示

分頁插件支援以下幾種調用方式:

//第一種,RowBounds方式的調用

List list = sqlSession.selectList(“x.y.selectIf”, null, new RowBounds(0, 10));

//第二種,Mapper接口方式的調用,推薦這種使用方式。

PageHelper.startPage(1, 10);

List list = countryMapper.selectIf(1);

//第三種,Mapper接口方式的調用,推薦這種使用方式。

PageHelper.offsetPage(1, 10);

List list = countryMapper.selectIf(1);

//第四種,參數方法調用

//存在以下 Mapper 接口方法,你不需要在 xml 處理後兩個參數

public interface CountryMapper {

List selectByPageNumSize(

@Param(“user”) User user,

@Param(“pageNum”) int pageNum,

@Param(“pageSize”) int pageSize);

}

//配置supportMethodsArguments=true

//在代碼中直接調用:

List list = countryMapper.selectByPageNumSize(user, 1, 10);

//第五種,參數對象

//如果 pageNum 和 pageSize 存在于 User 對象中,隻要參數有值,也會被分頁

//有如下 User 對象

public class User {

//其他fields

//下面兩個參數名和 params 配置的名字一緻

private Integer pageNum;

private Integer pageSize;

}

//存在以下 Mapper 接口方法,你不需要在 xml 處理後兩個參數

public interface CountryMapper {

List selectByPageNumSize(User user);

}

//當 user 中的 pageNum!= null && pageSize!= null 時,會自動分頁

List list = countryMapper.selectByPageNumSize(user);

//第六種,ISelect 接口方式

//jdk6,7用法,建立接口

Page page = PageHelper.startPage(1, 10).doSelectPage(new ISelect() {

@Override

public void doSelect() {

countryMapper.selectGroupBy();

}

});

//jdk8 lambda用法

Page page = PageHelper.startPage(1, 10).doSelectPage(()-> countryMapper.selectGroupBy());

//也可以直接傳回PageInfo,注意doSelectPageInfo方法和doSelectPage

pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(new ISelect() {

@Override

public void doSelect() {

countryMapper.selectGroupBy();

}

});

//對應的lambda用法

pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(() -> countryMapper.selectGroupBy());

//count查詢,傳回一個查詢語句的count數

long total = PageHelper.count(new ISelect() {

@Override

public void doSelect() {

countryMapper.selectLike(country);

}

});

//lambda

total = PageHelper.count(()->countryMapper.selectLike(country));

下面對最常用的方式進行詳細介紹

1). RowBounds方式的調用

List list = sqlSession.selectList(“x.y.selectIf”, null, new RowBounds(1, 10));

使用這種調用方式時,你可以使用RowBounds參數進行分頁,這種方式侵入性最小,我們可以看到,通過RowBounds方式調用隻是使用了這個參數,并沒有增加其他任何内容。

分頁插件檢測到使用了RowBounds參數時,就會對該查詢進行實體分頁。

關于這種方式的調用,有兩個特殊的參數是針對 RowBounds 的,你可以參看上面的 場景一 和 場景二

注:不隻有命名空間方式可以用RowBounds,使用接口的時候也可以增加RowBounds參數,例如:

//這種情況下也會進行實體分頁查詢

List selectAll(RowBounds rowBounds);

注意: 由于預設情況下的 RowBounds 無法擷取查詢總數,分頁插件提供了一個繼承自 RowBounds 的 PageRowBounds,這個對象中增加了 total 屬性,執行分頁查詢後,可以從該屬性得到查詢總數。

2). PageHelper.startPage 靜态方法調用

除了 PageHelper.startPage 方法外,還提供了類似用法的 PageHelper.offsetPage 方法。

在你需要進行分頁的 MyBatis 查詢方法前調用 PageHelper.startPage 靜态方法即可,緊跟在這個方法後的第一個MyBatis 查詢方法會被進行分頁。

例一:

//擷取第1頁,10條内容,預設查詢總數count

PageHelper.startPage(1, 10);

//緊跟着的第一個select方法會被分頁

List list = countryMapper.selectIf(1);

assertEquals(2, list.get(0).getId());

assertEquals(10, list.size());

//分頁時,實際傳回的結果list類型是Page,如果想取出分頁資訊,需要強制轉換為Page

assertEquals(182, ((Page) list).getTotal());

例二:

//request: url?pageNum=1&pageSize=10

//支援 ServletRequest,Map,POJO 對象,需要配合 params 參數

PageHelper.startPage(request);

//緊跟着的第一個select方法會被分頁

List list = countryMapper.selectIf(1);

//後面的不會被分頁,除非再次調用PageHelper.startPage

List list2 = countryMapper.selectIf(null);

//list1

assertEquals(2, list.get(0).getId());

assertEquals(10, list.size());

//分頁時,實際傳回的結果list類型是Page,如果想取出分頁資訊,需要強制轉換為Page,

//或者使用PageInfo類(下面的例子有介紹)

assertEquals(182, ((Page) list).getTotal());

//list2

assertEquals(1, list2.get(0).getId());

assertEquals(182, list2.size());

例三,使用PageInfo的用法:

//擷取第1頁,10條内容,預設查詢總數count

PageHelper.startPage(1, 10);

List list = countryMapper.selectAll();

//用PageInfo對結果進行包裝

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());

3). 使用參數方式

想要使用參數方式,需要配置 supportMethodsArguments 參數為 true,同時要配置 params 參數。 例如下面的配置:

在 MyBatis 方法中:

List selectByPageNumSize(

@Param(“user”) User user,

@Param(“pageNumKey”) int pageNum,

@Param(“pageSizeKey”) int pageSize);

當調用這個方法時,由于同時發現了 pageNumKey 和 pageSizeKey 參數,這個方法就會被分頁。params 提供的幾個參數都可以這樣使用。

除了上面這種方式外,如果 User 對象中包含這兩個參數值,也可以有下面的方法:

List selectByPageNumSize(User user);

當從 User 中同時發現了 pageNumKey 和 pageSizeKey 參數,這個方法就會被分頁。

注意:pageNum 和 pageSize 兩個屬性同時存在才會觸發分頁操作,在這個前提下,其他的分頁參數才會生效。

3). PageHelper 安全調用

  1. 使用 RowBounds 和 PageRowBounds 參數方式是極其安全的
  2. 使用參數方式是極其安全的
  3. 使用 ISelect 接口調用是極其安全的

    ISelect 接口方式除了可以保證安全外,還特别實作了将查詢轉換為單純的 count 查詢方式,這個方法可以将任意的查詢方法,變成一個 select count(*) 的查詢方法。

  4. 什麼時候會導緻不安全的分頁?

    PageHelper 方法使用了靜态的 ThreadLocal 參數,分頁參數和線程是綁定的。

隻要你可以保證在 PageHelper 方法調用後緊跟 MyBatis 查詢方法,這就是安全的。因為 PageHelper 在 finally 代碼段中自動清除了 ThreadLocal 存儲的對象。

如果代碼在進入 Executor 前發生異常,就會導緻線程不可用,這屬于人為的 Bug(例如接口方法和 XML 中的不比對,導緻找不到 MappedStatement 時), 這種情況由于線程不可用,也不會導緻 ThreadLocal 參數被錯誤的使用。

但是如果你寫出下面這樣的代碼,就是不安全的用法:

PageHelper.startPage(1, 10);

List list;

if(param1 != null){

list = countryMapper.selectIf(param1);

} else {

list = new ArrayList();

}

這種情況下由于 param1 存在 null 的情況,就會導緻 PageHelper 生産了一個分頁參數,但是沒有被消費,這個參數就會一直保留在這個線程上。當這個線程再次被使用時,就可能導緻不該分頁的方法去消費這個分頁參數,這就産生了莫名其妙的分頁。

上面這個代碼,應該寫成下面這個樣子:

List list;

if(param1 != null){

PageHelper.startPage(1, 10);

list = countryMapper.selectIf(param1);

} else {

list = new ArrayList();

}

這種寫法就能保證安全。

如果你對此不放心,你可以手動清理 ThreadLocal 存儲的分頁參數,可以像下面這樣使用:

List list;

if(param1 != null){

PageHelper.startPage(1, 10);

try{

list = countryMapper.selectAll();

} finally {

PageHelper.clearPage();

}

} else {

list = new ArrayList();

}

這麼寫很不好看,而且沒有必要。

  1. MyBatis 和 Spring 內建示例

    如果和Spring內建不熟悉,可以參考下面兩個

隻有基礎的配置資訊,沒有任何現成的功能,作為新手入門搭建架構的基礎

內建 Spring 3.x

內建 Spring 4.x

這兩個內建架構內建了 PageHelper 和 通用 Mapper。

  1. Spring Boot 內建示例

    https://github.com/abel533/MyBatis-Spring-Boot

繼續閱讀