一、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.配置文件:
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimezone=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.reasonable=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-abandoned=true
## 超时时间;单位为秒。180秒=3分钟
#spring.datasource.druid.remove-abandoned-timeout-millis=180
## 关闭abanded连接时输出错误日志
#spring.datasource.druid.log-abandoned=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 mapperPUBLIC "-//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: 和销毁之前执行@PostConstructpublic void initConfig(){PageInterceptor pageInterceptor=new PageInterceptor();//1.pageInterceptorProperties properties=new Properties();//2.properties// 设置数据库方言为MySQL,以便数据库操作工具类能够正确解析MySQL的SQL语法和特性properties.setProperty("helperDialect","mysql");pageInterceptor.setProperties(properties);//3.pageInterceptor设置properties//4.sqlSessionFactory添加pageInterceptorsqlSessionFactoryList.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 {@Autowiredprivate UserMapper userMapper;//也可以将PageHelper以插件的形式放在userMapper之前(startPage方法)(就具有分页效果了)//如果不加PageHelper.startPage()就没分页效果//注意: 每个查询方法前添加PageHelper.startPage()才会有分页效果@Testpublic 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 {@ResourceUserMapper 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 {@Autowiredprivate UserService userService;@GetMapping("/userL")@ResponseBodypublic 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/userLpublic 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() {@Overridepublic 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() {@Overridepublic void doSelect() {userMapper.selectGroupBy();}
});
//对应的lambda用法
pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(() -> userMapper.selectGroupBy());//count查询,返回一个查询语句的count数
long total = PageHelper.count(new ISelect() {@Overridepublic void doSelect() {userMapper.selectLike(user);}
});
//lambdatotal=PageHelper.count(()->userMapper.selectLike(user));