文章目錄
- 分頁
- 1. RowBounds 分頁
- 2. PageHelper 分頁
- 3. PageInfo 對象屬性描述
- 延遲加載
- 立即加載
- 激進式延遲加載
- 真-延遲加載
分頁
Mybatis 中實現分頁功能有 3 種途徑:
- RowBounds 分頁(不建議使用)
- Example 分頁(簡單情況可用)
- PageHelper 分頁(推薦 )
1. RowBounds 分頁
MyBatis 本身通過 RowBounds 對象提供了分頁功能,你僅需為你的 dao 的查詢方法多添加 RowBounds 類型的一個參數,并且不需要對配置文件做任何調整。
RowBounds 也稱原生分頁、邏輯分頁。
RowBounds bounds = new RowBounds(0, 4);
List<Employee> list = dao.select(bounds);
但是這種分頁是一種 邏輯分頁,MyBatis 并未使用 limit 子句,查詢的仍是 所有數據,只是它僅給你「看」到了所有數據中的一部分,邏輯分頁雖然完成了分頁功能,但是它并未通過分頁功能的對性能問題有所提升。
2. PageHelper 分頁
PageHelper 是一款被廣泛使用的 MyBatis 插件。它通過 Mybatis 的插件機制,巧妙地通過機制,在不需要配置文件(不需要寫 limit 子句)的情況下,動態去修改你所執行的 SQL,在其后動態添加 limit 子句。
為了使用 PageHelper 需要引入相應的包:
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.1.8</version>
</dependency>
PageHelper 是一款 MyBaits 插件,使用它需要向 Mybatis 注冊 PageHelper,并對它作出相關配置(mybatis-config.xml
)。
<plugins><!-- com.github.pagehelper 為 PageHelper 類所在包名 --><plugin interceptor="com.github.pagehelper.PageInterceptor"><!-- 使用下面的方式配置參數,后面會有所有的參數介紹 --><property name="helperDialect" value="mysql" /><property name="..." value="..."/></plugin>
</plugins>
警告
pagehelper 有 4.x 和 5.x 兩個版本,用法有所不同,并不是向下兼容,同樣的配置在使用 4.x 或 5.x 版本中可能會報錯。例如,上面的helperDialect
就是 5.x 中的配置,在 4.x 中使用的是dialect
。
如果 Mybatis 整合進了 Spring,除了上述這樣配置外,還可以將相應的注冊-配置工作就在 Spring 的配置文件中進行:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property .../><property name="plugins"><array><bean class="com.github.pagehelper.PageInterceptor"><property name="properties"><!--使用下面的方式配置參數,一行配置一個 --><value>param1=value1param2=value2...</value></property></bean></array></property>
</bean>
插件的屬性配置:
-
helperDialect
用于指明底層數據庫類型:oracle, mysql, mariadb, sqlite, hsqldb, postgresql, db2, sqlserver, informix, h2, sqlserver2012, derby
-
reasonable
是否啟用『合理化』功能
啟用(true)時,如果
pageNum < 1
,會返回第一頁內容;如果pageNum > pages
,會返回查詢最后一頁。禁用(false)時,超出合理的范圍會直接返回空數據。
在使用 PageHelper 時,PageHelper 提供了 2 種風格來描述分頁:
- pageNum / pageSize 組合
插件作者建議推薦方式
PageHelper.startPage(1, 10);
這種風格實際上是在模擬「人的語氣」。
- offset / limit 組合
PageHelper.offsetPage(0, 3);
很顯然,這種風格就是 SQL 語句的分頁寫法
在你調用查詢方法之前,調用 PageHelper 的上述兩個方法中的任意一個,都可激活 PageHelper 插件的分頁功能,使其動態地『幫』你修改SQL語句(添加 limit 子句)。而 Mybatis 的 select 返回的結果就返回的是一頁數據。
PageHelper.startPage(4, 2);
List<Employee> list = empDao.selectByExample(null);
PageInfo<Employee> info = new PageInfo<>(list, 5);System.out.println(info);
注意
由于 PageHelper 插件的實現涉及到 ThreadLocal 原理,這導致一旦 PageHelper 生產了一個分頁參數(一個內部使用的 Page 對象),但是沒有被消費,這個參數就會一直保留在這個線程的 ThreadLocal 中。當這個線程再次被使用時,就可能導致不該分頁的方法去消費這個分頁參數,這就產生了莫名其妙的分頁。所以,分頁參數的創建代碼,和查詢方法的調用代碼,必須「緊密的在一起」。
PageHelper 插件流行的原因在于,它不僅僅能實現分頁功能,而且還進一步封裝了頁面上的『分頁導航條』所需要的所有相關信息。
在使用 PageHelper 的過程中,我們已經提供了 4 個關鍵數據:
PageHelper.startPage(4, 2); // 當前頁的頁號, 每頁顯示的數據量
...
PageInfo<Employee> info = new PageInfo<>(list, 5); // 查詢結果, 導航欄上導航數字的個數
在創建了 PageInfo 之后便可以使用它:
// << < 2 3 [4] 5 6 > >>log.info("是否有下一頁:{}", pageInfo.isHasNextPage());
log.info("是否有上一頁:{}", pageInfo.isHasPreviousPage());
log.info("導航欄上第一個頁號:{}", pageInfo.getNavigateFirstPage());
log.info("導航欄上最后一個頁號:{}", pageInfo.getNavigateLastPage());
log.info("導航欄上的五個導航數字:{}", Arrays.toString(pageInfo.getNavigatepageNums()));
log.info("共有 {} 頁", pageInfo.getPages());
log.info("{} / {} ", pageInfo.getPageNum(), pageInfo.getPages());
log.info("共有 {} 條數據", info.getTotal());
3. PageInfo 對象屬性描述
屬性 | 說明 | 舉例 |
---|---|---|
int pageNum | 當前頁 | 比如,當前為第 5 頁 |
int pageSize | 每頁的數量 | 比如,每頁(計劃/預期)顯示 10 條數據 |
int size | 當前頁的數量 | 比如,以 98 條總數據為例,每頁最多顯示 10 條(最后一頁顯示 8 條數據) |
int startRow | 當前頁面第一個元素在數據庫中的行號 | 比如,以 98 條總數據的最后一頁為例,第一條數據是第 91 條 |
int endRow | 當前頁面最后一個元素在數據庫中的行號 | 比如,以 98 條總數據的最后一頁為例,最后一條數據是第 98 條 |
int pages | 總頁數 | 比如,以 98 條總數據為例,每頁顯示 10 條(其中最后一頁 8 條),因此共 10 頁 |
int prePage | 前一頁 | 比如,當前是第 5 頁,所以前一頁為 4 |
int nextPage | 下一頁 | 比如,當前是第 5 頁,所以下一頁為 6 |
boolean isFirstPage | 是否為第一頁 | 比如,當前是第 5 頁,不是第 1 頁,所以為 false |
boolean isLastPage | 是否為最后一頁 | 比如,當前是第 5 頁,不是最后 1 頁,所以為 false |
boolean hasPreviousPage | 是否有前一頁 | 比如,當前是第 5 頁,有前一頁,所以為 true |
boolean hasNextPage | 是否有下一頁 | 比如,當前是第 5 頁,有后一頁,所以為 true |
int navigatePages | 導航頁碼數 | 比如,頁面導航欄顯示 [3 4 5 6 7] 共 5 個數字 |
int[] navigatepageNums | 所有導航頁號 | 比如,頁面導航欄顯示 [3 4 5 6 7] 這 5 個數字 |
int navigateFirstPage | 導航條上的第一頁 | 比如,頁面導航欄顯示 [3 4 5 6 7] 時,第一頁是第 3 頁 |
int navigateLastPage | 導航條上的最后一頁 | 比如,頁面導航欄顯示 [3 4 5 6 7] 時,第一頁是第 7 頁 |
延遲加載
如果一個對象關聯另一個對象,那么在查詢 A 對象的時候,會去關聯查詢 B 對象。
何時查詢(加載)B 對象分為三種時機:
- 立即加載
- 激進式延遲加載
- 延遲加載
立即加載
MyBaits 默認是立即加載,即在查詢 A 對象的時候,會立即查詢其關聯的 B 對象。如果,B 對象也有關聯對象,例如 C 對象,那么還會立即查詢 C 對象,… 因此類推,直到把所有有關聯關系的數據全部查詢出來。
激進式延遲加載
通過設置,可以啟用延遲加載:
<settings><setting name="lazyLoadingEnabled" value="true"/>
</settings>
啟用延遲加載之后,Mybatis 又是默認的激進地延遲加載。
Mybatis 內部會進行某種規則判斷,從而使得激進式的延遲加載,有時候等同于立即加載,有時候等同于普通的延遲加載。
真-延遲加載
可以再通過配置關閉掉激進地延遲加載,從而進入普通的延遲加載:
<settings><setting name="lazyLoadingEnabled" value="true"/><setting name="aggressiveLazyLoading" value="false"/>
</settings>
普通的延遲加載只會在你真正用到 A 對象的 B 屬性時,再去查詢/加載 B 對象。