一、背景
Mybatis Plus支持分頁,但是需要在前端判斷是否開啓分頁、是否統計總數,不支持僅統計總數
在大數據的分頁查詢時,limit
和count
常常有性能問題,需要根據容量評估來進行設計
常見的解決方式
- 將數據按字段排序,然後依據字段 > offsetId,滾動查詢,不返回
count
總數 - 嵌套子查詢獲取offsetId(僅mysql)
等等等
本文僅討論如何在Mybatis Plus
中一個接口內實現分頁、查全部、查總數
二、實現
接着上次文章Mybatis Plus自定義分頁(自動開閉分頁)
- 定義擴展
PageParam
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class PageParam<T> extends Page<T> {
private static final long serialVersionUID = -3669295788865936942L;
private Integer isOpenPage = 1;
private Integer isSearchRecord = 1;
@Override
public long getSize() {
if (1 == isOpenPage) {
return super.getSize();
}
return -1;
}
@Override
public long getTotal() {
if (1 == isOpenPage) {
return super.getTotal();
}
return getRecords().size();
}
}
- 擴展
PaginationInnerInterceptor
@NoArgsConstructor
public class PaginationExInnerInterceptor extends PaginationInnerInterceptor {
/**
* 這裏進行count,如果count爲0這返回false(就是不再執行sql了)
*/
@Override
public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);
if (page == null || page.getSize() < 0 || !page.isSearchCount()) {
return isSearchRecord(page);
}
BoundSql countSql;
MappedStatement countMs = buildCountMappedStatement(ms, page.countId());
if (countMs != null) {
countSql = countMs.getBoundSql(parameter);
} else {
countMs = buildAutoCountMappedStatement(ms);
String countSqlStr = autoCountSql(page.optimizeCountSql(), boundSql.getSql());
PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
countSql = new BoundSql(countMs.getConfiguration(), countSqlStr, mpBoundSql.parameterMappings(), parameter);
PluginUtils.setAdditionalParameter(countSql, mpBoundSql.additionalParameters());
}
CacheKey cacheKey = executor.createCacheKey(countMs, parameter, rowBounds, countSql);
Object result = executor.query(countMs, parameter, rowBounds, resultHandler, cacheKey, countSql).get(0);
page.setTotal(result == null ? 0L : Long.parseLong(result.toString()));
return isSearchRecord(page) && continuePage(page);
}
private boolean isSearchRecord(IPage<?> page) {
if (!(page instanceof PageParam)) {
return true;
}
return BaseIsField.YES.val.equals(((PageParam<?>) page).getIsSearchRecord());
}
}
- 優化
重寫willDoQuery
固然好,但當mybatis-plus升級版本修改了willDoQuery方法,那麼存在不可控變化, 在修改內核代碼時,最優的方式應該是擴展,而不是重寫,故優化代碼爲
@NoArgsConstructor
public class PaginationExInnerInterceptor extends PaginationInnerInterceptor {
/**
* 這裏進行count,如果count爲0這返回false(就是不再執行sql了)
*/
@Override
public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
boolean willDoQuery = super.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);
if (page == null) {
return willDoQuery;
}
return isSearchRecord(page) && willDoQuery;
}
private boolean isSearchRecord(IPage<?> page) {
if (!(page instanceof PageParam)) {
return true;
}
return BaseIsField.YES.val.equals(((PageParam<?>) page).getIsSearchRecord());
}
}