1. 程式人生 > >Guns——使用Mybatis-plus的分頁外掛(五)

Guns——使用Mybatis-plus的分頁外掛(五)

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();
    }