1. 程式人生 > 其它 >PageHelper-分頁外掛詳解,springboot整合pagehelper

PageHelper-分頁外掛詳解,springboot整合pagehelper

技術標籤:Java外掛javamybatisPageHelper

1.不用外掛怎麼實現分頁?

2.使用PageHelper

開源,Mybatis分頁外掛,支援多種物理資料庫

3.springboot整合pagehelper步驟如下

1.匯入依賴
<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper-spring-boot-starter</artifactId>
	<version>1.3.0</version>
</dependency>
2.1編寫分頁方法入參物件
    PageParam
        implements IPage
        setOrderBy
2.2編寫分頁方法介面
    PageService
        page
        list
2.3在業務service上extends PageService並傳遞泛型
2.4在業務serviceImpl中重寫PageService的list方法即可

3.1PageParam

import com.github.pagehelper.IPage;
import lombok.Data;
import lombok.experimental.Accessors;

/**
 * 註解@Accessors實現Entity偽Build 如: entity.setX(x).setY(y)
 *
 * @param <T>
 */
@Data
@Accessors(chain = true)
public class PageParam<T> implements IPage {

    /** description = "頁碼", defaultValue =  1 */
    private Integer pageNum = 1;

    /** description = "頁數", defaultValue = 20 */
    private Integer pageSize = 20;

    /** description = "排序", example = "id desc" */
    private String orderBy;

    /** description = "查詢條件引數" */
    private T param;

    /**此處可優化 優化詳情且看解析*/
    public PageParam<T> setOrderBy(String orderBy) {
        this.orderBy = orderBy;
        return this;
    }
}

3.2PageService

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

import java.util.List;

/**
 * @param <Param>  泛型request
 * @param <Result> 泛型response
 */
public interface PageService<Param, Result> {
    /**
     * 分頁查詢
     * default關鍵字直接宣告預設實現
     *
     * @param param 請求引數DTO
     * @return 分頁集合
     */
    default PageInfo<Result> page(PageParam<Param> param) {
        return PageHelper.startPage(param).doSelectPageInfo(() -> list(param.getParam()));
    }

    /**
     * 集合查詢
     *
     * @param param 查詢引數
     * @return 查詢響應
     */
    List<Result> list(Param param);
}

3.3Controller

//PageParam包括分頁引數和查詢條件引數
@GetMapping("/pageTest/page/{page}/size/{size}")
@ApiOperation(value = "分頁查詢測試")
public ResponseResult pageTest(PageParam<StudentDO> pageParam) {
    return new ResponseResult().resultFlag(true).data(studentService.page(pageParam));
}

3.4Service

public interface IStudentService extends PageService<StudentDO, StudentDO> {
    List<Student> getAllStudent();
}


@Service
public class StudentServiceImpl implements IStudentService {
    @Autowired(required = false)
    StudentMapper studentMapper;

    @Override
    public List<Student> getAllStudent() {
        return studentMapper.getAllStudent();
    }

    /**
     * 分頁查詢獲取資料集合
     *
     * @param o
     * @return
     */
    @Override
    public List list(StudentDO o) {
        return studentMapper.getAllStudent();
    }
}

4.1執行過程

執行過程
1.controller接收分頁引數和查詢條件引數PageParam<StudentDO> pageParam,傳給studentService.page(pageParam)
    PageParam<StudentDO> pageParam的泛型StudentDO是查詢條件引數的型別,通常都是封裝成DTO
    page方法是通過pageService介面繼承而來的,該方法會呼叫分頁的元件
    PageHelper.startPage(param).doSelectPageInfo(() -> list(param.getParam()));
2.service方法的執行過程
    PageHelper.startPage(param).doSelectPageInfo(() -> list(param.getParam()));
    1.doSelectPageInfo引數是通過lambda表示式傳遞,傳遞的是不分頁的查詢結果資料集合
    2.然後轉換成分頁資料
    3.實際開發時,要做的事就是重寫list方法,獲取資料集合,傳遞泛型
        泛型:
            controller引數的泛型是查詢引數型別
            service泛型第一個是查詢引數型別(list方法引數型別)
            第二個引數是查詢資料結果型別(list方法返回值的集合中資料的型別)

4.2原始碼分析

如果只是簡單應用,可以不看這些,但如果要真正理解外掛的操作,需要了解原始碼的執行邏輯
主要需要了解的顯然就是PageHelper執行的特有方法幹了什麼

1.startPage
public static <E> Page<E> startPage(Object params) {
    //獲取page
    Page<E> page = PageObjectUtil.getPageFromObject(params, true);
    //獲取舊分頁
    Page<E> oldPage = getLocalPage();
    //如果舊分頁只排序不分頁,則將排序引數放入新分頁中
    if (oldPage != null && oldPage.isOrderByOnly()) {
        page.setOrderBy(oldPage.getOrderBy());
    }
    //組裝新分頁
    setLocalPage(page);
    return page;
}
1.1	getPageFromObject
    1.該方法先判斷分頁引數集合params是否為null
    2.如果不為null,則判斷是否為IPage型別,是則獲取pageNum,pageSize分頁引數組合成page
    3.返回page用來分頁
    4.如果上述條件不能滿足,那麼會通過反射獲取分頁的引數
public static <T> Page<T> getPageFromObject(Object params, boolean required) {
    //判斷分頁引數是否為null
    if (params == null) {
        throw new PageException("無法獲取分頁查詢引數!");
        //如果分頁引數不為null,判斷是否為IPage型別
    } else if (params instanceof IPage) {
        //組合新的Page,包括pageNum,PageSize
        IPage pageParams = (IPage)params;
        Page page = null;
        if (pageParams.getPageNum() != null && pageParams.getPageSize() != null) {
            page = new Page(pageParams.getPageNum(), pageParams.getPageSize());
        }

        if (StringUtil.isNotEmpty(pageParams.getOrderBy())) {
            if (page != null) {
            page.setOrderBy(pageParams.getOrderBy());
        } else {
            page = new Page();
            page.setOrderBy(pageParams.getOrderBy());
            page.setOrderByOnly(true);
        }
    }

    return page;
    } else {
        ...
        ...
    }
1.2	startPage剩餘程式碼
獲取當前執行緒中的Page,即舊的分頁
如果有舊的分頁,再判斷舊的分頁是否只有排序引數,即是否是隻排序不分頁
如果是這樣,就把排序引數放入新分頁中
2.doSelectPageInfo(ISelect select)
//明確:這是page的方法,引數是個函式式介面
public <E> PageInfo<E> doSelectPageInfo(ISelect select) {
    select.doSelect();
    return this.toPageInfo();
}
2.1doSelectPageInfo(ISelect select)的引數ISelect作為函式式介面,接收lambda表示式傳遞的函式
    第一步就是呼叫這個傳遞的函式,即select.doSelect();獲取要分頁的資料集合
    第二步return this.toPageInfo();轉為分頁形式
    這裡就會產生疑問,為什麼呼叫select.doSelect();之後不做任何操作就直接開始返回分頁資料了?
    實際上PageHelper分頁的操作是通過MyBatis攔截器來做的
    在select.doSelect();執行時,會觸發PageHelper定義的MyBatis攔截器,PageHelper會根據資料庫方言,解析資料,實現分頁
3.Mybatis攔截器
@Intercepts({@Signature(
    type = Executor.class,
    method = "query",
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
), @Signature(
    type = Executor.class,
    method = "query",
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)})
public class PageInterceptor implements Interceptor {
    private volatile Dialect dialect;
    private String countSuffix = "_COUNT";
    protected Cache<String, MappedStatement> msCountMap = null;
    private String default_dialect_class = "com.github.pagehelper.PageHelper";

    public PageInterceptor() {
    }

    public Object intercept(Invocation invocation) throws Throwable {
    ...
    ...
    }
}