一、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));