一、MyBatis实现分页
1、关于逻辑分类和物理分页
分页可分为逻辑分页和物理分页:
逻辑分页:是一次性把全部数据查询加载进内存 ,然后再进行分页。这样优点是减少IO次数,适合频繁访问、数据量少的情况。缺点是不适合大数据量,容易造成内存溢出。
物理分页:是利用limit语法在数据库中进行分页。他的优点是适合分页大数据量数据。缺点是频繁查询数据库,消耗性能。
2、Mybatis实现分页功能
mybatis实现分页有三种方式:
- 直接使用SQL语句,利用limit关键字分页(物理分页)
- RowBounds(逻辑分页)
- 第三方插件PageHelper(物理分页)
2.1 SQL语句实现分页
limit语法 limit [offset,] rows
offset 偏移量,从第条数据开始,默认为0
rows 行数,要拿多少条数据
mapper接口中:意思就是 从第几行开始拿多少条数据。
@Select("select count(*) from emp ")
int countEmp();
@Select("select * from emp limit #{offset},#{rows} ")
List<Emp> pageEmp(@Param("offset") int offset,@Param("rows") int rows);
如果我们要实现分页逻辑,一般前端会传 pageNum(第几页) , pageSize(多少条数据)。我们需要自己计算分页逻辑。
比如我们接受到的 pageNum = 3 ,pageSzie = 5:
int pageNum = 3; //查第3页
int pageSize = 5; //每页显示5条数据
int count = empMapper.countEmp(); //总条数
//计算共有多少页 +1是因为如果相除有余数 就等于多了一页,这里粗略计算为多一页
int pagesNum = (count / pageSize) + 1;
// 这页的偏移量offset是多少 , 也就是从第几条开始
int offset = (pageNum - 1) * pageSize; //偏移量
List<Emp> roles = empMapper.pageEmp(offset,pagesNum);
System.out.println(roles);
//最后把分页数据封装到Page类中返回,这里不做演示
这种方式缺点是比较麻烦,要自己计算分页数据和封装Page类。优点是自定义性强。
2.2 RowBounds实现分页
原理:通过RowBounds实现分页和通过数组方式分页原理差不多,都是一次获取所有符合条件的数据,然后在内存中对大数据进行操作,实现分页效果。只是数组分页需要我们自己去实现分页逻辑,这里更加简化而已。
存在问题:一次性从数据库获取的数据可能会很多,对内存的消耗很大,可能导师性能变差,甚至引发内存溢出。
适用场景:在数据量很大的情况下,建议还是适用拦截器实现分页效果。RowBounds建议在数据量相对较小的情况下使用。
RowBounds分页是Mybatis提供的一种分页方式,其原理主要是在执行SQL查询后,将返回的所有结果集加载到内存中,然后在内存中根据指定的偏移量(offset)和限制数(limit)进行分页处理。
具体来说,当我们在Mybatis的Mapper接口中调用查询方法时,可以传入一个RowBounds对象作为参数。这个RowBounds对象包含了分页所需的信息,比如当前页码、每页显示的记录数等。在执行查询时,Mybatis会首先执行完整的SQL查询语句,获取到所有满足条件的结果集。然后,Mybatis会根据RowBounds对象中指定的偏移量和限制数,在内存中对这些结果集进行截取,从而得到当前页需要展示的数据。
需要注意的是,RowBounds分页方式是一种逻辑分页,即在内存中进行分页处理。当数据量非常大时,这种方式可能会导致内存溢出的问题。因此,对于大数据量的分页需求,建议使用物理分页方式,即在SQL查询语句中添加LIMIT和OFFSET子句,直接在数据库层面进行分页处理。
在mapper接口中:
@Select("select count(*) from emp")
int countEmp();
@Select("select * from emp")
List<Emp> pageEmp(RowBounds rowBounds);
int pageNum = 1; //查第几页
int pageSize = 3; //每页几条数据
//总条数
int count = empMapper.countEmp();
//计算出:共有多少页、这页的偏移量offset是多少
int pagesNum = (count / pageSize) + 1; //共有多少页
int offset = (pageNum - 1) * pageSize; //偏移量
RowBounds rowBounds = new RowBounds(offset,pageSize);
List<Emp> emps = empMapper.pageEmp(rowBounds);
System.out.println(emps);
//最后把分页数据封装到Page类中返回,这里不做演示
2.3 第三方插件PageHelper
PageHelper的分页原理:主要基于MyBatis的插件机制。具体来说,PageHelper内部实现了一个PageInterceptor拦截器,这个拦截器会在MyBatis执行SQL查询之前进行拦截。
当我们在代码中调用PageHelper的startPage方法时,它会在当前线程上下文中设置一个ThreadLocal变量,用于保存分页的参数,如当前页码、每页显示的数量等。
随后,当MyBatis执行SQL查询时,PageInterceptor拦截器会拦截到这一操作。拦截器会从ThreadLocal中获取到分页参数,并根据这些参数来改写原始的SQL语句,添加LIMIT和OFFSET子句,以实现分页查询。
改写后的SQL语句会被发送到数据库执行,数据库返回的结果集就是根据分页参数查询得到的结果。
最后,PageInterceptor拦截器会将ThreadLocal中的分页参数清除,避免对后续操作产生影响。
通过这种方式,PageHelper实现了对MyBatis查询结果的分页处理,而无需修改原有的SQL语句、Mapper接口和XML文件,因此具有无侵入性和易用性。同时,由于分页操作是在数据库层面进行的,因此也具有较高的性能。
需要注意的是,PageHelper使用了ThreadLocal来保存分页参数,因此分页参数是与线程绑定的,这意味着不同的线程之间不会共享分页参数,从而保证了分页的准确性和独立性。
总的来说,PageHelper通过拦截MyBatis的SQL查询操作,并在查询语句中添加LIMIT和OFFSET子句,实现了对查询结果的分页处理,从而简化了分页操作的实现过程,提高了开发效率。
PageHelper 是一个 MyBatis 的分页插件,可以简化分页操作。
导入第三方依赖包:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
代码如下:
PageHelper.startPage(1, 3);
PageInfo<Emp> pageInfo = new PageInfo<>(empMapper.allEmp());
System.out.println(pageInfo.getNextPage()); //下一页
System.out.println(pageInfo.getPrePage()); //上一页
System.out.println(pageInfo.getPageNum()); //当前页
System.out.println(pageInfo.getPageSize()); //每页多少条
System.out.println(pageInfo.getSize()); //当前页的条数
System.out.println(pageInfo.getTotal()); //总条数
System.out.println(pageInfo.getPages()); //总页数
System.out.println(pageInfo.getList());
直接执行上面代码发现不行,还需要手动加上一个拦截器:
package com.test.demo.config;
import com.github.pagehelper.PageInterceptor;
import org.apache.ibatis.plugin.Interceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PageHelperConfigure {
@Bean
public Interceptor[] plugins() {
return new Interceptor[]{new PageInterceptor()};
}
}
如果分页失效可以参考下面的文章:
SpringBoot+MyBatis使用pagehelper分页插件及其注意事项(含解决分页不生效问题)_springboot pagehelper查询生效页面不显示-CSDN博客