PageHelper详解:使用方法与注意事项

PageHelper详解:使用方法与注意事项

一、PageHelper简介

PageHelper是MyBatis框架的一个强大分页插件,用于简化数据库分页查询操作。它通过拦截SQL执行过程并自动添加分页语句,使开发者可以不必手写复杂的分页SQL,从而大大提高了开发效率。

核心特点

支持多种数据库(MySQL、Oracle、PostgreSQL等)自动识别数据库方言,生成对应的分页语句支持多种分页方式(如静态分页、动态分页)支持连续分页查询(如"上一页"、“下一页”)可自定义分页参数和结果处理

二、基本使用方法

2.1 引入依赖

在Maven项目中添加PageHelper依赖:

com.github.pagehelper

pagehelper-spring-boot-starter

1.4.6

2.2 基础配置(Spring Boot)

在application.properties或application.yml中配置PageHelper:

# PageHelper配置

pagehelper:

helper-dialect: mysql # 指定数据库方言

reasonable: true # 开启合理化,页码小于1查询第一页,大于总页数查询最后一页

support-methods-arguments: true # 支持通过Mapper接口参数传递分页参数

page-size-zero: true # pageSize为0时查询所有记录,相当于没有分页

params: count=countSql # 用于从对象中根据属性名取值

2.3 基本使用

方式一:使用startPage静态方法

@RestController

@RequestMapping("/users")

public class UserController {

@Autowired

private UserService userService;

@GetMapping

public PageInfo getUsers(@RequestParam(defaultValue = "1") int pageNum,

@RequestParam(defaultValue = "10") int pageSize) {

// 开始分页,只对之后的第一个查询有效

PageHelper.startPage(pageNum, pageSize);

// 执行查询

List users = userService.getAllUsers();

// 封装分页结果

return new PageInfo<>(users);

}

}

方式二:使用Page对象

@GetMapping("/page")

public PageInfo getUsersByPage() {

// 创建Page对象,构造方法中传入页码和每页记录数

Page page = new Page<>(1, 10);

// 使用page对象查询

page = userService.getUserByPage(page);

// 转换为PageInfo对象返回

return new PageInfo<>(page);

}

方式三:分页参数自动获取(针对Spring MVC)

// 在Mapper接口方法中添加Page参数

public interface UserMapper {

List findAll(Page page);

}

// 在Controller中

@GetMapping("/auto")

public PageInfo getUsersAuto(@RequestParam(defaultValue = "1") int pageNum,

@RequestParam(defaultValue = "10") int pageSize) {

// 直接构造Page对象传给Mapper

Page page = new Page<>(pageNum, pageSize);

List users = userMapper.findAll(page);

return new PageInfo<>(users);

}

2.4 PageInfo对象

PageInfo是PageHelper提供的用于封装分页结果的对象,包含完整的分页信息:

PageInfo pageInfo = new PageInfo<>(users);

// 页码相关信息

int pageNum = pageInfo.getPageNum(); // 当前页码

int pageSize = pageInfo.getPageSize(); // 每页记录数

int pages = pageInfo.getPages(); // 总页数

long total = pageInfo.getTotal(); // 总记录数

// 导航页相关信息

int[] navigatepageNums = pageInfo.getNavigatepageNums(); // 导航页码数组

int navigatePages = pageInfo.getNavigatePages(); // 导航页码数量

// 页面状态

boolean isFirstPage = pageInfo.isIsFirstPage(); // 是否是第一页

boolean isLastPage = pageInfo.isIsLastPage(); // 是否是最后一页

boolean hasPreviousPage = pageInfo.isHasPreviousPage(); // 是否有前一页

boolean hasNextPage = pageInfo.isHasNextPage(); // 是否有下一页

// 数据

List list = pageInfo.getList(); // 当前页数据列表

三、高级用法

3.1 排序查询

// 按id降序排列

PageHelper.startPage(1, 10).orderBy("id desc");

List users = userService.getAllUsers();

// 多字段排序

PageHelper.startPage(1, 10).orderBy("age desc, create_time asc");

List users = userService.getAllUsers();

3.2 count查询优化

// 禁用count查询,适用于不需要总记录数的场景

PageHelper.startPage(1, 10, false);

List users = userService.getAllUsers();

// 自定义count查询SQL

PageHelper.startPage(1, 10).doSelectPageInfo(() -> userService.getAllUsers());

3.3 分页参数设置

// 设置导航页码数量

PageInfo pageInfo = new PageInfo<>(users, 5); // 显示5个导航页码

// 使用默认的每页记录数

Page page = PageHelper.startPage(1); // 使用默认的pageSize

3.4 动态获取分页参数

@GetMapping("/dynamic")

public PageInfo getDynamic(@RequestParam Map params) {

// 从params中动态获取分页参数

int pageNum = params.containsKey("pageNum")

? Integer.parseInt(params.get("pageNum").toString()) : 1;

int pageSize = params.containsKey("pageSize")

? Integer.parseInt(params.get("pageSize").toString()) : 10;

PageHelper.startPage(pageNum, pageSize);

List users = userService.getAllUsers();

return new PageInfo<>(users);

}

3.5 与MyBatis-Plus集成

// 使用MyBatis-Plus的Service接口

@GetMapping("/mybatis-plus")

public IPage getUserWithMP(@RequestParam(defaultValue = "1") int pageNum,

@RequestParam(defaultValue = "10") int pageSize) {

// 创建MP的Page对象

com.baomidou.mybatisplus.extension.plugins.pagination.Page page =

new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageNum, pageSize);

// 使用MP的分页方法

return userService.page(page);

}

四、注意事项和常见问题

4.1 生命周期问题

注意事项1:PageHelper方法调用后紧跟查询语句

// 错误的用法

PageHelper.startPage(1, 10);

method1(); // 其他方法调用

List users = userService.getAllUsers(); // 不会应用分页

// 正确的用法

PageHelper.startPage(1, 10);

List users = userService.getAllUsers(); // 立即执行分页查询

注意事项2:一次分页只对一次查询有效

// 错误的用法

PageHelper.startPage(1, 10);

List users1 = userService.getAllUsers(); // 应用分页

List users2 = userService.getActiveUsers(); // 不会应用分页

// 正确的用法

PageHelper.startPage(1, 10);

List users1 = userService.getAllUsers(); // 应用分页

PageHelper.startPage(1, 10); // 需要再次调用

List users2 = userService.getActiveUsers(); // 应用分页

4.2 多线程问题

PageHelper使用ThreadLocal存储分页参数,在多线程环境下可能出现问题。

// 多线程环境下的错误用法

PageHelper.startPage(1, 10);

CompletableFuture.runAsync(() -> {

List users = userService.getAllUsers(); // 不会应用分页

});

// 正确的用法

CompletableFuture.runAsync(() -> {

PageHelper.startPage(1, 10); // 在同一线程中调用

List users = userService.getAllUsers();

});

4.3 嵌套查询问题

在嵌套查询或关联查询中,分页可能不符合预期。

// 嵌套查询的问题

PageHelper.startPage(1, 10);

List depts = departmentService.getAllWithEmployees();

// 只有Department会分页,关联的Employee不会分页

// 解决方案:单独查询并手动组装数据

PageHelper.startPage(1, 10);

List depts = departmentService.getAll();

for (Department dept : depts) {

dept.setEmployees(employeeService.getByDeptId(dept.getId()));

}

4.4 count查询性能问题

当数据量大时,count查询可能会影响性能。

// 优化方案1:禁用count查询

PageHelper.startPage(1, 10, false);

List users = userService.getAllUsers();

// 优化方案2:缓存count结果

int totalCount = redisTemplate.opsForValue().get("user:total");

if (totalCount == 0) {

// 执行count查询并缓存结果

PageHelper.startPage(1, 0);

userService.getAllUsers();

totalCount = PageInfo.of(users).getTotal();

redisTemplate.opsForValue().set("user:total", totalCount, 1, TimeUnit.HOURS);

}

4.5 参数合理化问题

默认情况下,PageHelper不会校验页码的合理性,需要配置。

pagehelper:

reasonable: true # 开启合理化,页码小于1查询第一页,大于总页数查询最后一页

五、实战示例

5.1 集成Spring Boot的完整例子

// 实体类

@Data

public class User {

private Long id;

private String username;

private String email;

private Date createTime;

}

// Mapper接口

@Mapper

public interface UserMapper {

List selectAll();

List selectByCondition(User user);

}

// Mapper XML

// Service接口

public interface UserService {

PageInfo getUsers(int pageNum, int pageSize);

PageInfo getUsersByCondition(User user, int pageNum, int pageSize);

}

// Service实现

@Service

public class UserServiceImpl implements UserService {

@Autowired

private UserMapper userMapper;

@Override

public PageInfo getUsers(int pageNum, int pageSize) {

PageHelper.startPage(pageNum, pageSize);

List users = userMapper.selectAll();

return new PageInfo<>(users);

}

@Override

public PageInfo getUsersByCondition(User user, int pageNum, int pageSize) {

PageHelper.startPage(pageNum, pageSize);

List users = userMapper.selectByCondition(user);

return new PageInfo<>(users);

}

}

// Controller

@RestController

@RequestMapping("/users")

public class UserController {

@Autowired

private UserService userService;

@GetMapping

public Result> listUsers(

@RequestParam(defaultValue = "1") int pageNum,

@RequestParam(defaultValue = "10") int pageSize) {

PageInfo pageInfo = userService.getUsers(pageNum, pageSize);

return Result.success(pageInfo);

}

@GetMapping("/search")

public Result> searchUsers(

User user,

@RequestParam(defaultValue = "1") int pageNum,

@RequestParam(defaultValue = "10") int pageSize) {

PageInfo pageInfo = userService.getUsersByCondition(user, pageNum, pageSize);

return Result.success(pageInfo);

}

}

5.2 前端分页展示(Vue + Element UI)

六、最佳实践

6.1 合理设置分页大小

根据数据特性和显示需求合理设置每页记录数,避免过大或过小:

过大:影响查询性能和前端加载时间过小:增加页面切换频率,影响用户体验

6.2 使用缓存优化

对于变化不频繁的数据,考虑使用缓存:

@Cacheable(value = "userCache", key = "'page_'+#pageNum+'_'+#pageSize")

public PageInfo getUsers(int pageNum, int pageSize) {

PageHelper.startPage(pageNum, pageSize);

List users = userMapper.selectAll();

return new PageInfo<>(users);

}

6.3 避免大量Join查询

分页查询时避免过多的Join操作,改为先查询主表,再批量查询关联数据:

// 不推荐

PageHelper.startPage(pageNum, pageSize);

List users = userMapper.selectWithRolesAndPermissions();

// 推荐

PageHelper.startPage(pageNum, pageSize);

List users = userMapper.selectAll();

List userIds = users.stream().map(User::getId).collect(Collectors.toList());

Map> userRoleMap = roleService.getUserRoles(userIds);

users.forEach(user -> user.setRoles(userRoleMap.getOrDefault(user.getId(), Collections.emptyList())));

6.4 合理处理排序

在高并发场景下,使用索引字段进行排序,避免全表排序:

// 推荐使用主键或索引字段排序

PageHelper.startPage(pageNum, pageSize).orderBy("id desc");

List users = userMapper.selectAll();

6.5 封装通用分页方法

创建通用的分页查询方法,减少重复代码:

public class PageUtils {

public static PageInfo startPage(Integer pageNum, Integer pageSize,

String orderBy, Supplier> selectSupplier) {

if (pageNum == null || pageSize == null) {

// 不分页

return new PageInfo<>(selectSupplier.get());

}

// 设置分页参数

Page page = PageHelper.startPage(pageNum, pageSize);

if (StringUtils.hasText(orderBy)) {

page.setOrderBy(orderBy);

}

// 执行查询

List list = selectSupplier.get();

return new PageInfo<>(list);

}

}

// 使用方式

PageInfo pageInfo = PageUtils.startPage(1, 10, "create_time desc",

() -> userMapper.selectAll());

七、总结

PageHelper是一个强大的MyBatis分页插件,通过简单的配置和调用,可以大大简化分页查询的实现。但在使用过程中需要注意各种细节和潜在问题,如线程安全、生命周期等。通过合理配置和使用最佳实践,PageHelper可以帮助开发者高效实现各种分页查询需求。

希望这篇详细的PageHelper使用指南能够帮助你更好地理解和使用这个工具!

相关推荐

如何查看减刑
365net

如何查看减刑

📅 08-17 👁️ 4114
以传奇的身份离开 纳瓦斯的皇马生涯总回顾
365bet中文网

以传奇的身份离开 纳瓦斯的皇马生涯总回顾

📅 07-01 👁️ 1071
怎么把Win10注册表恢复初始?
365bet限制投注

怎么把Win10注册表恢复初始?

📅 07-24 👁️ 4079