欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于RowBounds分頁(yè)原理、RowBounds的坑記錄

 更新時(shí)間:2023年04月25日 09:57:10   作者:五月天的尾巴  
這篇文章主要介紹了關(guān)于RowBounds分頁(yè)原理、RowBounds的坑記錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

背景說(shuō)明

項(xiàng)目中經(jīng)常會(huì)使用分頁(yè)查詢(xún),有次使用了RowBounds進(jìn)行分頁(yè),因?yàn)楹芏鄨?chǎng)景或網(wǎng)上也看到很多這樣的寫(xiě)法,所以我也在項(xiàng)目中使用了該類(lèi)進(jìn)行分頁(yè)。

但是有次線上卻拋了異常,由此引發(fā)了對(duì)RowBounds原理的探究。

RowBounds是將所有符合條件的數(shù)據(jù)全都查詢(xún)到內(nèi)存中,然后在內(nèi)存中對(duì)數(shù)據(jù)進(jìn)行分頁(yè),若數(shù)據(jù)量大,千萬(wàn)別使用RowBounds

如我們寫(xiě)的sql語(yǔ)句:

select * from user where id>0 limit 0,10

RowBounds會(huì)將id>0的所有數(shù)據(jù)全都加載到內(nèi)存中,然后截取前10行,若id>0有100萬(wàn)條,則100萬(wàn)條數(shù)據(jù)都會(huì)加載到內(nèi)存中,從而造成內(nèi)存OOM。

一:RowBounds分頁(yè)原理

Mybatis可以通過(guò)傳遞RowBounds對(duì)象,來(lái)進(jìn)行數(shù)據(jù)庫(kù)數(shù)據(jù)的分頁(yè)操作,然而遺憾的是,該分頁(yè)操作是對(duì)ResultSet結(jié)果集進(jìn)行分頁(yè),也就是人們常說(shuō)的邏輯分頁(yè),而非物理分頁(yè)(物理分頁(yè)當(dāng)然就是我們?cè)趕ql語(yǔ)句中指定limit和offset值)。

RowBounds源碼如下:

public class RowBounds {
? /* 默認(rèn)offset是0**/
? public static final int NO_ROW_OFFSET = 0;
? /* 默認(rèn)Limit是int的最大值,因此它使用的是邏輯分頁(yè)**/
? public static final int NO_ROW_LIMIT = Integer.MAX_VALUE;
? public static final RowBounds DEFAULT = new RowBounds();

? private int offset;
? private int limit;

? public RowBounds() {
? ? this.offset = NO_ROW_OFFSET;
? ? this.limit = NO_ROW_LIMIT;
? }

? public RowBounds(int offset, int limit) {
? ? this.offset = offset;
? ? this.limit = limit;
? }

? public int getOffset() {
? ? return offset;
? }

? public int getLimit() {
? ? return limit;
? }

}

對(duì)數(shù)據(jù)庫(kù)數(shù)據(jù)進(jìn)行分頁(yè),依靠offset和limit兩個(gè)參數(shù),表示從第幾條開(kāi)始,取多少條。也就是人們常說(shuō)的start,limit。

下面看看Mybatis的如何進(jìn)行分頁(yè)的。

org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValuesForSimpleResultMap()方法源碼。

Mybatis中使用RowBounds實(shí)現(xiàn)分頁(yè)的大體思路:

先取出所有數(shù)據(jù),然后游標(biāo)移動(dòng)到offset位置,循環(huán)取limit條數(shù)據(jù),然后把剩下的數(shù)據(jù)舍棄。

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
? ? ? throws SQLException {
? DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
//跳過(guò)RowBounds設(shè)置的offset值
? ?skipRows(rsw.getResultSet(), rowBounds);
//判斷數(shù)據(jù)是否小于limit,如果小于limit的話就不斷的循環(huán)取值
? ?while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
? ? ?ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
? ? ?Object rowValue = getRowValue(rsw, discriminatedResultMap);
? ? ?storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
? ?}
}

private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) throws SQLException {
?? ?//判斷數(shù)據(jù)是否小于limit,小于返回true
? ? return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
}

? //跳過(guò)不需要的行,應(yīng)該就是rowbounds設(shè)置的limit和offset
? private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
? ? if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
? ? ? if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
? ? ? ? rs.absolute(rowBounds.getOffset());
? ? ? }
? ? } else {
?? ? ?//跳過(guò)RowBounds中設(shè)置的offset條數(shù)據(jù),只能逐條滾動(dòng)到指定位置
? ? ? for (int i = 0; i < rowBounds.getOffset(); i++) {
? ? ? ? rs.next();
? ? ? }
? ? }
}

二:RowBounds的使用

原理:攔截器。

使用方法:

RowBounds:在dao.java中的方法中傳入RowBounds對(duì)象。

2.1:引入依賴(lài)

<dependency>
?? ?<groupId>org.mybatis</groupId>
?? ?<artifactId>mybatis</artifactId>
?? ?<version>3.5.6</version>
</dependency>

2.2:service層

import org.apache.ibatis.session.RowBounds;

public class UserServiceImpl implements UserService {
?? ?@Autowired
? ? private UserDao userDao;
? ??
? ? @Override
? ? public Map<String, Object> queryUserList(String currentPage, String pageSize) {
? ? ? ? //查詢(xún)數(shù)據(jù)總條數(shù)
? ? ? ? int total = userDao.queryCountUser();
? ? ? ? //返回結(jié)果集
? ? ? ? Map<String,Object> resultMap = new HashMap<String,Object>();
? ? ? ??
? ? ? ? resultMap.put("total", total);
? ? ? ? //總頁(yè)數(shù)
? ? ? ? int totalpage = (total + Integer.parseInt(pageSize) - 1) / Integer.parseInt(pageSize);
? ? ? ? resultMap.put("totalpage", totalpage);
? ? ? ??
? ? ? ? //數(shù)據(jù)的起始行
? ? ? ? int offset = (Integer.parseInt(currentPage)-1)*Integer.parseInt(pageSize);
? ? ? ? RowBounds rowbounds = new RowBounds(offset, Integer.parseInt(pageSize));
? ? ? ? //用戶(hù)數(shù)據(jù)集合
? ? ? ? List<Map<String, Object>> userList = userDao.queryUserList(rowbounds);
? ? ? ??
? ? ? ? resultMap.put("userList", userList);
? ? ? ??
? ? ? ? return resultMap;
? ? }

}

2.3:dao層

import org.apache.ibatis.session.RowBounds;

public interface UserDao {
? ??
? ? public int queryCountUser(); ? ? ? //查詢(xún)用戶(hù)總數(shù)
? ? public List<Map<String, Object>> queryUserList(RowBounds rowbounds); ? ?//查詢(xún)用戶(hù)列表
}

2.4:mapper.xml

mappep.xml里面正常配置,不用對(duì)rowBounds任何操作。

mybatis的攔截器自動(dòng)操作rowBounds進(jìn)行分頁(yè)。

<?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.test.mapper.UserDao">
? ??
? ? <!-- 查詢(xún)用戶(hù)總數(shù) -->
? ? <select id="queryCountUser" resultType="java.lang.Integer">
? ? ? ? select count(1) from user
? ? </select>
? ??
? ? <!-- 查詢(xún)用戶(hù)列表 -->
? ? <select id="queryUserList" resultType="java.util.Map">
? ? ? ? select * from user
? ? </select>
? ??
</mapper>

三:RowBounds的坑

2021-11-19 15:15:14.933 ERROR [task-10] [org.springframework.transaction.interceptor.TransactionInterceptor] Application exception overridden by rollback exception
org.springframework.dao.TransientDataAccessResourceException:
— Error querying database. Cause: java.sql.SQLException: Java heap space
— The error may exist in com/test/mapper/InfoRecordMapper.java (best guess)
– The error may involve com.test.mapper.InfoRecordMapper.selectByExampleAndRowBounds-Inline
— The error occurred while setting parameters

RowBounds是將所有符合條件的數(shù)據(jù)全都查詢(xún)到內(nèi)存中,然后在內(nèi)存中對(duì)數(shù)據(jù)進(jìn)行分頁(yè)

如我們查詢(xún)user表中id>0的數(shù)據(jù),然后分頁(yè)查詢(xún)sql如下:

select * from user where id >0 limit 3,10

但使用RowBounds后,會(huì)將id>0的所有數(shù)據(jù)都加載到內(nèi)存中,然后跳過(guò)offset=3條數(shù)據(jù),截取10條數(shù)據(jù)出來(lái),若id>0的數(shù)據(jù)有100萬(wàn),則100w數(shù)據(jù)都會(huì)被加載到內(nèi)存中,從而造成內(nèi)存OOM。

所以當(dāng)數(shù)據(jù)量非常大時(shí),一定要慎用RowBounds類(lèi)。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論