一、分页查询常用方式
- 在
MyBatis-Plus
中最常用的分页查询的方法是结合IPage
接口和Page
类来实现,也是MyBatis-Plus
官方推荐的方式。 - 同时还支持使用
PageHelper
插件进行分页查询,但这通常用于MyBatis
原生集成,而不是MyBatis-Plus
。
二、使用 IPage 进行分页查询
2.1 IPage 分页查询原理
MyBatis-Plus的 IPage 分页插件主要是通过AOP(面向切面编程)技术来实现的,它可以在运行时动态地拦截方法调用,并在方法执行前后添加额外的行为。这种方式使得分页功能可以很容易地集成到现有的代码中,而不需要对现有查询逻辑做出大的改动。其工作原理如下:
- IPage接口: IPage接口定义了分页相关的方法,包括设置当前页码、每页条数、总条数、是否计数等属性和方法。Page类实现了IPage接口,是一个具体的分页对象。
- 分页插件: MyBatis-Plus提供了分页插件,该插件在MyBatis执行查询之前介入,根据传入的IPage对象构造出对应的分页SQL语句。
- 查询方法: 在MyBatis-Plus中,查询方法通常接受IPage对象作为参数之一。当调用这些查询方法时,分页插件会截取方法参数,并结合IPage对象中的分页参数生成带有分页逻辑的SQL语句。
- 执行查询: 分页插件生成的SQL语句被传递给MyBatis执行,MyBatis执行查询并获取结果集。
- 封装结果: 查询结果被MyBatis-Plus封装成IPage对象,其中包含了总条数、当前页数据列表、总页数等信息。
- 返回结果: 最终,调用者获取到的是封装了分页信息的IPage对象,而不是直接的数据结果集。
2.2 IPage 在Spring Boot项目使用实例
在Spring Boot项目中使用MyBatis-plus进行分页查询通常遵循以下步骤:
- 在pom.xml文件中添加依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>版本号</version>
</dependency>
- 在application.properties或application.yml文件中配置数据库连接信息以及开启MyBatis-plus自动映射功能:
## mysql数据库
spring:
datasource:
dynamic:
datasource:
master:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&useInformationSchema=true
username: root
password: root
## mybatis-plus配置信息
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
banner: false
enable-sql-runner: true
db-config:
id-type: ASSIGN_ID
logic-delete-field: DELETE_FLAG
logic-delete-value: DELETED
logic-not-delete-value: NOT_DELETE
mapper-locations: classpath*:com/test/**/mapping/*.xml,com/bstek/**/mapping/*.xml
type-handlers-package: com.test.common.handler
pagination:
type: OFFSET
- 新建一个Mybatis-Plus的插件配置类,在里面添加分页拦截器,如果不添加分页拦截器,使用IPage进行分页查询的结果中的
records
任然会是全部的数据。
@Configuration
public class MybatisPlusConfig {
/**
* IPage的分页使用的是拦截器,属于物理分页,好处就是处理大量数据时,查询速度快。
*
* @return MybatisPlus拦截器
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 向MybatisPlus拦截器链中添加分页拦截器
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
- 接下来就是接口调用过程,首先创建个接口来获取分页数据。
@GetMapping("/sys/page")
public IPage<SysDTO> page(SysPageParam sysPageParam) {
return sysService.page(sysPageParam);
}
- 定义service接口并继承IService接口
public interface SysService extends IService<SysEntity> {
/**
* 获取最近使用分页
*/
IPage<SysRecentlyUsedDTO> page(SysRecentlyUsedPageParam sysRecentlyUsedPageParam);
}
- 定义service实现类并继承ServiceImpl类
/**
* 如果是单表操作,直接通过IService接口中提供的page方法进行分页查询
* 就不需要编写Mapper.xml
**/
@Service
public class SysServiceImpl extends ServiceImpl<SysMapper, SysEntity> implements SysService {
@Override
public IPage<SysDTO> page(SysPageParam sysPageParam) {
Page<SysDTO> page = new Page<>(sysPageParam.getCurrent(), sysPageParam.getSize());
QueryWrapper<SysOrg> queryWrapper = new QueryWrapper<>();
return this.page(page, queryWrapper);
}
}
/**
* 如果是复杂的查询,就需要通过xml中的自定义sql查询分页
*
**/
@Service
public class SysServiceImpl extends ServiceImpl<SysMapper, SysEntity> implements SysService {
@Resource
private SysMapper sysMapper;
@Override
public IPage<SysDTO> page(SysPageParam sysPageParam) {
Page<SysDTO> page = new Page<>(sysPageParam.getCurrent(), sysPageParam.getSize());
QueryWrapper<SysDTO> queryWrapper = new QueryWrapper<>();
return sysMapper.selectSysDto(page, queryWrapper);
}
}
- 定义Mapper接口并继承BaseMapper
public interface SysMapper extends BaseMapper<SysEntity> {
IPage<SysDTO> selectSysDto(Page<SysDTO> page, @Param("queryParam") QueryWrapper<SysDTO> queryWrapper);
}
- 编写Mapper.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.test.web.sys.org.mapper.SysMapper">
<select id="selectSysDto" resultType="com.test.web.sys.org.dto.SysDTO">
SELECT
si.ORG_ID,
si.USER_ID,
o.NAME AS orgName,
u.NAME AS userName
FROM
SYS_INFO si
LEFT JOIN SYS_ORG o ON si.ORG_ID = o.ID
LEFT JOIN SYS_USER u ON si.USER_ID = u.ID
WHERE si.DELETE_FLAG = 'NOT_DELETE'
ORDER BY si.USED_TIME DESC
</select>
</mapper>
三、使用 PageHelper 进行分页查询
3.1 PageHelper分页插件的工作原理
PageHelper分页插件的工作原理基于MyBatis的拦截器模式,通过拦截MyBatis的执行过程,动态地在查询语句前添加分页逻辑。具体步骤如下:
- 拦截器: PageHelper利用MyBatis的拦截器机制,在MyBatis执行查询操作之前拦截调用。拦截器会分析传入的分页参数,并根据这些参数构建分页SQL。
- 动态SQL: 根据传入的pageNum(当前页码)和pageSize(每页数量)参数,PageHelper在查询语句的前面动态地添加对应数据库的分页语法。例如,在MySQL中添加LIMIT子句,在Oracle中添加ROWNUM条件。
- 执行查询: PageHelper将修改后的分页查询语句交给MyBatis执行,MyBatis执行原始的查询逻辑,但由于已经加入了分页语法,所以实际只会返回分页后的数据集。
- 获取分页结果: MyBatis执行查询后,PageHelper会从结果集中提取必要的分页信息(如总记录数、当前页数据等),并将这些信息封装成PageInfo对象返回。
- 参数处理: PageHelper支持多种方式来识别分页参数,可以通过方法参数、URL参数等多种方式传递分页信息,并且支持自定义参数名。
- 性能优化: PageHelper考虑到性能问题,会尽量避免全表扫描,并且在一些情况下会使用游标而非立即执行查询,以减少内存消耗。
- 数据库兼容性: PageHelper支持多种数据库,并且针对不同数据库的分页语法做了适配,这样开发者就不需要为不同数据库编写不同的分页逻辑。
- 配置灵活性: PageHelper提供了丰富的配置选项,允许开发者根据自己的需求调整分页行为,比如是否开启合理模式、是否支持方法参数分页等。
3.2 PageHelper在Spring Boot项目使用实例
在Spring Boot项目中集成并使用PageHelper分页插件进行查询,通常需要以下步骤:
- 在项目的pom.xml文件中添加PageHelper的Maven依赖。
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>版本号</version>
</dependency>
- 在application.yml或者application.properties文件中配置PageHelper的相关属性。
pagehelper:
reasonable: true # 是否进行分页合理化
supportMethodsArguments: true # 是否支持通过Mapper接口参数来传递分页参数
- 编写Mapper接口,在Mapper接口中定义分页查询的方法,不需要添加任何分页的注解或逻辑。
public interface UserMapper {
List<User> selectUsers();
}
- 使用分页查询,在服务层或控制器层,使用PageHelper的静态方法
startPage
来启用分页功能,然后执行查询操作。
@RestController
public class UserController {
@Autowired
private UserMapper userMapper;
@GetMapping("/users")
public PageInfo<User> listUsers(@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<User> users = userMapper.selectUsers();
return new PageInfo<>(users);
}
}
在上面的代码中,listUsers方法通过调用PageHelper.startPage方法来开启分页,并传入当前页码pageNum和每页大小pageSize。
随后执行userMapper.selectUsers()方法来查询数据。查询结果会自动包含分页信息。