zoukankan      html  css  js  c++  java
  • 基于Mybatis分页插件PageHelper

    基于Mybatis分页插件PageHelper

    1.分页插件使用

    1POM依赖

    PageHelper的依赖如下。需要新的版本可以去maven上自行选择

    <!-- PageHelper 插件分页 -->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>4.0.1</version>
    </dependency>

    2MybatisPageHelper的配置

    打开Mybatis配置文件,一般在Resource路径下mybatis-config.xml

    <configuration>
       <settings>
            <!-- 设置启用数据库字段下划线映射到java对象的驼峰式命名属性,默认为false -->
              <setting name="mapUnderscoreToCamelCase" value="true" />
         </settings>
         <objectWrapperFactory type="com.maomao.xwz.mybatis.handle.MapWrapperFactory"/>
        <plugins>
            <!-- com.github.pagehelper为PageHelper类所在包名 -->
            <plugin interceptor="com.github.pagehelper.PageHelper">
                <!-- 4.0.0以后版本可以不设置该参数 -->
                <property name="dialect" value="mysql"/>
                <!-- 该参数默认为false -->
                <!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 -->
                <!-- 和startPage中的pageNum效果一样-->
                <property name="offsetAsPageNum" value="true"/>
                <!-- 该参数默认为false -->
                <!-- 设置为true时,使用RowBounds分页会进行count查询 -->
                <property name="rowBoundsWithCount" value="true"/>
                <!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 -->
                <!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型)-->
                <property name="pageSizeZero" value="true"/>
                <!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
                <!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
                <!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
                <property name="reasonable" value="false"/>
                <!-- 3.5.0版本可用 - 为了支持startPage(Object params)方法 -->
                <!-- 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 -->
                <!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,orderBy,不配置映射的用默认值 -->
                <!-- 不理解该含义的前提下,不要随便复制该配置 -->
                <property name="params" value="pageNum=start;pageSize=limit;"/>
                <!-- 支持通过Mapper接口参数来传递分页参数 -->
                <property name="supportMethodsArguments" value="true"/>
                <!-- always总是返回PageInfo类型,check检查返回类型是否为PageInfo,none返回Page -->
                <property name="returnPageInfo" value="check"/>
            </plugin>
        </plugins>
    </configuration>

    3PageHelper分页使用

    1)只统计查询总数量count

    public PageInfo selectByExample() {
        PageHelper.startPage(1, -1);
        List list = this.baseMapper.selectByExample(new BsFeedbackExample());
        PageInfo pageInfo = new PageInfo(list);
        log.info(JSONObject.toJSONString(pageInfo));
        return pageInfo;
    }

    执行sql日志:

    2)正常分页

    public PageInfo selectByExample1() {
        PageHelper.startPage(1, 1);
        List list = this.baseMapper.selectByExample(new BsFeedbackExample());
        PageInfo pageInfo = new PageInfo(list);
        log.info(JSONObject.toJSONString(pageInfo));
        return pageInfo;
    }

    执行sql日志:

    3)分页统计总数

    public PageInfo selectByExample2() {
        PageHelper.startPage(1, 1, true);
        List list = this.baseMapper.selectByExample(new BsFeedbackExample());
        PageInfo pageInfo = new PageInfo(list);
        log.info(JSONObject.toJSONString(pageInfo));
        return pageInfo;
    }

    执行sql日志:

    4) 分页不统计总数

    public PageInfo selectByExample3() {
        PageHelper.startPage(1, 1, false);
        List list = this.baseMapper.selectByExample(new BsFeedbackExample());
        PageInfo pageInfo = new PageInfo(list);
        log.info(JSONObject.toJSONString(pageInfo));
        return pageInfo;
    }

    执行sql日志:

    5) 查询全部数据

    public PageInfo selectByExample3() {
        PageHelper.startPage(1, 0);
        List list = this.baseMapper.selectByExample(new BsFeedbackExample());
        PageInfo pageInfo = new PageInfo(list);
        log.info(JSONObject.toJSONString(pageInfo));
        return pageInfo;
    }

    执行sql日志:

    6) 分页排序orderBy

    public PageInfo selectByExample5() {
        String orderBy = "id desc";
        PageHelper.startPage(1, 2, orderBy);
        List list = this.baseMapper.selectByExample(new BsFeedbackExample());
        PageInfo pageInfo = new PageInfo(list);
        log.info(JSONObject.toJSONString(pageInfo));
        return pageInfo;
    }

    执行sql日志:

    7) 直接传入分页参数RowBounds

    public PageInfo selectByExample6() {
        List list = this.baseMapper.selectByExample(new BsFeedbackExample(), new RowBounds(2, 1));
        PageInfo pageInfo = new PageInfo(list);
        log.info(JSONObject.toJSONString(pageInfo));
        return pageInfo;
    }

    执行sql日志:

    4PageHelper如何确保安全性 

    PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。

    只要你可以保证在 PageHelper 方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelper  finally 代码段中自动清除了 ThreadLocal 存储的对象。

    如果代码在进入 Executor 前发生异常,就会导致线程不可用,这属于人为的 Bug(例如接口方法和 XML 中的不匹配,导致找不到 MappedStatement 时), 这种情况由于线程不可用,也不会导致 ThreadLocal 参数被错误的使用。

    有一个安全性问题,需要注意一下,不然可能导致分页错乱。

    不安全的用法:

    PageHelper.startPage(1, 10);

    List<Country> list;

    if(param1 != null){

        list = countryMapper.selectIf(param1);

    } else {

        list = new ArrayList<Country>();

    }

    这种情况下由于 param1 存在 null 的情况,就会导致 PageHelper 生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。

    安全的用法:

    List<Country> list;

    if(param1 != null){

        PageHelper.startPage(1, 10);

        list = countryMapper.selectIf(param1);

    } else {

        list = new ArrayList<Country>();

    }

    2. PageHelper源码分析

    1、 线程安全

    PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。PageHelper.startPage方法调用后,将分页参数设置在ThreadLocal中。

    /**
     * 基础分页方法
     *
     * @author liuzh
     */
    public abstract class PageMethod {
        protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();
        protected static boolean DEFAULT_COUNT = true;
        /**
         * 开始分页
         *
         * @param pageNum      页码
         * @param pageSize     每页显示数量
         * @param count        是否进行count查询
         * @param reasonable   分页合理化,null时用默认配置
         * @param pageSizeZero true且pageSize=0时返回全部结果,false时分页,null时用默认配置
         */
        public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
            Page<E> page = new Page<E>(pageNum, pageSize, count);
            page.setReasonable(reasonable);
            page.setPageSizeZero(pageSizeZero);
            //当已经执行过orderBy的时候
            Page<E> oldPage = getLocalPage();
            if (oldPage != null && oldPage.isOrderByOnly()) {
                page.setOrderBy(oldPage.getOrderBy());
            }
            setLocalPage(page);
            return page;
        }

        /**
         * 开始分页
         *
         * @param offset 页码
         * @param limit  每页显示数量
         */
        public static <E> Page<E> offsetPage(int offset, int limit) {
            return offsetPage(offset, limit, DEFAULT_COUNT);
        }

        /**
         * 开始分页
         *
         * @param offset 页码
         * @param limit  每页显示数量
         * @param count  是否进行count查询
         */
        public static <E> Page<E> offsetPage(int offset, int limit, boolean count) {
            Page<E> page = new Page<E>(new int[]{offset, limit}, count);
            //当已经执行过orderBy的时候
            Page<E> oldPage = getLocalPage();
            if (oldPage != null && oldPage.isOrderByOnly()) {
                page.setOrderBy(oldPage.getOrderBy());
            }
            setLocalPage(page);
            return page;
        }

        /**
         * 排序
         *
         * @param orderBy
         */
        public static void orderBy(String orderBy) {
            Page<?> page = getLocalPage();
            if (page != null) {
                page.setOrderBy(orderBy);
            } else {
                page = new Page();
                page.setOrderBy(orderBy);
                page.setOrderByOnly(true);
                setLocalPage(page);
            }
        }

    2、Mybatis分页拦截

    定义了Mybatis拦截器,在Mybatis执行sql前,进行拦截,针对sql进行分页处理;
    /**
     * Mybatis - 通用分页拦截器<br/>
     * 项目地址 : http://git.oschina.net/free/Mybatis_PageHelper
     *
     * @author liuzh/abel533/isea533
     * @version 5.0.0
     */
            @SuppressWarnings({"rawtypes", "unchecked"})
            @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 {
                //缓存count查询的ms
                protected Cache<String, MappedStatement> msCountMap = null;
                private Dialect dialect;
                private String default_dialect_class = "com.github.pagehelper.PageHelper";
                private Field additionalParametersField;
                private String countSuffix = "_COUNT";
                @Override
                public Object intercept(Invocation invocation) throws Throwable {

    3、分页参数内置查询sql

    MySqlDialect中加载AbstractHelperDialect实现,从线程中取出分页参数,生成分页sql;

    经常提到的count查询,其实是PageHelper帮助我们生成的一个MappedStatement内存对象,它可以免去我们在XXXMapper.xml内单独声明一个sql count查询,我们只需要写一个sql分页业务查询即可。
    /**
     * 针对 PageHelper 的实现
     *
     * @author liuzh
     * @since 2016-12-04 14:32
     */
    public abstract class AbstractHelperDialect extends AbstractDialect implements Constant {

        /**
         * 获取分页参数
         *
         * @param <T>
         * @return
         */
        public <T> Page<T> getLocalPage() {
            return PageHelper.getLocalPage();
        }

    3.PageHelper使用建议

    1、明确指定dialect(mysql)

    2当业务sql存在内部嵌套时,明确编写sql分页业务和与它对应的count查询,别图省事。

    MyBatis工具 http://www.mybatis.tk

    推荐使用 Mybatis 通用 Mapper3 https://github.com/abel533/Mapper

    推荐使用 Mybatis 分页插件 PageHelper https://github.com/pagehelper/Mybatis-PageHelper

    推荐使用Mybatis介绍 http://www.mybatis.org/mybatis-3/zh/configuration.html#plugins

  • 相关阅读:
    Pytorch cpu版离线安装:win10 + Anaconda + Pip
    离线配置Opencv-python: Anaconda3 + Pycharm+ Win10 + Python3.6
    HSmartWindowControl 之 摄像头实时显示( 使用 WPF )
    OpencvSharp 在WPF的Image控件中显示图像
    HSmartWindowControl 之 显示图像
    HSmartWindowControl之安装篇 (Visual Studio 2013 & Halcon 18)
    使用ILMerge 打包C# 绿色免安装版程序
    C# 日志输出工具库—log4net 安装、配置及简单应用
    Python:windows下scikit-learn 安装和更新
    C# 开源仪表盘库—Agauge App
  • 原文地址:https://www.cnblogs.com/anye-15068156823/p/8889655.html
Copyright © 2011-2022 走看看