一、PageHelper框架的介绍:

PageHelper比自己用limit的好处在于:不需要自己计算目前需要从第几条开始。只需要传入要查询的数据页码就可以。

使用简单:PageHelper提供了非常简单易用的API,只需要在查询方法中调用PageHelper.startPage方法即可实现分页。

功能强大:PageHelper支持多种数据库,支持多种分页方式,支持自定义分页查询语句等。

性能优秀:PageHelper采用了预处理和缓存技术,可以大大提高分页查询的性能。

物理分页: 使用sql直接对数据进行分页处理。PageHelper也是物理分页插件。

逻辑分页: 数据全部查出来,然后再进行分页,这样再数据量大时,内容根本扛不住(除非系统比较小)。所以真正使用时都是使用PageHelper这种物理分页插件

实现原理是基于Mybatis的拦截器QueryInterceptor 来实现的,通过拦截sql查询,来对sql进行增强改造,其实这种思想比比皆是。比如MP的分页插件也是这种思想,比如各种组件里的Interceptor也都是这种思想。

二、使用要点:

2.1.引入jar:

官方文档推荐使用最新版jar,PageHelper最新版本:6.1.0

<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
  <groupId>com.github.pagehelper</groupId>
  <artifactId>pagehelper</artifactId>
  <version>5.3.3</version>
</dependency>

 

2.2.分页:

 注: 在项目中只需要引入依赖,然后用PageHelper.startPage(查询页码, 每页的数据条数)

就可以对后边的sql实现分页查询了(后边的sql就不是查询所有数据了,而是用PageHelper实现了给sql添加了limit的效果)

三、具体案例代码:

3.1.项目依赖:

pom.xml配置文件内容如下:

<properties>
  <java.version>1.8</java.version>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.build.outputEncoding>UTF-8</project.build.outputEncoding>
  <spring-boot.version>2.6.13</spring-boot.version>
</properties>




<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>${spring-boot.version}</version>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <version>${spring-boot.version}</version>
    <scope>provided</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <version>${spring-boot.version}</version>
    <scope>test</scope>
  </dependency>
  <!--热部署-->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
    <version>${spring-boot.version}</version>
  </dependency>
  <!--工具类-->
  <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.1</version>
  </dependency>
  <!-- alibaba的druid数据库连接池 -->
  <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.20</version>
  </dependency>
  <!-- alibaba的druid数据库连接池 -->
  <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.20</version>
  </dependency>
  <!--mybatis-plus(springboot版)-->
  <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.0</version>
    <exclusions>
      <exclusion>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
      </exclusion>
    </exclusions>
  </dependency>

  <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
  <!--mybatis-plus-boot-starter里包含的spring-jdbc版本是5.2.8,本项目中报缺少一个类,要替换为5.3.23-->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.23</version>
  </dependency>
  <!--引入Lombok依赖-->
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.32</version>
  </dependency>
  <!-- 添加MySQL JDBC依赖 -->
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.23</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper-spring-boot-autoconfigure -->
  <!--<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-autoconfigure</artifactId>
    <version>2.1.0</version>  -->   <!--  此版本2.1.0不知道是否和下边的5.3.3匹配 --><!--
  </dependency>-->
  <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
  <dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.3.3</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
 <!-- <dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.3.2</version>
  </dependency>-->
</dependencies>

<build>
  <finalName>a_pagehelper</finalName>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.8.1</version>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
      </configuration>
    </plugin>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <version>${spring-boot.version}</version>
      <configuration>
        <mainClass>com.zyq.pagehelper.AppMain</mainClass>
        <skip>true</skip>
      </configuration>
      <executions>
        <execution>
          <id>repackage</id>
          <goals>
            <goal>repackage</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
  <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
    <plugins>
      <plugin>
        <artifactId>maven-clean-plugin</artifactId>
        <version>3.1.0</version>
      </plugin>
 <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
      <plugin>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.0.2</version>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.0</version>
      </plugin>
      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.22.1</version>
      </plugin>
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.2.2</version>
      </plugin>
      <plugin>
        <artifactId>maven-install-plugin</artifactId>
        <version>2.5.2</version>
      </plugin>
      <plugin>
        <artifactId>maven-deploy-plugin</artifactId>
        <version>2.8.2</version>
      </plugin>
    </plugins>
  </pluginManagement>
</build>

 

 3.2.配置文件:

 

spring.datasource.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimeznotallow=Asia/Shanghai
#spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
mybatis.mapper-locations=classpath:mappers/*.xml

# pageHelper 配置(有默认值,可以不用配置)
pagehelper.helper-dialect=mysql
#启用合理化
pagehelper.reasonable=true
#配置参数映射
pagehelper.params=count=countSql
# 支持方法参数
pagehelper.support-methods-arguments=true

#pagehelper.reasonable: pageHelper使用时,数据库只有8行数据,pageNum=1&pageSize=10,
# pageNum=2&pageSize=10,pageNum=3&pageSize=10。。。返回的数据都是那8条。
#原因:这是pageHelper里面自带的一个功能,叫做reasonable分页参数合理化默认是false,3.3.0以上版本可用。
#启用合理化时(设置为true时),如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页;
#禁用合理化时(设置为false时),如果pageNum<1或pageNum>pages会返回空数据。
#解决:spring Boot项目里面:pagehelper.reasnotallow=false

#params :为了支持 startPage(object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取
#不配置映射的用默认值,默认值为值,
#可以配置pageNum,pagesize,count,pagesizeZero,reasonable

#supportmethodsArguments:支持通过 Mapper 接口参数来传递分页参数,默认值 false,分页插件会从查询方法的
#参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页(具体如何传值待以后研究)。

# 解决循环依赖的问题
spring.main.allow-circular-references=true
spring.datasource.name=test

## 配置连接池信息
## 初始化大小,最小,最大
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.initial-size=5
spring.datasource.druid.min-idle=5
spring.datasource.druid.max-active=30

## 配置获取连接等待超时的时间
spring.datasource.druid.max-wait=60000
## 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
#spring.datasource.druid.time-between-eviction-runs-millis=60000
## 配置一个连接在池中最小生存的时间,单位是毫秒
#spring.datasource.druid.min-evictable-idle-time-millis=300000
#spring.datasource.druid.validation-query=SELECT 1 FROM DUAL
#spring.datasource.druid.test-while-idle=true
#spring.datasource.druid.test-on-borrow=false
#spring.datasource.druid.test-on-return=false
#spring.datasource.druid.pool-prepared-statements=true
#spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20
## 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
#spring.datasource.druid.filters=stat,wall
## 通过connectProperties属性来打开mergeSql功能;慢SQL记录
#spring.datasource.druid.connection-properties=druid.stat.mergeSql=true
#spring.datasource.druid.filter.stat.slow-sql-millis=5000
## 超过时间限制是否回收
#spring.datasource.druid.remove-abandnotallow=true
## 超时时间;单位为秒。180秒=3分钟
#spring.datasource.druid.remove-abandoned-timeout-millis=180
## 关闭abanded连接时输出错误日志
#spring.datasource.druid.log-abandnotallow=true

# mybatis-plus 默认扫描mapper.xml的目录
mybatis-plus.mapper-locations=classpath*:/mappers/*.xml
#配置sql打印日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

# 全局的slf4j配置 (slf4j是lombok框架提供的log4j工具)
logging.level.root=WARN
## 局部的slf4j配置
#logging.level.com.zyq.mybatisPlusDemo=DEBUG
logging.level.com.zyq.AppMain=INFO
#slf4j的日志级别 ERROR>WARN>INFO>DEBUG>TRACE

 

3.3.User:

package com.zyq.pagehelper.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String userName;
    private String password;
    private Integer age;

    public User(Integer age) {
        this.age = age;
    }
}

 

3.4.Mapper接口:

package com.zyq.pagehelper.mapper;
import com.zyq.pagehelper.entity.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper//如果在启动类上添加了@MapperScan()则可以省略@Mapper
public interface UserMapper {
    //@Select("select * from users where age>#{age}")
    List<User> getUserList(User user);
}

 

3.5.UserMapper.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="com.zyq.pagehelper.mapper.UserMapper">

    <select id="getUserList" parameterType="com.zyq.pagehelper.entity.User" resultType="com.zyq.pagehelper.entity.User">
        select * from users where age>#{age}
    </select>
</mapper>

 

3.6.配置类:

package com.zyq.pagehelper.config;
import com.github.pagehelper.PageInterceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
import java.util.Properties;
@Configuration
public class PageHelperConfig {//这个配置类官方说不是必须的。网上有人说不配置时发现分页无法生效,

    //可能存在多个连接工厂,是允许这么注入的//验证了5.3.3 和 5.1.11 两个版本都是无法正常分页的,
    @Resource//所以还是加上这个配置最好
    private List<SqlSessionFactory> sqlSessionFactoryList;
    //@PostConstruct: Bean初始化之前执行
    //@PreDestroy: 和销毁之前执行
    @PostConstruct
    public void initConfig(){
        PageInterceptor pageInterceptor=new PageInterceptor();//1.pageInterceptor
        Properties properties=new Properties();//2.properties
        // 设置数据库方言为MySQL,以便数据库操作工具类能够正确解析MySQL的SQL语法和特性
        properties.setProperty("helperDialect","mysql");
        pageInterceptor.setProperties(properties);//3.pageInterceptor设置properties

        //4.sqlSessionFactory添加pageInterceptor
        sqlSessionFactoryList.forEach(sqlSessionFactory -> sqlSessionFactory.getConfiguration().addInterceptor(pageInterceptor));
        /*sqlSessionFactoryList.forEach(new Consumer<SqlSessionFactory>() {
            public void accept(SqlSessionFactory sqlSessionFactory) {
                sqlSessionFactory.getConfiguration().addInterceptor(pageInterceptor);
            }
        });
        sqlSessionFactoryList.forEach(sqlSessionFactory -> {sqlSessionFactory.getConfiguration().addInterceptor(pageInterceptor);});*/
    }
}

 

3.7.测试类:

package com.zyq.pagehelper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.zyq.pagehelper.entity.User;
import com.zyq.pagehelper.mapper.UserMapper;
import com.zyq.pagehelper.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class AppMainTest {
    @Autowired
    private UserMapper userMapper;
    //也可以将PageHelper以插件的形式放在userMapper之前(startPage方法)(就具有分页效果了)
    //如果不加PageHelper.startPage()就没分页效果
    //注意: 每个查询方法前添加PageHelper.startPage()才会有分页效果
    @Test
    public void test2() {
        PageHelper.startPage(2,5);
        List<User> userList=userMapper.getUserList(new User(0));
        userList.forEach(user -> System.out.println(user));
    }

}

3.8.UserService:

package com.zyq.pagehelper.service;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.zyq.pagehelper.entity.User;
import com.zyq.pagehelper.mapper.UserMapper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class UserService {
    @Resource
    UserMapper UserMapper;

    /**获取用户集合
     * @param user   封装好条件的user类
     * @param pageNo    第几页
     * @param pageSize   每页显示的长度
     * @return
     */
    public PageInfo<User> getUserList(User user, int pageNo, int pageSize){
        //设置分页信息: 查询的页码,每页的数据量//true表示需要统计总数
        //这样会多进行一次请求select count(0);
        //统计总数,(只对简单SQL语句其效果,复杂SQL语句需要自己写)
        Page<?> page=PageHelper.startPage(pageNo,pageSize,true);
        System.out.printf("数据总数:"+page.getTotal());
        List<User> userList= UserMapper.getUserList(user);//根据user中的数据作为查询条件
        PageInfo<User> pageInfo=new PageInfo<User>(userList);//将List数据封装为PageInfo对象
        return  pageInfo;
    }

}

3.9.TestController:

package com.zyq.pagehelper.controller;
import com.github.pagehelper.PageInfo;
import com.zyq.pagehelper.entity.User;
import com.zyq.pagehelper.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
public class TestController {
    @Autowired
    private UserService userService;

    @GetMapping("/userL")
    @ResponseBody
    public List<User> userList() {
        PageInfo<User> pageInfo=userService.getUserList(new User(0),1,4);
        return pageInfo.getList();
    }
}

3.10.主启动类:

package com.zyq.pagehelper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AppMain {  //   localhost:8080/userL
    public static void main(String[] args) {
        SpringApplication.run(AppMain.class, args);
    }
}

3.11.浏览器测试:

 

 

本案例代码地址:

​https://gitee.com/zhaoyong1230/08mybatis​

四、其他用法:

//第一种,RowBounds方式的调用
List<User> list = sqlSession.selectList("x.y.selectIf", null, new RowBounds(0, 10));
 
//第二种,Mapper接口方式的调用,推荐这种使用方式。
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectIf(1);
 
//第三种,Mapper接口方式的调用,推荐这种使用方式。
PageHelper.offsetPage(1, 10);
List<User> list = userMapper.selectIf(1);
 
//第四种,参数方法调用
//存在以下 Mapper 接口方法,你不需要在 xml 处理后两个参数
public interface CountryMapper {
    List<User> selectByPageNumSize(
            @Param("user") User user,
            @Param("pageNum") int pageNum,
            @Param("pageSize") int pageSize);
}
//配置supportMethodsArguments=true
//在代码中直接调用:
List<User> list = userMapper.selectByPageNumSize(user, 1, 10);
 
//第五种,参数对象
//如果 pageNum 和 pageSize 存在于 User 对象中,只要参数有值,也会被分页
//有如下 User 对象
public class User {
    //其他fields
    //下面两个参数名和 params 配置的名字一致
    private Integer pageNum;
    private Integer pageSize;
}
//存在以下 Mapper 接口方法,你不需要在 xml 处理后两个参数
public interface CountryMapper {
    List<User> selectByPageNumSize(User user);
}
//当 user 中的 pageNum!= null && pageSize!= null 时,会自动分页
List<User> list = userMapper.selectByPageNumSize(user);
 
//第六种,ISelect 接口方式
//jdk6,7用法,创建接口
Page<User> page = PageHelper.startPage(1, 10).doSelectPage(new ISelect() {
    @Override
    public void doSelect() {
        userMapper.selectGroupBy();
    }
});
//jdk8 lambda用法
Page<User> page = PageHelper.startPage(1, 10).doSelectPage(()-> userMapper.selectGroupBy());
 
//也可以直接返回PageInfo,注意doSelectPageInfo方法和doSelectPage
pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(new ISelect() {
    @Override
    public void doSelect() {
        userMapper.selectGroupBy();
    }
});
//对应的lambda用法
pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(() -> userMapper.selectGroupBy());
 
//count查询,返回一个查询语句的count数
long total = PageHelper.count(new ISelect() {
    @Override
    public void doSelect() {
        userMapper.selectLike(user);
    }
});
//lambda
        total=PageHelper.count(()->userMapper.selectLike(user));