1. 程式人生 > 實用技巧 >自定義註解,更優雅的使用MP分頁功能

自定義註解,更優雅的使用MP分頁功能

分頁功能使用

MP的分頁功能是通過MyBatis的外掛實現的,使用起來也非常簡單。下面先介紹下使用方式。

step1:配置分頁外掛

@Configuration
@EnableTransactionManagement
@MapperScan("com.csx.demo.spring.boot.dao")
public class MyBatisPlusConfig {

    private static final Logger log = LoggerFactory.getLogger(MyBatisPlusConfig.class);

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 如果是單資料來源的話,最好制定資料庫型別,不然的話MP需要根據資料庫連線來推斷資料庫型別
        // 效能上略有損失
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

需要注意的是:MP提供了很多開箱即用的外掛,這些外掛的使用順序有講究。官方文件建議的配置順序是:

目前已有的功能:

  • 自動分頁: PaginationInnerInterceptor
  • 多租戶: TenantLineInnerInterceptor
  • 動態表名: DynamicTableNameInnerInterceptor
  • 樂觀鎖: OptimisticLockerInnerInterceptor
  • sql效能規範: IllegalSQLInnerInterceptor
  • 防止全表更新與刪除: BlockAttackInnerInterceptor

注意:
使用多個功能需要注意順序關係,建議使用如下順序
多租戶,動態表名
分頁,樂觀鎖
sql效能規範,防止全表更新與刪除
總結: 對sql進行單次改造的優先放入,不對sql進行改造的最後放入

step2:寫分頁程式碼

IPage<User> page = new Page<>(1,10);
((Page<User>) page).addOrder(OrderItem.desc("user_id"));
IPage<User> userIPage = userDAO.selectPage(page, null);

自定義註解,更優雅的使用MP分頁功能

我們發現:雖然MP的分頁外掛使用起來非常簡單,但是還是需要每次從引數中拿分頁引數、排序引數等,程式碼看起來還是略微顯得冗餘。

這邊定義了一個自定義註解,可以簡化上面的這些操作。下面是實現的程式碼。

step1:定義自己的PageInfo

說明下:這邊的PageInfo是可以不定義的,你可以直接使用MP的IPage實現。但是個人具有潔癖,有些資訊不想返回前端,所以定義了一個精簡的PageInfo.

/**
 * 自定義的PageInfo
 * 內容比MP中的IPage精簡
 * @param <T>
 */
public class PageInfo<T> {

    private Integer pageNo;

    private Integer pageSize;

    private String sortColumn;
    
    private Long total;

    private List<T> rows;
    
    public PageInfo(Integer pageNo, Integer pageSize, String sortColumn) {
        this.pageNo = pageNo;
        this.pageSize = pageSize;
        this.sortColumn = sortColumn;
    }
    // 省略get和set方法
}

step2:定義分頁註解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Pagination {

    // 可以自定義分頁欄位名稱,當前頁欄位名稱預設是pageNO
    String pageNoField() default "pageNo";
    // 可以自定義分頁欄位名稱,每頁數量名稱預設是pageNO
    String pageSizeField() default "pageSize";
    // 可以自定義分頁欄位名稱,排序欄位名稱預設是pageNO
    String sortField() default "sort";
    // 也可以通過註解指定排序欄位
    String sortItem() default "";
}

step3:註解處理類

@Aspect
@Component
public class PaginationHandler {

    private static final Logger log = LoggerFactory.getLogger(PaginationHandler.class);

    private static final int DEFAULT_PAGE_NO = 1;
    private static final int DEFAULT_PAGE_SIZE = 10;

    @Around("@annotation(pagination)&&args(pageParam)")
    public Object handlePagination(ProceedingJoinPoint point,
                                   Pagination pagination,
                                   Object pageParam) throws Throwable {
        int pageNo = DEFAULT_PAGE_NO;
        int pageSize = DEFAULT_PAGE_SIZE;
        String sortCols;

        String pageNoField = pagination.pageNoField();
        String pageSizeField = pagination.pageSizeField();
        String sortField = pagination.sortField();
        sortCols = pagination.sortItem();

        if (pageParam == null) {
            PageInfo pageInfo = new PageInfo(pageNo, pageSize, sortCols);
            PageUtil.setPageInfo(pageInfo);
        } else {
            if (pageParam instanceof Map) {
                Map<String, Object> param = (Map<String, Object>) pageParam;
                JSONObject json = new JSONObject(param);
                if (json.getInteger(pageNoField) != null) {
                    pageNo = json.getIntValue(pageNoField);
                }
                if (json.getInteger(pageSizeField) != null) {
                    pageSize = json.getIntValue(pageSizeField);
                }
                if (json.getInteger(sortField) != null) {
                    sortCols = json.getString(sortField);
                }
                PageInfo pageInfo = new PageInfo(pageNo, pageSize, sortCols);
                PageUtil.setPageInfo(pageInfo);
            } else {
                // 暫時只支援Map型別的引數
                // 如果需要支援其他型別的引數,可以在這邊新增
                PageInfo pageInfo = new PageInfo(pageNo, pageSize, sortCols);
                PageUtil.setPageInfo(pageInfo);
            }
        }
        try {
            Object result = point.proceed();
            if (result instanceof Response) {
                Object data = ((Response) result).getData();
                if(data instanceof IPage){
                    long total = ((IPage) data).getTotal();
                    List records = ((IPage) data).getRecords();
                    PageInfo pageInfo = PageUtil.getPageInfo();
                    pageInfo.setTotal(total);
                    pageInfo.setRows(records);
                    ((Response) result).setData(pageInfo);
                }
                return result;
            } else {
                // Todo 暫時沒想到好的處理方法
                return result;
            }
        } finally {
            PageUtil.removePageInfo();
        }
    }
}

step4:分頁工具

public class PageUtil {

    private static final String ASC = "asc";

    private static final String DESC = "desc";

    private static final Logger log = LoggerFactory.getLogger(PageUtil.class);

    private static final ThreadLocal<PageInfo> pageInfoHolder = new ThreadLocal<>();

    public static void setPageInfo(PageInfo pageInfo) {
        pageInfoHolder.set(pageInfo);
    }

    public static void removePageInfo() {
        pageInfoHolder.remove();
    }

    public static PageInfo getPageInfo() {
        return pageInfoHolder.get();
    }

    public static <T> IPage<T> page() {
        PageInfo pageInfo = getPageInfo();
        Integer pageNo = pageInfo.getPageNo();
        Integer pageSize = pageInfo.getPageSize();
        //col1:aes,col2:des形式
        String sortCols = pageInfo.getSortColumn();
        IPage<T> iPage = new Page<>(pageNo, pageSize);
        if (!StringUtils.isEmpty(sortCols)) {
            String[] split = sortCols.split(",");
            for (String s : split) {
                try {
                    int index = s.lastIndexOf(':');
                    String col = s.substring(0, index);
                    String sortType = s.substring(index, s.length());
                    if(ASC.equalsIgnoreCase(sortCols)){
                        ((Page<T>) iPage).addOrder(OrderItem.asc(col));
                    } else if (DESC.equalsIgnoreCase(sortType)){
                        ((Page<T>) iPage).addOrder(OrderItem.desc(col));
                    } else {
                        log.warn("sort col {} is invalid, ignore it...",s);
                        continue;
                    }
                } catch (Exception e) {
                    log.warn("sort col {} is invalid, ignore it...",s);
                }
            }
        }
        return iPage;
    }
}

step5:使用

 @PostMapping("/page")
    @Pagination
    public Object info(@RequestBody Map param)  {
        IPage<User> page = userService.page(PageUtil.page(), null);
        Response response = new Response();
        response.success().success().setData(page);
        return response;
    }

上面的程式碼比較簡單,具體就不分析了。