針對中小型管理系統,採用「每頁查詢修改 SQL LIMIT 子句」方式實現分頁,程式碼量少、效能可控。本文提供 MyBatis Mapper、Controller 邏輯、JSP 前端完整三層實現。
<mapper namespace="ec.help.dao.UserDao">
<resultMap type="ec.help.bean.User" id="userResult">
<id column="id" javaType="string"/>
<result column="username" javaType="string"/>
<result column="password" javaType="string"/>
</resultMap>
<select id="getUserByPage" resultType="ec.help.bean.User">
select * from user limit #{0}, #{1}
</select>
<!-- #{0}=起始行(0-based), #{1}=頁面大小 -->
</mapper>
@Controller
public class UserController {
@Value("#{configProperties['userPageSize']}")
private String userPageSize; // 配置檔讀取頁面大小
@Autowired
private UserService userService;
@RequestMapping("/listUser.do")
public ModelAndView listUser(@RequestParam(required=false) String page, Model model) {
int pageSize = Integer.parseInt(userPageSize);
// 1. 查總筆數(可優化為 count(*))
List<User> allUsers = userService.getAllUser();
int total = allUsers.size();
model.addAttribute("userNum", total);
// 2. 計算總頁數
int pageTimes = (total + pageSize - 1) / pageSize; // 無條件進位
model.addAttribute("pageTimes", pageTimes);
// 3. 處理當前頁碼
int currentPage = (page == null) ? 1 : Integer.parseInt(page);
model.addAttribute("currentPage", currentPage);
// 4. 計算 OFFSET
int startRow = (currentPage - 1) * pageSize;
// 5. 分頁查詢
List<User> users = userService.getUserByPage(startRow, pageSize);
model.addAttribute("users", users);
return new ModelAndView("user/listUser");
}
}
<%@ taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" %>
<div class="pagging">
<div class="left">共 ${userNum} 條記錄</div>
<div class="right">
<!-- 首頁/上一頁 -->
<c:choose>
<c:when test="${currentPage == 1}">
<span class="disabled"><< 前一頁</span>
</c:when>
<c:otherwise>
<a href="listUser.do?page=${currentPage-1}"><< 前一頁</a>
</c:otherwise>
</c:choose>
<!-- 頁碼列表 -->
<c:forEach begin="1" end="${pageTimes}" varStatus="loop">
<c:choose>
<c:when test="${currentPage == loop.index}">
<span class="current">${loop.index}</span>
</c:when>
<c:otherwise>
<a href="listUser.do?page=${loop.index}">${loop.index}</a>
</c:otherwise>
</c:choose>
</c:forEach>
<!-- 下一頁/尾頁 -->
<c:choose>
<c:when test="${currentPage == pageTimes}">
<span class="disabled">後一頁 >></span>
</c:when>
<c:otherwise>
<a href="listUser.do?page=${currentPage+1}">後一頁 >></a>
</c:otherwise>
</c:choose>
</div>
</div>
SELECT COUNT(*) FROM user 替代全表查詢LIMIT 100000, 20 效能差 → 使用游標/ID 範圍分頁| 方案 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| SQL LIMIT 分頁 | 資料量 < 10萬、中小系統 | 實現簡單、即時資料 | 大偏移效能差 |
| 記憶體分頁 | 資料量極小、靜態資料 | 查詢一次、翻頁極快 | 記憶體壓力大、資料不即時 |
| 游標/Keyset 分頁 | 大資料量、高併發 | 效能穩定、無大偏移問題 | 不支援隨機跳頁、實現複雜 |
| PageHelper/MP 插件 | MyBatis/MyBatis-Plus 專案 | 零侵入、自動改寫 SQL | 複雜查詢可能失效 |