PageHelper-分頁外掛詳解,springboot整合pagehelper
阿新 • • 發佈:2021-02-10
技術標籤: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 {
...
...
}
}