添加依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.5</version>
</dependency>
配置文件
pagehelper:
# helper-dialect:指定数据库,不指定的话会默认自动检测数据库类型
helper-dialect: mysql
# reasonable:是否启用分页合理化。如果启用,当pagenum<1时,会自动查询第一页的数据,当pagenum>pages时,自动查询最后一页数据;不启用的,以上两种情况都会返回空数据
reasonable: false
# support-methods-arguments:默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。(如果参数中有pageNum,pageSize分页参数,则会自动分页)
support-methods-arguments: false
# params:用于从对象中根据属性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
params: count=countsql
方法使用
//方法一:使用service查询到的结果存储在request域中
private void selectAllUsers(HttpServletRequest request, HttpServletResponse response){
Page<Person> page=PageHelper.startPage(Integer.parseInt(num),5);
//通过userService获取user的信息,其sql语句为"select * from user" 但因pagehelp已经注册为插件,所以pagehelp会在原sql语句上增加limit,从而实现分页
List<Person> persons=userService.getAllUsersBypageHelper();
//获取页面信息的对象,里面封装了许多页面的信息 如:总条数,当前页码,需显示的导航页等等
PageInfo<Person> pageHelper=page.toPageInfo();
}
//方法二:使用service查询到的结果存储在自定义的类中然后返回给前端
public PageInfo<ProjectListVO> queryByProjectName(Integer pageNo, Integer pageSize, String projectName) {
PageResult<List<ProjectListVO>> result = new PageResult<>();
try {
PageHelper.startPage(pageNo, pageSize);
//设置完上边的PageHelper之后查询的时候,查询语句会自动加入 limits startpage count,查询结果就是正确的结果
List<ProjectListVO> projectVOList = projectMapper.queryByProjectName(projectName);
//获取页面信息的对象,里面封装了许多页面的信息 如:总条数,当前页码,需显示的导航页等等
PageInfo<ProjectListVO> pageInfo = new PageInfo<>(projectVOList);
} finally {
PageHelper.clearPage();
}
return result;
}
PageHelper.clearPage()
PageHelper 是较为常用的分页插件,通过实现 Mybatis 的 Interceptor 接口完成对 query sql 的动态分页,其中分页参数由
ThreadLocal进行保存,threadlocal而是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据.
简单的 分页执行过程:
- 设置 page 参数
- 执行 query 方法
- Interceptor 接口 中校验 ThreadLocal 中是否存在有设置的 page 参数
- 存在 page 参数,重新生成 count sql 和 page sql,并执行查询。不存在 page 参数,直接返回 查询结果
- 执行 LOCAL_PAGE.remove() 清除 page 参数 threadlocal
这里就存在一个问题了
观察上述的执行过程,可以发现,如果在第 1 步和第 2 步 之间发生异常,那么 LOCAL_PAGE 中当前线程对应的 page 参数并不会 remove。
在不使用线程池的情况下,当前线程在执行完毕后会被销毁,这时 当前线程 中的 threadLocals 参数 将会被情况,也就清空 了
LOCAL_PAGE 中 当前线程的 page 参数。
但是如果使用了线程池,当前线程执行完毕,并不会被销毁,而是会将当前线程再次存放到池中,标记为空闲状态,以便后续使用。在后续使用这个线程的时候,由于
线程 的 threadLocals 依旧存在有值,尽管我们在第 1 步时未设置 page 参数,第 3 步 的也能获取到page参数,从而生成 count sql 和
page sql,从而影响我们的正常查询我。
另外SpringBoot 项目中会使用内置的 Tomcat 作为服务器,而Tomcat会默认使用线程来处理请求,从而便引发了上述问题
解决方案1,在每次使用完分页语句后执行pagehelper.clearpage()如上demo所示,但是这样比较麻烦
我们可以实现 HandlerInterceptor , WebRequestInterceptor 对 request 请求的拦截器,清理我们localthread里的page
@Component
public class UrlInterceptor implements HandlerInterceptor, WebMvcConfigurer {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
PageHelper.clearPage();
return true;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this).addPathPatterns("/**");
}
}
解决方案2,使用try-with-resources语句,这是JDK1.7的新特性,
Java 7增强了try语句的功能——它允许在try关键字后跟一对圆括号,圆括号可以声明,初始化一个或多个资源,此处的资源指得是那些必须在程序结束时必须关闭的资源(比如数据库连接,网络连接等)
只要这些资源实现类实现了Closeable或AutoCloseable接口,就可以自动关闭。
注意:如果try()里面有两个资源,用逗号分开,资源的close方法的调用顺序与它们的创建顺序相反。
//方法一
private void selectAllUsers(HttpServletRequest request, HttpServletResponse response) {
try (Page<Person> page = PageHelper.startPage(Integer.parseInt(num), 5)) {
//通过userService获取user的信息,其sql语句为"select * from user" 但因PageHelp已经注册为插件,所以pagehelp会在原sql语句上增加limit,从而实现分页
List<Person> persons = userService.getAllUsersBypageHelper();
//获取页面信息的对象,里面封装了许多页面的信息 如:总条数,当前页码,需显示的导航页等等
PageInfo<Person> pageHelper = page.toPageInfo();
}
}
//方法二:使用service查询到的结果存储在自定义的类中然后返回给前端
public PageInfo<ProjectListVO> queryByProjectName(Integer pageNo, Integer pageSize, String projectName) {
PageResult<List<ProjectListVO>> result = new PageResult<>();
try (PageHelper.startPage(pageNo, pageSize)) {
//设置完上边的PageHelper之后查询的时候,查询语句会自动加入 limits startpage count,查询结果就是正确的结果
List<ProjectListVO> projectVOList = projectMapper.queryByProjectName(projectName);
//获取页面信息的对象,里面封装了许多页面的信息 如:总条数,当前页码,需显示的导航页等等
PageInfo<ProjectListVO> pageInfo = new PageInfo<>(projectVOList);
}
return result;
}