首頁 > 軟體

Spring Boot 整合PageHelper的使用方法

2022-04-20 22:00:23

前言:

專案中資料分頁是一個很常見的需求,目前大部分專案都會使用pagehelper進行分頁,那麼在使用的過程中是否考慮如下問題?

一、基本整合

引入jar包

  <dependency>
		<groupId>com.github.pagehelper</groupId>
		<artifactId>pagehelper-spring-boot-starter</artifactId>
		<version>${pagehelper.version}</version>
 </dependency>

Yml組態檔中新增相關設定

pagehelper:
    helperDialect: mysql
    reasonable: true
    supportMethodsArguments: true
    params: count=countSql

封裝相關分頁方法

分頁引數類
 public class PageParam<T> implements Serializable
{
    private static final long serialVersionUID = -7916211163897873899L;
    private int pageNum=1;
    private int pageSize=10;
    //條件引數
    private T param;
    //排序欄位
    private String orderBy;

    public int getPageSize()
    {
        return pageSize;
    }
    public void setPageSize(int pageSize)
    {
        this.pageSize = pageSize;
    }
    public int getPageNum()
    {
        return pageNum;
    }
    public void setPageNum(int pageNum)
    {
        this.pageNum = pageNum;
    }
    public T getParam()
    {
        return param;
    }
    public void setParam(T param)
    {
        this.param = param;
    }
    public String getOrderBy()
    {
        return orderBy;
    }
    public void setOrderBy(String orderBy)
    {
        //需要注意sql注入
        this.orderBy = orderBy;
    }
}
分頁結果類
public class PagedList<T> implements Serializable
{
    private static final long serialVersionUID = -1253790062865437768L;
    private int pageNum = 1;
    private List<T> data = null;
    private int pageCount = 0;
    private int recordCount = -1;
    private int pagingType = 0;
    private int pageSize;
    private String orderBy;
    /**
     * @return the pageSize
     */
    public int getPageSize()
    {
        return pageSize;
    }
    /**
     * @param pageSize
     *            the pageSize to set
     */
    public void setPageSize(int pageSize)
    {
        if (pageSize <= 0)
        {
            return;
        }
        this.pageSize = pageSize;
    }

    /**
     * @return the pageCount
     */
    public int getPageCount()
    {
        return pageCount;
    }
    /**
     * @param pageCount
     *            the pageCount to set
     */
    public void setPageCount(int pageCount)
    {
        if (pageCount <= 0)
        {
            return;
        }
        this.pageCount = pageCount;
    }
    /**
     * @return the recordCount
     */
    public int getRecordCount()
    {
        return recordCount;
    }
    /**
     * @param recordCount
     *            the recordCount to set
     */
    public void setRecordCount(int recordCount)
    {
        this.recordCount = recordCount;
        calcPageCount();
    }
    private void calcPageCount()
    {
        if (this.recordCount < 0)
        {
            return;
        }
        int tmp = this.recordCount % getPageSize();
        this.pageCount = (tmp == 0 ? (this.recordCount / getPageSize())
                : (this.recordCount / getPageSize() + 1));
        if (this.pageNum > this.pageCount && this.pageCount != 0)
        {
            this.pageNum = this.pageCount;
        }
        this.pageNum = this.pageCount;
    }
    public void setData(List<T> data)
    {
        this.data = data;
        if (ObjectUtil.isNotEmpty(data) && this.recordCount == -1)
        {
            this.recordCount = data.size();
        }
    }
    public List<T> getData()
    {
        return data;
    }
    /**
     * @return the pagingType
     */
    public int getPagingType()
    {
        return pagingType;
    }
    /**
     * @param pagingType
     *            the pagingType to set
     */
    public void setPagingType(int pagingType)
    {
        this.pagingType = pagingType;
    }

    public void setOrderBy(String orderBy)
    {
        this.orderBy = orderBy;
    }
    public int getPageNum()
    {
        return pageNum;
    }
    public void setPageNum(int pageNum)
    {
        this.pageNum = pageNum;
    }
    public String getOrderBy()
    {
        return orderBy;
    }
}
分頁工具類
public class PageUtils implements Serializable
{
    private static final long serialVersionUID = 377943433889798799L;
    public static <T> PagedList<T> exportPagedList(PageParam<T> pageParam)
    {
        PagedList<T> pl = new PagedList<T>();
        // pagesize
        int pageSize = pageParam.getPageSize();
        if (pageSize <= 0)
        {
            pageSize = 10;
        }
        else
        {
            pl.setPageSize(pageSize);
        }
        int pageNum  = pageParam.getPageNum();
        pl.setPageNum(pageNum);
       String orderBy= pageParam.getOrderBy();
       if(StringUtil.isNotEmpty(orderBy))
       {
           //防止sql注入
           String orderBySql=SQLFilter.sqlInject(orderBy);
           pl.setOrderBy(orderBySql);
       }
        return pl;
    }
    public static <T>PagedList<T> toPageList(PageInfo<T> spage)
    {
        PagedList<T> pagedList = new PagedList<T>();
        pagedList.setPageSize((int) spage.getPageSize());
        pagedList.setPageNum((int) spage.getPageNum());
        pagedList.setRecordCount((int) spage.getTotal());
        pagedList.setData(spage.getList());
        pagedList.setPageCount((int) spage.getPages());
        return pagedList;
    }
}

範例程式碼

  @PostMapping("getPageList")
    public Result getPageList(@RequestBody PageParam<TUser> pageParm)
    {
       //接收引數
        PagedList<TUser> pl =PageUtils.exportPagedList(pageParm);
        return Result.success(userService.queryPageList(pl, pageParm.getParam()));
    }
  public PagedList<TUser> queryPageList(PagedList<TUser> page,TUser user)
    {
       PageInfo<TUser> pageInfo= PageHelper.startPage(page).doSelectPageInfo(()-> list(user));
       //轉換結果
       return PageUtils.toPageList(pageInfo);
    }

前段傳入引數

{
    "pageSize":10,
    "pageNum":"1",
    //查詢條件
     "param":{
         "name":"張三210001"
    },
    //排序欄位
    "orderBy":"age desc"
}

執行結果

2022-04-15 22:26:39.914 [http-nio-9090-exec-9] DEBUG [613920d89eb54bfd8601c93ec8572dcf] c.s.f.m.UserMapper.queryPageList - ==>  Preparing: SELECT * FROM t_user u LEFT JOIN t_user_role ur ON ur.userOid = u.oid WHERE name = ? order by age desc LIMIT ? 
2022-04-15 22:26:39.919 [http-nio-9090-exec-9] DEBUG [613920d89eb54bfd8601c93ec8572dcf] c.s.f.m.UserMapper.queryPageList - ==> Parameters: 張三210001(String), 10(Integer)
2022-04-15 22:26:40.267 [http-nio-9090-exec-9] DEBUG [613920d89eb54bfd8601c93ec8572dcf] c.s.f.m.UserMapper.queryPageList - <==      Total: 1

基礎的分頁查詢已經發完成了,下面解答上面的問題的方法

二、分頁中的排序欄位如何防止SQL隱碼攻擊問題

對於前段傳入的排序欄位,我們需要進行SQL過濾處理,關於這個問題其實在上述的分頁封裝類中已經進行了解決

範例程式碼:

public class SQLFilter
{
    public static String sqlInject(String str)
    {
        if (StringUtil.isBlank(str))
        {
            return null;
        }
        // 去掉'|"|;|字元
        str = StringUtil.replace(str, "'", "");
        str = StringUtil.replace(str, """, "");
        str = StringUtil.replace(str, ";", "");
        str = StringUtil.replace(str, "\", "");
        // 轉換成小寫
        str = str.toLowerCase();
        // 非法字元
        String[] keywords = { "master", "truncate", "insert", "select",
                "delete", "update", "declare", "alert", "drop" };
        // 判斷是否包含非法字元
        for (String keyword : keywords)
        {
            if (str.indexOf(keyword) != -1)
            {
                throw new SysException("包含非法字元");
            }
        }
        return str;
    }
}

三、複雜的SQL分頁語句

複雜的SQL分頁語句,需要自定義SQL的count語句如何實現呢?

PageHelper實現分頁,預設是查詢自定義的count語句是否存在,如果存在就用自定義的語句,否則就在外層包裝查詢的語句,而自定義count語句只需要在在查詢語句名稱後面新增_COUNT即可。例如

查詢集合的語句名稱為queryPageList,那麼查詢count的語句為queryPageList_COUNT,返回Long型別即可。

<select id="queryPageList_COUNT" resultType="java.lang.Long">
	 select count(1) from t_user  u 
	 left join t_user_role ur on ur.userOid=u.oid
	 <where>
	  <if test="name != null">name=#{name}</if>
	 </where>
</select>

四、分頁失效的常見的場景有哪些?

1.pageHelper分頁查詢有個特殊的要求,查詢下sql語句一定要緊跟在分頁查詢的後面,否則分頁查詢會失效。之前採用的如下寫法容易失效,建議採用java8的寫法

  PageHelper.startPage(pagedList.getPageNum(),pagedList.getPageSize());
        //緊跟分頁查詢後面
        List<TUser> list = list(user);
        PageInfo<TUser> pageInfo =new PageInfo<>(list);
        return PageUtils.toPageList(pageInfo);

2.注意pagehelper的reasonable 預設為false,遇到查詢頁數大於總頁數時,出現分頁失敗

pagehelper的reasonable 預設為false,遇到查詢頁數大於總頁數時,查詢為空;當reasonable設定為true時,遇到查詢頁數大於總頁數時,查詢最後一頁資料;

3.PageHelper先開啟分頁,後對list資料操作將會導致分頁錯誤

範例程式碼:

    public PageInfo<TUserVO> getUserPageList(int pageNum, int pageSize) {
        PageHelper.startPage(pageNum,pageSize);
        List<TUserVO> tUserVOsByView = userMapper.getUserList();
        List<TUserVO> TUserVOs = new ArrayList<>();
        for (TUserVO TUserVO : tUserVOsByView) {
            TUserVO TUserVOSingle = new TUserVO();
            TUserVOSingle.setHdId(TUserVO.getHdId());
            TUserVOs.add(TUserVOSingle);
        }
        PageInfo<TUserVO> pageViewInfo = new PageInfo<>(TUserVOs);
        return pageViewInfo;
    }

4.PageHelper先對list資料操作,後開啟分頁,將會導致分頁失效

範例程式碼:

    public PageInfo<TUserVO> getUserPageList(int pageNum, int pageSize) {       
        List<TUserVO> tUserVOsByView = userMapper.getUserList();
        List<TUserVO> TUserVOs = new ArrayList<>();
        for (TUserVO TUserVO : tUserVOsByView) {
            TUserVO TUserVOSingle = new TUserVO();
            TUserVOSingle.setHdId(TUserVO.getHdId());
        }
        PageHelper.startPage(pageNo,pageSize);
        PageInfo<TUserVO> pageViewInfo = new PageInfo<>(TUserVOs);
        return pageViewInfo;
    }

大家需要注意下,抽時間可以去驗證下結果。

五、大表資料PageHelper分頁效能如何

PageHelper 對於大表查詢資料量越大,效能越差,這是因為PageHelper分頁是自動在sql語句後面拼接limit沒有進行相關的優化,一旦資料大,效能就比較慢。

例如:

優化前SQL語句:

SELECT d.* FROM tag_detail d LIMIT 10000000,10 

查詢的時間大概需要10秒左右,執行速度比較慢。

優化後SQL語句:

SELECT d.* FROM tag_detail d
INNER JOIN 
    (SELECT oid FROM tag_detail LIMIT 10000000,10) t
ON d.oid= t.oid;

子查詢先通過分頁查詢主鍵欄位,然後進行關聯查詢,經過優化後,查詢時間大概為1秒左右。效能大幅度提升。

總結:

本文講解了PageHelper的基本的使用和相關的問題,這些都是我從實際的專案中總結出來的問題以及相關的解決方案,大家在使用的時候要特別注意,不要放同樣的錯誤。

到此這篇關於Spring Boot 整合PageHelper的使用方法的文章就介紹到這了,更多相關Spring Boot 整合PageHelper內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com