?寫在前面:大家好!我是
晴空?
。如果博客中有不足或者的錯誤的地方歡迎在評論區或者私信我指正,感謝大家的不吝賜教。我的唯一博客更新地址是:https://ac-fun.blog.csdn.net/。非常感謝大家的支持。一起加油,沖鴨!
?用知識改變命運,用知識成就未來!加油 (? ??o??)? (? ??o??)?
文章目錄
- 為什么需要分頁查詢
- 減少數據庫壓力
- 減少網絡傳輸數據量
- 提高系統的穩定性
- 提升用戶體驗
- 原始的實現方式
- 計算偏移量
- 在Mapper接口中定義查詢方法
- 編寫SQL語句
- 開發流程及完整代碼
- Controller層
- Service實現類
- Mapper接口方法
- xml文件SQL
- 使用插件實現
- 引入分頁插件依賴
- Service實現類
- xml文件SQL
- 與原始分頁的不同
為什么需要分頁查詢
?分頁查詢是一種常見的數據庫查詢技術,用于將查詢結果分成多個頁面展示,而不是一次性返回所有數據。使用分頁查詢主要是為了減少數據庫壓力、減少網絡傳輸數據量、提高系統的穩定性、提高客戶體驗。
減少數據庫壓力
?一次性查詢全部數據(例如百萬條記錄)會占用大量的資源(CPU、內存、I/O),導致響應變慢甚至系統崩潰。分頁后,每次僅查詢少量數據(如每頁100條),可以顯著降低負載。
減少網絡傳輸數據量
?分頁查詢每次只傳輸當前頁的數據,相比于全表查詢會極大的減少網絡傳輸的數據量,降低網絡帶寬的占用。
提高系統的穩定性
?后端服務處理分頁查詢時,單次處理的數據量可控,避免因一次性加載大數據導致內存耗盡出現 OOM 問題。對于前端也由于無需一次性渲染大量的數據而減少了內存崩潰的風險。
提升用戶體驗
?分頁查詢由于單次查詢的數據量少,后端與前端可以快速的處理相關的數據。用戶無需進行長時間的等待,極大的提高了客戶的體驗。
原始的實現方式
?如果不使用分頁查詢相關的插件需要我們自己計算分頁查詢的偏移量offset,還需要手動查詢結果集以及數據總條數,并且在相關的 Mapper.xml 中定義分頁查詢的 SQL 語句。主要實現步驟如下:
計算偏移量
?手動分頁查詢需要我們通過在 SQL 語句中添加分頁相關的語法來實現,例如在 MySQL 中的語法:
SELECT * FROM users LIMIT #{pageSize} offset #{offset};
其中,#{offset} 表示偏移量,但是前端的分頁查詢請求中一般只有查詢第幾頁 page 和每頁的大小 pageSize。需要我們先計算一下偏移量是多少。
?需要注意前端傳的 page 是從 0 開始的還是從 1 開始的。
- 從 0 開始則 offset = page * pageSize
- 從 1 開始則 offset = (page - 1) * pageSize
在Mapper接口中定義查詢方法
/*** 分頁查詢結果集* @param page* @param offset* @param name* @param categoryId* @param status* @return*/
List<DishVO> pageQuery(int page, int offset, String name, Integer categoryId, Integer status);/*** 查詢總條數* @return*/
int getTotalSize();
編寫SQL語句
<mapper namespace="com.sky.mapper.DishMapper"><insert id="insert" useGeneratedKeys="true" keyProperty="id">insert into dish (name, category_id, price, image, description, status, create_time, update_time, create_user, update_user)values (#{name}, #{categoryId}, #{price}, #{image}, #{description},#{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})</insert><select id="pageQuery" resultType="com.sky.vo.DishVO">select d.*, c.name as categoryName from dish d left outer join category c on d.category_id = c.id<where><if test="name != null">and d.name like concat('%', #{name}, '%')</if><if test="categoryId != null">and d.category_id = #{categoryId}</if><if test="status != null">and d.status = #{status}</if></where>order by d.create_time desclimit #{page}offset #{offset}</select><select id="getTotalSize" resultType="java.lang.Integer">select count(*) from dish;</select>
</mapper>
開發流程及完整代碼
?在開發過程中 由外而內 進行開發效率會更高一些,我們一般不需要先寫 Mapper.xml 中的 SQL語句,再定義Mapper接口中的方法,然后再通過 Service類 中進行調用。
?一般會從 Service類 開始寫起,然后再通過編輯器的快捷方式幫助我們生成相關的代碼,再一層一層的實現。
Controller層
@GetMapping("/page")
@ApiOperation("菜品分頁查詢")
public Result<PageResult> pageQuery(DishPageQueryDTO dishPageQueryDTO) {log.info("菜品分頁查詢開始[{}]", dishPageQueryDTO);return dishService.pageQuery(dishPageQueryDTO);
}
Service實現類
@Override
public Result<PageResult> pageQuery(DishPageQueryDTO dishPageQueryDTO) {// 計算偏移量int offset = (dishPageQueryDTO.getPage() - 1) * dishPageQueryDTO.getPageSize();// 查詢當前頁的數據List<DishVO> dishVOList = dishMapper.pageQuery(dishPageQueryDTO.getPageSize(), offset, dishPageQueryDTO.getName(),dishPageQueryDTO.getCategoryId(), dishPageQueryDTO.getStatus());// 查詢數據庫中的總條數int total = dishMapper.getTotalSize();PageResult pageResult = new PageResult();pageResult.setTotal(total);pageResult.setRecords(dishVOList);log.info("分頁查詢結果為[{}]", pageResult);return Result.success(pageResult);
}
Mapper接口方法
/*** 分頁查詢結果集* @param page* @param offset* @param name* @param categoryId* @param status* @return*/
List<DishVO> pageQuery(int page, int offset, String name, Integer categoryId, Integer status);/*** 查詢總條數* @return*/
int getTotalSize();
xml文件SQL
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.DishMapper"><insert id="insert" useGeneratedKeys="true" keyProperty="id">insert into dish (name, category_id, price, image, description, status, create_time, update_time, create_user, update_user)values (#{name}, #{categoryId}, #{price}, #{image}, #{description},#{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})</insert><select id="pageQuery" resultType="com.sky.vo.DishVO">select d.*, c.name as categoryName from dish d left outer join category c on d.category_id = c.id<where><if test="name != null">and d.name like concat('%', #{name}, '%')</if><if test="categoryId != null">and d.category_id = #{categoryId}</if><if test="status != null">and d.status = #{status}</if></where>order by d.create_time desclimit #{page}offset #{offset}</select><select id="getTotalSize" resultType="java.lang.Integer">select count(*) from dish;</select>
</mapper>
使用插件實現
?使用分頁插件可以極大的簡化分頁查詢實現。雖然實現的主要原理還是通過原始實現方式中提到的邏輯,但是通過分頁插件我們就可以少寫很多代碼而且一般分頁插件(例如 PageHelper) 會通過動態 SQL 的構建和優化,能夠有效避免傳統分頁方法的性能問題。
?使用插件進行分頁查詢只需要修改一下上述原始實現方式的Service類 及 xml 文件中的 SQL 語句即可。
引入分頁插件依賴
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
Service實現類
@Override
public Result<PageResult> pageQuery(DishPageQueryDTO dishPageQueryDTO) {PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());Page<DishVO> page = dishMapper.pageHelperQuery(dishPageQueryDTO);PageResult pageResult = new PageResult();pageResult.setTotal(page.getTotal());pageResult.setRecords(page.getResult());log.info("分頁查詢結果為[{}]", pageResult);return Result.success(pageResult);}
xml文件SQL
<select id="pageHelperQuery" resultType="com.sky.vo.DishVO">select d.*, c.name as categoryName from dish d left outer join category c on d.category_id = c.id<where><if test="name != null">and d.name like concat('%', #{name}, '%')</if><if test="categoryId != null">and d.category_id = #{categoryId}</if><if test="status != null">and d.status = #{status}</if></where>order by d.create_time desc
</select>
與原始分頁的不同
?使用分頁插件進行分頁不需要手動計算分頁查詢的偏移量,在寫SQL語句時也不需要顯式地使用 LIMIT 和 OFFSET 來實現分頁。而且分頁插件也會直接將查詢的結果集和總條數封裝到 Page對象 中,不需要我們手動的查詢結果集和總條數。
- mysql開啟緩存、設置緩存大小、緩存過期機制
- PageHelper分頁插件最新源碼解讀及使用
- 蒼穹外賣