Guns——使用Mybatis-plus的分頁外掛(五)
阿新 • • 發佈:2018-12-21
1.介紹常規分頁方法
- 物理分頁: 物理分頁依賴的是某一物理實體,這個物理實體就是資料庫,比如MySQL資料庫提供了limit關鍵字,程式設計師只需要編寫帶有limit關鍵字的SQL語句,資料庫返回的就是分頁結果。
- 邏輯分頁:邏輯分頁依賴的是程式設計師編寫的程式碼。資料庫返回的不是分頁結果,而是全部資料,然後再由程式設計師通過程式碼獲取分頁資料,常用的操作是一次性從資料庫中查詢出全部資料並存儲到List集合中,因為List集合有序,再根據索引獲取指定範圍的資料。
- 注意:一般使用的是物理分頁,因為使用邏輯分頁一次性獲取資料過多,不易把控
2.前臺使用bootstrap table
由於bootstao table內容較多,在這裡不介紹bootstrap table,只是想說明使用此table會自動傳遞部分引數
3.配置相關bean
- 其實就是在配置類上增加一個攔截器進行攔截分頁,這是關鍵操作
/**
* mybatis-plus分頁外掛
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
4.建立攔截器需要的page物件
- 在這裡依據前端傳來的引數進行page的生產
/** * BootStrap Table預設的分頁引數建立 * * @author fengshuonan * @date 2017-04-05 22:25 */ public class PageFactory<T> { public Page<T> defaultPage() { HttpServletRequest request = HttpContext.getRequest(); int limit = Integer.valueOf(request.getParameter("limit")); //每頁多少條資料 int offset = Integer.valueOf(request.getParameter("offset")); //每頁的偏移量(本頁當前有多少條) String sort = request.getParameter("sort"); //排序欄位名稱 String order = request.getParameter("order"); //asc或desc(升序或降序) if (ToolUtil.isEmpty(sort)) { Page<T> page = new Page<>((offset / limit + 1), limit); page.setOpenSort(false); return page; } else { Page<T> page = new Page<>((offset / limit + 1), limit, sort); if (Order.ASC.getDes().equals(order)) { page.setAsc(true); } else { page.setAsc(false); } return page; } } }
5.實戰演練
從下面可以看到我們直接通過PageFactory的方法建立一個Page,並將Page傳到 對應的dao,攔截器會根據此Page引數判斷是否需要分頁
/** * 查詢操作日誌列表 */ @RequestMapping("/list") @Permission(Const.ADMIN_NAME) @ResponseBody public Object list(@RequestParam(required = false) String beginTime, @RequestParam(required = false) String endTime, @RequestParam(required = false) String logName, @RequestParam(required = false) Integer logType) { Page<OperationLog> page = new PageFactory<OperationLog>().defaultPage(); List<Map<String, Object>> result = operationLogService.getOperationLogs(page, beginTime, endTime, logName, BizLogType.valueOf(logType), page.getOrderByField(), page.isAsc()); page.setRecords(new LogWarpper(result).wrap()); return new PageInfoBT<>(page); }
- 從mapper.xml 上來看我們確實看不到有分頁操作的痕跡,所以證明是攔截器幫我們操作了
- 但是分頁攔截器只是幫我們分頁,並沒有幫我們排序或者按欄位排序,所以我們可以看到這一部分需要我們手工寫入xml檔案裡面
<select id="getOperationLogs" resultType="map">
select * from sys_operation_log where 1 = 1
<if test="beginTime != null and beginTime !='' and endTime != null and endTime != ''">
and (createTime between CONCAT(#{beginTime},' 00:00:00') and CONCAT(#{endTime},' 23:59:59'))
</if>
<if test="logName != null and logName !=''">
and logname like CONCAT('%',#{logName},'%')
</if>
<if test="logType != null and logType !=''">
and logtype like CONCAT('%',#{logType},'%')
</if>
<choose>
<when test="orderByField != null and orderByField !=''">
<choose>
<when test="isAsc == true">
order by ${orderByField} ASC
</when>
<otherwise>
order by ${orderByField} DESC
</otherwise>
</choose>
</when>
<otherwise>
order by createtime DESC
</otherwise>
</choose>
</select>
6.原理
總的來說就是判斷欄位是否有傳入page物件,如果有則改變sql語句,之後再繼續執行業務邏輯
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) PluginUtils.realTarget(invocation.getTarget());
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
this.sqlParser(metaObject);
// 先判斷是不是SELECT操作
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
return invocation.proceed();
}
RowBounds rowBounds = (RowBounds) metaObject.getValue("delegate.rowBounds");
/* 不需要分頁的場合 */
if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
// 本地執行緒分頁
if (localPage) {
// 採用ThreadLocal變數處理的分頁
rowBounds = PageHelper.getPagination();
if (rowBounds == null) {
return invocation.proceed();
}
} else {
// 無需分頁
return invocation.proceed();
}
}
// 針對定義了rowBounds,做為mapper介面方法的引數
BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
String originalSql = boundSql.getSql();
Connection connection = (Connection) invocation.getArgs()[0];
DBType dbType = StringUtils.isNotEmpty(dialectType) ? DBType.getDBType(dialectType) : JdbcUtils.getDbType(connection.getMetaData().getURL());
if (rowBounds instanceof Pagination) {
Pagination page = (Pagination) rowBounds;
boolean orderBy = true;
if (page.isSearchCount()) {
SqlInfo sqlInfo = SqlUtils.getOptimizeCountSql(page.isOptimizeCountSql(), sqlParser, originalSql);
orderBy = sqlInfo.isOrderBy();
this.queryTotal(overflowCurrent, sqlInfo.getSql(), mappedStatement, boundSql, page, connection);
if (page.getTotal() <= 0) {
return invocation.proceed();
}
}
String buildSql = SqlUtils.concatOrderBy(originalSql, page, orderBy);
originalSql = DialectFactory.buildPaginationSql(page, buildSql, dbType, dialectClazz);
} else {
// support physical Pagination for RowBounds
originalSql = DialectFactory.buildPaginationSql(rowBounds, originalSql, dbType, dialectClazz);
}
/*
* <p> 禁用記憶體分頁 </p>
* <p> 記憶體分頁會查詢所有結果出來處理(這個很嚇人的),如果結果變化頻繁這個資料還會不準。</p>
*/
metaObject.setValue("delegate.boundSql.sql", originalSql);
metaObject.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
metaObject.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
return invocation.proceed();
}