• Mybatis框架插件PageHelper的使用


    在web开发过程中涉及到表格时,例如dataTable,就会产生分页的需求,通常我们将分页方式分为两种:前端分页和后端分页。

    前端分页

    一次性请求数据表格中的所有记录(ajax),然后在前端缓存并且计算count和分页逻辑,一般前端组件(例如dataTable)会提供分页动作。

    特点是:简单,很适合小规模的web平台;当数据量大的时候会产生性能问题,在查询和网络传输的时间会很长。

    后端分页

    在ajax请求中指定页码(pageNum)和每页的大小(pageSize),后端查询出当页的数据返回,前端只负责渲染。

    特点是:复杂一些;性能瓶颈在MySQL的查询性能,这个当然可以调优解决。一般来说,web开发使用的是这种方式。

    我们说的也是后端分页。

    • MySQL对分页的支持

    简单来说MySQL对分页的支持是通过limit子句。请看下面的例子。

    1 limit关键字的用法是
    2 LIMIT [offset,] rows
    3 offset是相对于首行的偏移量(首行是0),rows是返回条数。
    4 
    5 # 每页10条记录,取第一页,返回的是前10条记录
    6 select * from tableA limit 0,10;
    7 # 每页10条记录,取第二页,返回的是第11条记录,到第20条记录,
    8 select * from tableA limit 10,10;

    这里提一嘴的是,MySQL在处理分页的时候是这样的:

    limit 1000,10 - 过滤出1010条数据,然后丢弃前1000条,保留10条。当偏移量大的时候,性能会有所下降。

    limit 100000,10 - 会过滤10w+10条数据,然后丢弃前10w条。如果在分页中发现了性能问题,可以根据这个思路调优。

    • Mybatis分页插件PageHelper

    在使用Java Spring开发的时候,Mybatis算是对数据库操作的利器了。不过在处理分页的时候,Mybatis并没有什么特别的方法,一般需要自己去写limit子句实现,成本较高。好在有个PageHelper插件:

    1、添加maven依赖

    1 <dependency>
    2     <groupId>com.github.pagehelper</groupId>
    3     <artifactId>pagehelper</artifactId>
    4     <version>5.0.0</version>
    5 </dependency>

    2、Mybatis对PageHelper进行配置(在Mybatis的配置文件中,如 mybatis-config.xml 或 sqlMapConfig.xml)

     1     <!-- 配置分页插件 -->
     2     <plugins>
     3           <!-- com.github.pagehelper为PageHelper类所在包名 -->
     4           <plugin interceptor="com.github.pagehelper.PageHelper">
     5               <!-- 设置数据库类型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六种数据库-->        
     6             <property name="dialect" value="mysql"/>
     7             <!-- 设置为true时,使用RowBounds分页会进行count查询 会去查询出总数 -->
     8             <property name="rowBoundsWithCount" value="true"/>
     9           </plugin>
    10     </plugins>

    3、使用插件进行分页,mapper层不用进行修改,在service层进行操作

     1     /**
     2      * 获取用户集合
     3      * @param user   封装好条件的user类
     4      * @param pageNo    第几页
     5      * @param pageSize   每页显示的长度
     6      * @return
     7      */
     8     @Override
     9     public PageInfo<SysUser> getSysUserList(SysUser user, int pageNo, int pageSize) {
    10         PageHelper.startPage(pageNo,pageSize);
    11         List<SysUser> userList = sysUserMapper.getSysUserList(user);
    12         PageInfo<SysUser> pageinfo = new PageInfo<SysUser>(userList);
    13         return pageinfo;
    14     }

    分页插件就是这样使用,PageInfo 是引自com.github.pagehelper.PageInfo; 插件对mybatis执行流程进行了增强,添加了limit以及count查询,属于物理分页。

    说明:

    1、PageHelper的优点是,分页和Mapper.xml完全解耦。实现方式是以插件的形式,对Mybatis执行的流程进行了强化,添加了总数count和limit查询。属于物理分页。

    2、Page page = PageHelper.startPage(pageNum, pageSize, true); - true表示需要统计总数,这样会多进行一次请求select count(0); 省略掉true参数只返回分页数据。 

    1)统计总数,(将SQL语句变为 select count(0) from xxx,只对简单SQL语句其效果,复杂SQL语句需要自己写)

        Page<?> page = PageHelper.startPage(1,-1);

        long count = page.getTotal();

    2)分页,pageNum - 第N页, pageSize - 每页M条数

        A、只分页不统计(每次只执行分页语句)

        PageHelper.startPage([pageNum],[pageSize]);

        List<?> pagelist = queryForList( xxx.class, "queryAll" , param);

        //pagelist就是分页之后的结果

        B、分页并统计(每次执行2条语句,一条select count语句,一条分页语句)适用于查询分页时数据发生变动,需要将实时的变动信息反映到分页结果上

        Page<?> page = PageHelper.startPage([pageNum],[pageSize],[iscount]);

        List<?> pagelist = queryForList( xxx.class , "queryAll" , param);

        long count = page.getTotal();

        //也可以 List<?> pagelist = page.getList();  获取分页后的结果集

    3)使用PageHelper查全部(不分页)

        PageHelper.startPage(1,0);

        List<?> alllist = queryForList( xxx.class , "queryAll" , param);

    4)PageHelper的其他API

        String orderBy = PageHelper.getOrderBy();    //获取orderBy语句

        Page<?> page = PageHelper.startPage(Object params);

        Page<?> page = PageHelper.startPage(int pageNum, int pageSize);

        Page<?> page = PageHelper.startPage(int pageNum, int pageSize, boolean isCount);

        Page<?> page = PageHelper.startPage(pageNum, pageSize, orderBy);

        Page<?> page = PageHelper.startPage(pageNum, pageSize, isCount, isReasonable);    //isReasonable分页合理化,null时用默认配置

        Page<?> page = PageHelper.startPage(pageNum, pageSize, isCount, isReasonable, isPageSizeZero);    //isPageSizeZero是否支持PageSize为0,true且pageSize=0时返回全部结果,false时分页,null时用默认配置

    5)、默认值

        //RowBounds参数offset作为PageNum使用 - 默认不使用

        private boolean offsetAsPageNum = false;

        //RowBounds是否进行count查询 - 默认不查询

        private boolean rowBoundsWithCount = false;

        //当设置为true的时候,如果pagesize设置为0(或RowBounds的limit=0),就不执行分页,返回全部结果

        private boolean pageSizeZero = false;

        //分页合理化

        private boolean reasonable = false;

        //是否支持接口参数来传递分页参数,默认false

        private boolean supportMethodsArguments = false;  

    3、有一个安全性问题,需要注意一下,不然可能导致分页错乱。我这里直接粘贴了这篇博客里的一段话。

     1 PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。
     2 
     3 只要你可以保证在 PageHelper 方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelper 在 finally 代码段中自动清除了 ThreadLocal 存储的对象。
     4 
     5 如果代码在进入 Executor 前发生异常,就会导致线程不可用,这属于人为的 Bug(例如接口方法和 XML 中的不匹配,导致找不到 MappedStatement 时), 这种情况由于线程不可用,也不会导致 ThreadLocal 参数被错误的使用。
     6 
     7 但是如果你写出下面这样的代码,就是不安全的用法:
     8 
     9 PageHelper.startPage(1, 10);
    10 List<Country> list;
    11 if(param1 != null){
    12     list = countryMapper.selectIf(param1);
    13 } else {
    14     list = new ArrayList<Country>();
    15 }
    16 这种情况下由于 param1 存在 null 的情况,就会导致 PageHelper 生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。
    17 
    18 上面这个代码,应该写成下面这个样子:
    19 
    20 List<Country> list;
    21 if(param1 != null){
    22     PageHelper.startPage(1, 10);
    23     list = countryMapper.selectIf(param1);
    24 } else {
    25     list = new ArrayList<Country>();
    26 }
    27 这种写法就能保证安全。
    28 
    29 如果你对此不放心,你可以手动清理 ThreadLocal 存储的分页参数,可以像下面这样使用:
    30 
    31 List<Country> list;
    32 if(param1 != null){
    33     PageHelper.startPage(1, 10);
    34     try{
    35         list = countryMapper.selectAll();
    36     } finally {
    37         PageHelper.clearPage();
    38     }
    39 } else {
    40     list = new ArrayList<Country>();
    41 }
    42 这么写很不好看,而且没有必要。

    4、最后附上PageInfo类的详细属性供参考:

     1  //当前页
     2     private int pageNum;
     3     //每页的数量
     4     private int pageSize;
     5     //当前页的数量
     6     private int size;
     7     //排序
     8     private String orderBy;
     9 
    10     //由于startRow和endRow不常用,这里说个具体的用法
    11     //可以在页面中"显示startRow到endRow 共size条数据"
    12 
    13     //当前页面第一个元素在数据库中的行号
    14     private int startRow;
    15     //当前页面最后一个元素在数据库中的行号
    16     private int endRow;
    17     //总记录数
    18     private long total;
    19     //总页数
    20     private int pages;
    21     //结果集
    22     private List<T> list;
    23 
    24     //第一页
    25     private int firstPage;
    26     //前一页
    27     private int prePage;
    28     //下一页
    29     private int nextPage;
    30     //最后一页
    31     private int lastPage;
    32 
    33     //是否为第一页
    34     private boolean isFirstPage = false;
    35     //是否为最后一页
    36     private boolean isLastPage = false;
    37     //是否有前一页
    38     private boolean hasPreviousPage = false;
    39     //是否有下一页
    40     private boolean hasNextPage = false;
    41     //导航页码数
    42     private int navigatePages;
    43     //所有导航页号
    44     private int[] navigatepageNums;

    使用PageInfo这个类,你需要将查询出来的list放进去:

        PageInfo<CityList> p=new PageInfo<CityList>(list);
    然后将数据返回   mv.addObject("page", p);
    这样在页面中就可以通过${page.nextPage}翻到下一页,
    ${page.prePage}翻到上一页, 

  • 相关阅读:
    Hive是什么
    Hive导入数据的四种方法
    grafana安装
    ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)
    linux下mysql安装报错及修改密码登录等等
    chown -R 用户名:组名 ./ 及 chown用法介绍
    Hadoop提交作业流程
    Java中常见的几种异常
    mysql索引需要了解的几个注意
    js数组依据下标删除元素
  • 原文地址:https://www.cnblogs.com/haw2106/p/9956320.html
Copyright © 2020-2023  润新知