SSM 框架實現分頁顯示查詢(基於用户管理案例)
在 SSM 框架中實現分頁查詢,最常用且高效的方案是集成MyBatis 分頁插件(PageHelper),它能自動攔截 SQL 並添加分頁語句(LIMIT),無需手動編寫複雜分頁 SQL。以下基於之前的 “用户管理” 案例,完整補充分頁查詢的實現步驟,包含配置、代碼編寫與測試。
一、前期準備:集成 PageHelper 分頁插件
1.1 引入 PageHelper 依賴(pom.xml)
在原有 Maven 依賴中添加 PageHelper 座標,需注意與 MyBatis 版本兼容(此處適配 MyBatis 3.4.6):
<!-- MyBatis分頁插件PageHelper --><dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.10</version></dependency><!-- PageHelper與Spring整合依賴(若用MyBatis-Spring需引入) --><dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.13</version> <!-- 排除Spring Boot依賴(因當前是純SSM項目,非Spring Boot) --> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </exclusion> </exclusions></dependency>
1.2 配置 PageHelper 插件(mybatis-config.xml)
在 MyBatis 全局配置文件中,通過<plugins>標籤配置 PageHelper,指定數據庫方言(如 MySQL):
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <!-- 配置分頁插件PageHelper --> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- 指定數據庫方言:MySQL、Oracle等,PageHelper會根據方言生成對應分頁SQL --> <property name="helperDialect" value="mysql"/> <!-- 啓用合理化分頁:若pageNum<1則查詢第一頁,若pageNum>總頁數則查詢最後一頁 --> <property name="reasonable" value="true"/> <!-- 支持通過Mapper接口參數傳遞分頁參數(可選) --> <property name="supportMethodsArguments" value="true"/> </plugin> </plugins> <!-- 其他配置(如別名、環境等,原有配置不變) --> <typeAliases> <!-- 給POJO包配置別名,後續XML中可直接用“User”代替“com.pojo.User” --> <package name="com.pojo"/> </typeAliases></configuration>
二、第二步:修改持久層(Mapper)支持分頁
分頁查詢的核心是 “攔截原有查詢 SQL 並添加分頁條件”,因此無需修改原有selectAllUsers方法,僅需確保 Mapper 接口方法返回 List(PageHelper 會自動將結果封裝為分頁對象)。
2.1 Mapper 接口(UserMapper.java)
無需新增方法,複用原有查詢所有用户的方法(PageHelper 會自動處理分頁):
package com.mapper;import com.pojo.User;import org.apache.ibatis.annotations.Param;import java.util.List;public interface UserMapper { // 原有方法:查詢所有用户(分頁時PageHelper會自動攔截此方法,添加LIMIT) List<User> selectAllUsers(); // (可選)帶條件的分頁查詢(如按用户名模糊查詢) List<User> selectUsersByUsername(@Param("username") String username);}
2.2 Mapper XML(UserMapper.xml)
若需 “帶條件的分頁查詢”,補充對應的 SQL(無需手動加 LIMIT,PageHelper 自動處理):
<mapper namespace="com.mapper.UserMapper"> <!-- 原有結果集映射(不變) --> <resultMap id="UserResultMap" type="User"> <id column="id" property="id"/> <result column="username" property="username"/> <result column="age" property="age"/> <result column="email" property="email"/> <result column="create_time" property="createTime"/> </resultMap> <!-- 1. 原有查詢所有用户(分頁時自動添加LIMIT) --> <select id="selectAllUsers" resultMap="UserResultMap"> SELECT id, username, age, email, create_time FROM tb_user ORDER BY create_time DESC </select> <!-- 2. 帶條件的分頁查詢(按用户名模糊查詢) --> <select id="selectUsersByUsername" parameterType="string" resultMap="UserResultMap"> SELECT id, username, age, email, create_time FROM tb_user WHERE username LIKE CONCAT('%', #{username}, '%') <!-- 模糊查詢條件 --> ORDER BY create_time DESC </select></mapper>
三、第三步:業務層(Service)處理分頁邏輯
Service 層需接收 “頁碼(pageNum)” 和 “每頁條數(pageSize)” 參數,通過 PageHelper 啓動分頁,再調用 Mapper 方法查詢,最終返回分頁結果對象(包含總條數、總頁數、當前頁數據)。
3.1 Service 接口(UserService.java)
新增分頁查詢方法,區分 “無條件分頁” 和 “帶條件分頁”:
package com.service;import com.github.pagehelper.PageInfo;import com.pojo.User;import java.util.List;public interface UserService { // 原有CRUD方法(不變)... /** * 1. 無條件分頁查詢所有用户 * @param pageNum 頁碼(從1開始) * @param pageSize 每頁顯示條數 * @return 分頁結果對象(含總條數、當前頁數據等) */ PageInfo<User> getUserPage(int pageNum, int pageSize); /** * 2. 帶條件分頁查詢(按用户名模糊查詢) * @param pageNum 頁碼 * @param pageSize 每頁條數 * @param username 模糊查詢的用户名(可為null,null時查所有) * @return 分頁結果對象 */ PageInfo<User> getUserPageByUsername(int pageNum, int pageSize, String username);}
3.2 Service 實現類(UserServiceImpl.java)
實現分頁方法,核心是通過PageHelper.startPage(pageNum, pageSize)啓動分頁,後續的第一個 Mapper 查詢會自動被分頁:
package com.service;import com.github.pagehelper.PageHelper;import com.github.pagehelper.PageInfo;import com.mapper.UserMapper;import com.pojo.User;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Servicepublic class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; // 原有CRUD方法實現(不變)... /** * 無條件分頁查詢 */ @Override public PageInfo<User> getUserPage(int pageNum, int pageSize) { // 1. 啓動分頁:pageNum=當前頁碼,pageSize=每頁條數 PageHelper.startPage(pageNum, pageSize); // 2. 調用Mapper查詢(此查詢會自動被分頁,添加LIMIT) List<User> userList = userMapper.selectAllUsers(); // 3. 封裝分頁結果:PageInfo包含總條數、總頁數、當前頁數據等信息 PageInfo<User> pageInfo = new PageInfo<>(userList); return pageInfo; } /** * 帶條件分頁查詢(按用户名模糊查詢) */ @Override public PageInfo<User> getUserPageByUsername(int pageNum, int pageSize, String username) { PageHelper.startPage(pageNum, pageSize); // 若用户名不為null,則按條件查詢;否則查詢所有 List<User> userList = (username != null && !username.trim().isEmpty()) ? userMapper.selectUsersByUsername(username) : userMapper.selectAllUsers(); return new PageInfo<>(userList); }}
關鍵説明:PageInfo 對象的核心屬性
PageInfo 是 PageHelper 提供的分頁結果封裝類,包含前端分頁顯示所需的所有信息,常用屬性如下:
|
屬性名 |
含義 |
示例值 |
|
pageNum |
當前頁碼 |
1 |
|
pageSize |
每頁條數 |
10 |
|
total |
總記錄數 |
100 |
|
pages |
總頁數(total/pageSize,向上取整) |
10 |
|
list |
當前頁的數據列表 |
[User1, User2,...] |
|
isFirstPage |
是否為第一頁 |
true |
|
isLastPage |
是否為最後一頁 |
false |
|
navigatePages |
導航頁碼數(默認 5,如 1 2 3 4 5) |
5 |
|
navigatepageNums |
導航頁碼數組 |
[1,2,3,4,5] |
四、第四步:控制層(Controller)接收分頁請求
Controller 層接收前端傳遞的分頁參數(pageNum、pageSize、查詢條件),調用 Service 分頁方法,返回包含分頁結果的 JSON 數據,供前端渲染分頁組件。
4.1 Controller 類(UserController.java)
新增分頁查詢接口,支持 “無條件分頁” 和 “帶條件分頁”:
package com.controller;import com.github.pagehelper.PageInfo;import com.pojo.User;import com.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;import java.util.HashMap;import java.util.Map;@Controller@RequestMapping("/user")@ResponseBodypublic class UserController { @Autowired private UserService userService; // 原有CRUD接口(不變)... /** * 1. 無條件分頁查詢接口 * URL示例:/user/page?pageNum=1&pageSize=10 * @param pageNum 頁碼(默認1) * @param pageSize 每頁條數(默認10) * @return 分頁結果JSON */ @GetMapping("/page") public Map<String, Object> getUserPage( // @RequestParam設置默認值,避免前端未傳參時報錯 @RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "10") int pageSize) { Map<String, Object> result = new HashMap<>(); try { PageInfo<User> pageInfo = userService.getUserPage(pageNum, pageSize); result.put("code", 200); result.put("msg", "分頁查詢成功!"); result.put("data", pageInfo); // 分頁結果對象 } catch (Exception e) { result.put("code", 500); result.put("msg", "分頁查詢失敗:" + e.getMessage()); } return result; } /** * 2. 帶條件分頁查詢接口(按用户名模糊查詢) * URL示例:/user/page/condition?pageNum=1&pageSize=10&username=張三 * @param pageNum 頁碼(默認1) * @param pageSize 每頁條數(默認10) * @param username 模糊查詢的用户名(可為null) * @return 分頁結果JSON */ @GetMapping("/page/condition") public Map<String, Object> getUserPageByUsername( @RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "10") int pageSize, @RequestParam(required = false) String username) { // required=false表示參數可選 Map<String, Object> result = new HashMap<>(); try { PageInfo<User> pageInfo = userService.getUserPageByUsername(pageNum, pageSize, username); result.put("code", 200); result.put("msg", "條件分頁查詢成功!"); result.put("data", pageInfo); } catch (Exception e) { result.put("code", 500); result.put("msg", "條件分頁查詢失敗:" + e.getMessage()); } return result; }}
五、第五步:前端分頁組件渲染(示例)
前端需接收 Controller 返回的 PageInfo 數據,渲染分頁列表和分頁導航(以 Vue 為例,其他框架邏輯類似):
5.1 前端請求示例(Axios)
// 1. 無條件分頁查詢(第1頁,每頁10條)axios.get("/user/page", { params: { pageNum: 1, pageSize: 10 }}).then(res => { if (res.data.code === 200) { const pageInfo = res.data.data; console.log("總記錄數:", pageInfo.total); console.log("當前頁數據:", pageInfo.list); console.log("總頁數:", pageInfo.pages); // 渲染列表和分頁導航 this.userList = pageInfo.list; this.pageInfo = pageInfo; }});// 2. 帶條件分頁查詢(查詢用户名含“張三”的第1頁數據)axios.get("/user/page/condition", { params: { pageNum: 1, pageSize: 10, username: "張三" }}).then(res => { // 邏輯同上...});
5.2 分頁導航組件示例(Vue 模板)
<template> <div class="pagination"> <!-- 上一頁 --> <button @click="changePage(pageInfo.pageNum - 1)" :disabled="pageInfo.isFirstPage"> 上一頁 </button> <!-- 導航頁碼 --> <button v-for="num in pageInfo.navigatepageNums" :key="num" @click="changePage(num)" :class="{ active: num === pageInfo.pageNum }" > {{ num }} </button> <!-- 下一頁 --> <button @click="changePage(pageInfo.pageNum + 1)" :disabled="pageInfo.isLastPage"> 下一頁 </button> <!-- 分頁信息 --> <span> 共{{ pageInfo.total }}條記錄,當前第{{ pageInfo.pageNum }}頁/共{{ pageInfo.pages }}頁 </span> </div></template><script>export default { data() { return { userList: [], pageInfo: {} // 存儲分頁結果 }; }, methods: { // 切換頁碼 changePage(pageNum) { // 調用分頁查詢接口,傳入新頁碼 this.getUserPage(pageNum); }, // 分頁查詢方法 getUserPage(pageNum) { axios.get("/user/page", { params: { pageNum: pageNum, pageSize: 10 } }).then(res => { if (res.data.code === 200) { this.userList = res.data.data.list; this.pageInfo = res.data.data; } }); } }};</script>
六、關鍵注意事項
- PageHelper 啓動時機:PageHelper.startPage(pageNum, pageSize)必須在 “查詢方法調用前” 執行,且僅對 “後續第一個 Mapper 查詢” 生效,若有多個查詢,需多次調用 startPage。
- 數據庫方言適配:在 mybatis-config.xml 中必須指定helperDialect,否則 PageHelper 無法生成正確的分頁 SQL(如 MySQL 用 LIMIT,Oracle 用 ROWNUM)。
- 合理化分頁:開啓reasonable=true後,若前端傳遞 pageNum=0 或 pageNum > 總頁數,會自動修正為第 1 頁或最後一頁,避免查詢為空。
- 性能優化:分頁查詢本質是通過 LIMIT 優化,但當數據量極大(如千萬級)時,建議結合 “索引” 使用(如按 create_time 排序時,給 create_time 字段建索引),避免全表掃描。