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&useUnicode=true&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 方式四
在实体类中声明 pageNum
和 pageSize
属性。不建议使用,侵入性太强 了。
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
配置的字段中取值,查找到合适的值时就会自动分页。