Skip to content

13.pageHelper 分页查询

1.环境搭建

跟动态 SQL 的环境一样

1.1 SQL

shell
DROP TABLE IF EXISTS `blog`;
CREATE TABLE `blog` (
  `id` varchar(50) NOT NULL COMMENT '博客id',
  `title` varchar(100) NOT NULL COMMENT '博客标题',
  `author` varchar(30) NOT NULL COMMENT '博客作者',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `views` int(30) NOT NULL COMMENT '浏览量',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `blog` VALUES ('0001', 'hong/My-Note', '谢禹宏', '2021-04-21 00:00:00', '100');
INSERT INTO `blog` VALUES ('0002', 'MyBatis-Plus', '苞米豆', '2021-04-21 00:00:00', '100');
INSERT INTO `blog` VALUES ('0003', 'Hello MyBatis', '母鸡', '2021-04-21 00:00:00', '120');
INSERT INTO `blog` VALUES ('0004', 'Hello Vuew', '尤雨溪', '2021-01-21 00:00:00', '100');
INSERT INTO `blog` VALUES ('0005', 'Hello Linux', '林纳斯', '2001-04-21 00:00:00', '120');

1.2 Maven

xml
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>

1.3 实体类

java
package org.hong.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.sql.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date create_time;
    private Integer views;
}

1.4 MyBatisUtil 工具类

java
package org.hong.util;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MyBatisUtil {

    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            // 获取sqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
    // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
    public static SqlSession getSqlSession(){
        // openSession(): 此方式打开SQL会话, 事务是开启状态
        // openSession(true): 此方式打开SQL会话, 事务是关闭状态
        return sqlSessionFactory.openSession();
    }

    public static SqlSessionFactory getSqlSessionFactory() {
        return sqlSessionFactory;
    }
}

1.5 jdbc.properties

shell
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?userSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8
username=root
password=1234

1.6 log4j.properties

shell
log4j.rootLogger=DEBUG,A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n

1.7 MyBatis 核心配置文件

xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!-- configuration: 核心配置文件 -->
<configuration>

    <!-- 导入外部配置文件, 放在最前面 -->
    <properties resource="jdbc.properties"/>

     <settings>
        <!-- 设置日志输出, 方便观察sql语句和参数 -->
        <setting name="logImpl" value="LOG4J"/>
        <!-- 开启驼峰命名法 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <!--
        environments配置项目的运行环境, 可以配置多个
        default: 启用的环境
     -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!-- 数据库连接信息 -->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 每一个Mapper.xml都需要在MyBatis核心配置文件中注册!!! -->
    <mappers>
        <package name="org.hong.mapper"/>
    </mappers>

</configuration>

1.8 BlogMapper.java

java
package org.hong.mapper;

public interface BlogMapper {

}

1.9 BlogMapper.xml

xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.hong.mapper.BlogMapper">

</mapper>

2.配置拦截器插件

MyBatis 核心配置文件

xml
<plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

Spring 配置文件 ( SSM 整合后的配置 )

xml
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <!-- 注意其他配置 -->
  <property name="plugins">
    <array>
      <bean class="com.github.pagehelper.PageInterceptor">
        <property name="properties"></property>
      </bean>
    </array>
  </property>
</bean>

3.接口方法

java
@Select("select * from blog")
List<Blog> getByPage();

4.测试用例

java
package org.hong.test;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.apache.ibatis.session.SqlSession;
import org.hong.mapper.BlogMapper;
import org.hong.pojo.Blog;
import org.hong.util.MyBatisUtil;
import org.junit.Test;

import java.util.List;

public class PageHelperTest {
    @Test
    public void testHello(){
        SqlSession sqlSession = MyBatisUtil.getSqlSession();

        // 调用PageHelper类的startPage方法指定查询页数和每页的数量, 会返回一个page对象
        /**
         * 原理: 调用startPage方法后, PageHelper会在当前线程中绑定一个page对象, 跟返回的对象是同一个对象,
         *       然后在MyBatis执行我们的查询时, 会被PageHelper拦截, 并拿出与当前线程绑定的page对象,
         *       并修改MyBatis即将执行的sql语句, 为sql语句添加上分页的功能。
         *       为什么PageHelper会拦截MyBatis的执行, 这是MyBatis的插件机制的问题, 想要了解自己去百度。
         */
        Page<Object> page = PageHelper.startPage(2, 2);

        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        List<Blog> blogs = mapper.getByPage();
        blogs.forEach(System.out :: println);

        // page对象封装了查询信息
        System.out.println("当前页码:" + page.getPageNum());
        System.out.println("总记录数:" + page.getTotal());
        System.out.println("每页的记录数:" + page.getPageSize());
        System.out.println("总页码:" + page.getPages());

        sqlSession.close();
    }
}

5.控制台打印

shell
## PageHelper首先会查询表中的总记录数
[main] [org.hong.mapper.BlogMapper.getByPage_COUNT]-[DEBUG] ==>  Preparing: SELECT count(0) FROM blog
[main] [org.hong.mapper.BlogMapper.getByPage_COUNT]-[DEBUG] ==> Parameters:
[main] [org.hong.mapper.BlogMapper.getByPage_COUNT]-[DEBUG] <==      Total: 1
## 接着MyBatis会发送被PageHelper改造过的sql语句, 进行分页查询
[main] [org.hong.mapper.BlogMapper.getByPage]-[DEBUG] ==>  Preparing: select * from blog LIMIT ?, ?
[main] [org.hong.mapper.BlogMapper.getByPage]-[DEBUG] ==> Parameters: 2(Long), 2(Integer)
[main] [org.hong.mapper.BlogMapper.getByPage]-[DEBUG] <==      Total: 2
Blog(id=0003, title=Hello MyBatis, author=母鸡, createTime=2021-04-21, views=120)
Blog(id=0004, title=Hello Vuew, author=尤雨溪, createTime=2021-01-21, views=100)
## page对象封装的信息
当前页码:2
总记录数:5
每页的记录数:2
总页码:3

6.常见的使用方式

更多使用方式去看官网,知道这几种就够用了。

方式一与方式二的环境跟上面一样,只有测试用例不一样

6.1 方式一 ( 推荐使用 )

java
@Test
public void testPage1(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();

    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    // startPage(起始页数, 每页的数量)
    Page<Object> page = PageHelper.startPage(2, 2);
    List<Blog> blogs = mapper.getByPage();
    blogs.forEach(System.out :: println);

    sqlSession.close();
}

控制台打印

shell
[main] [org.hong.mapper.BlogMapper.getByPage_COUNT]-[DEBUG] ==>  Preparing: SELECT count(0) FROM blog
[main] [org.hong.mapper.BlogMapper.getByPage_COUNT]-[DEBUG] ==> Parameters:
[main] [org.hong.mapper.BlogMapper.getByPage_COUNT]-[DEBUG] <==      Total: 1
[main] [org.hong.mapper.BlogMapper.getByPage]-[DEBUG] ==>  Preparing: select * from blog LIMIT ?, ?
[main] [org.hong.mapper.BlogMapper.getByPage]-[DEBUG] ==> Parameters: 2(Long), 2(Integer)
[main] [org.hong.mapper.BlogMapper.getByPage]-[DEBUG] <==      Total: 2
Blog(id=0003, title=Hello MyBatis, author=母鸡, createTime=2021-04-21, views=120)
Blog(id=0004, title=Hello Vuew, author=尤雨溪, createTime=2021-01-21, views=100)

6.2 方式二 ( 推荐使用 )

java
@Test
public void testPage2(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();

    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    // offsetPage(起始索引, 查询条数)
    Page<Object> page = PageHelper.offsetPage(1, 3);
    List<Blog> blogs = mapper.getByPage();
    blogs.forEach(System.out :: println);

    sqlSession.close();
}

控制台打印

shell
[main] [org.hong.mapper.BlogMapper.getByPage_COUNT]-[DEBUG] ==>  Preparing: SELECT count(0) FROM blog
[main] [org.hong.mapper.BlogMapper.getByPage_COUNT]-[DEBUG] ==> Parameters:
[main] [org.hong.mapper.BlogMapper.getByPage_COUNT]-[DEBUG] <==      Total: 1
[main] [org.hong.mapper.BlogMapper.getByPage]-[DEBUG] ==>  Preparing: select * from blog LIMIT ?, ?
[main] [org.hong.mapper.BlogMapper.getByPage]-[DEBUG] ==> Parameters: 1(Long), 3(Integer)
[main] [org.hong.mapper.BlogMapper.getByPage]-[DEBUG] <==      Total: 3
Blog(id=0002, title=MyBatis-Plus, author=苞米豆, createTime=2021-04-21, views=100)
Blog(id=0003, title=Hello MyBatis, author=母鸡, createTime=2021-04-21, views=120)
Blog(id=0004, title=Hello Vuew, author=尤雨溪, createTime=2021-01-21, views=100)

6.3 方式三

配置 PageHelper 插件时配置**supportMethodsArguments**属性为**true**

xml
<plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <property name="supportMethodsArguments" value="true"/>
    </plugin>
</plugins>

6.3.1 接口方法

java
// 我们不需要处理pageNum和pageSize
@Select("select * from blog")
List<Blog> getPageMethod(@Param("pageNum")Integer pageNum, // 起始页数, 必须使用@Param注解指定key为pageNum
                         @Param("pageSize")Integer pageSize); // 每页数量, 必须使用@Param注解指定key为pageSize

6.3.2 测试用例

java
@Test
public void testPage3(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();

    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    /**
    * 注意:
    *  1.如果传入的是0或者负数, PageHelper依旧会进行分页
    *  2.如果传入的两个参数中任意一个参数为null, PageHelper将不会进行分页
    *  3.根据这个特性, 建议在声明接口方法时不要使用int, 而是使用Integer
    */
    List<Blog> blogs = mapper.getPageMethod(1, 2);
    blogs.forEach(System.out :: println);

    sqlSession.close();
}

6.3.3 控制台打印

shell
[main] [org.hong.mapper.BlogMapper.getPageMethod_COUNT]-[DEBUG] ==>  Preparing: SELECT count(0) FROM blog
[main] [org.hong.mapper.BlogMapper.getPageMethod_COUNT]-[DEBUG] ==> Parameters:
[main] [org.hong.mapper.BlogMapper.getPageMethod_COUNT]-[DEBUG] <==      Total: 1
[main] [org.hong.mapper.BlogMapper.getPageMethod]-[DEBUG] ==>  Preparing: select * from blog LIMIT ?
[main] [org.hong.mapper.BlogMapper.getPageMethod]-[DEBUG] ==> Parameters: 2(Integer)
[main] [org.hong.mapper.BlogMapper.getPageMethod]-[DEBUG] <==      Total: 2
Blog(id=0001, title=hong/My-Note, author=谢禹宏, createTime=2020-01-01, views=100)
Blog(id=0002, title=MyBatis-Plus, author=苞米豆, createTime=2021-04-21, views=100)

6.4 方式四

在实体类中声明 pageNumpageSize 属性。不建议使用,侵入性太强 了。

6.4.1 实体类

java
package org.hong.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.sql.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;
    private Integer views;

    // 当实体对象中的pageNum!= null && pageSize!= null时, 会自动分页
    private Integer pageNum;
    private Integer pageSize;
}

6.4.2 接口方法

java
@Select("select * from blog")
List<Blog> getPojo(Blog blog);

6.4.3 测试用例

java
@Test
public void testPage4(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();

    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Blog blog = new Blog();
    blog.setPageNum(2);
    blog.setPageSize(2);
    List<Blog> pojo = mapper.getPojo(blog);
    pojo.forEach(System.out :: println);

    sqlSession.close();
}

6.4.4 控制台打印

shell
[main] [org.hong.mapper.BlogMapper.getPojo_COUNT]-[DEBUG] ==>  Preparing: SELECT count(0) FROM blog
[main] [org.hong.mapper.BlogMapper.getPojo_COUNT]-[DEBUG] ==> Parameters:
[main] [org.hong.mapper.BlogMapper.getPojo_COUNT]-[DEBUG] <==      Total: 1
[main] [org.hong.mapper.BlogMapper.getPojo]-[DEBUG] ==>  Preparing: select * from blog LIMIT ?, ?
[main] [org.hong.mapper.BlogMapper.getPojo]-[DEBUG] ==> Parameters: 2(Long), 2(Integer)
[main] [org.hong.mapper.BlogMapper.getPojo]-[DEBUG] <==      Total: 2
Blog(id=0003, title=Hello MyBatis, author=母鸡, createTime=2021-04-21, views=120, pageNum=null, pageSize=null)
Blog(id=0004, title=Hello Vuew, author=尤雨溪, createTime=2021-01-21, views=100, pageNum=null, pageSize=null)

7.常用的属性

  • pageSizeZero:默认值为 false,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。
  • **reasonable**:分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。
  • supportMethodsArguments:支持通过 Mapper 接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。

Released under the MIT License.