MongoTemplate 结合 Spring Data Commons 中的 Pageable 和 Sort 接口,可以非常方便地实现分页和排序功能。
以下是如何使用 MongoTemplate 进行分页和排序:
核心概念:
-
org.springframework.data.domain.Sort:- 用于定义排序规则。
- 可以通过
Sort.by(Sort.Direction direction, String... properties)或Sort.by(List<Sort.Order> orders)创建。 Sort.Order对象包含排序方向 (ASC或DESC) 和属性名。
-
org.springframework.data.domain.Pageable:- 封装了分页信息,包括页码 (page number)、每页大小 (page size) 和排序信息 (
Sort)。 - 最常用的实现是
org.springframework.data.domain.PageRequest。
- 封装了分页信息,包括页码 (page number)、每页大小 (page size) 和排序信息 (
-
org.springframework.data.domain.Page<T>:- 查询结果的封装,包含了当前页的数据列表、总记录数、总页数等分页信息。
使用步骤:
1. 排序 (Sorting)
你可以直接将 Sort 对象应用到 Query 中。
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import static org.springframework.data.mongodb.core.query.Criteria.where;
// 假设有一个 User 类和 MongoTemplate 实例
// MongoTemplate mongoTemplate = ...;
// 示例1: 按单个字段升序排序
Query queryAsc = new Query(where("status").is("ACTIVE"));
queryAsc.with(Sort.by(Sort.Direction.ASC, "username")); // 按 username 升序
List<User> sortedUsersAsc = mongoTemplate.find(queryAsc, User.class);
// 示例2: 按单个字段降序排序
Query queryDesc = new Query(where("status").is("ACTIVE"));
queryDesc.with(Sort.by(Sort.Direction.DESC, "registrationDate")); // 按 registrationDate 降序
List<User> sortedUsersDesc = mongoTemplate.find(queryDesc, User.class);
// 示例3: 按多个字段排序 (先按 lastName 升序,再按 firstName 升序)
Query queryMultiSort = new Query(where("department").is("Sales"));
Sort multiSort = Sort.by(
Sort.Order.asc("lastName"),
Sort.Order.asc("firstName")
);
queryMultiSort.with(multiSort);
// 或者更简洁的方式:
// queryMultiSort.with(Sort.by("lastName", "firstName").ascending());
// 或者
// queryMultiSort.with(Sort.by(Sort.Direction.ASC, "lastName", "firstName"));
List<User> multiSortedUsers = mongoTemplate.find(queryMultiSort, User.class);
// 示例4: 混合排序方向
Query queryMixedSort = new Query(where("points").gt(100));
Sort mixedSort = Sort.by(
Sort.Order.desc("points"), // 按 points 降序
Sort.Order.asc("username") // 然后按 username 升序
);
queryMixedSort.with(mixedSort);
List<User> mixedSortedUsers = mongoTemplate.find(queryMixedSort, User.class);
2. 分页 (Pagination)
分页通常与排序结合使用。你需要创建一个 Pageable 对象,并将其应用到 Query 中。
MongoTemplate 本身没有直接返回 Page<T> 的 find 方法。你需要分别获取数据列表和总数,然后手动构建 Page<T> 对象,或者使用辅助方法。
方法一:手动获取数据和总数,然后构建 PageImpl
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import static org.springframework.data.mongodb.core.query.Criteria.where;
// MongoTemplate mongoTemplate = ...;
// 分页参数
int pageNumber = 0; // 第 0 页 (第一页)
int pageSize = 10; // 每页 10 条记录
Sort sort = Sort.by(Sort.Direction.DESC, "registrationDate"); // 按注册日期降序
// 创建 Pageable 对象
Pageable pageable = PageRequest.of(pageNumber, pageSize, sort);
// 创建查询条件
Query query = new Query(where("status").is("ACTIVE"));
// 1. 获取总记录数 (不应用分页的 skip 和 limit, 但应用排序是可选的,通常不需要)
long totalCount = mongoTemplate.count(query, User.class); // 注意:这里的 query 不应该包含分页参数 (skip/limit)
// 2. 应用分页参数到查询中
query.with(pageable); // 这会设置 query.setSkip() 和 query.setLimit() 以及 query.setSortObject()
// 3. 获取当前页的数据列表
List<User> usersOnPage = mongoTemplate.find(query, User.class);
// 4. 构建 Page<T> 对象
Page<User> userPage = new PageImpl<>(usersOnPage, pageable, totalCount);
// 使用 Page 对象
System.out.println("Total elements: " + userPage.getTotalElements());
System.out.println("Total pages: " + userPage.getTotalPages());
System.out.println("Current page number: " + userPage.getNumber());
System.out.println("Page size: " + userPage.getSize());
System.out.println("Content: " + userPage.getContent());
方法二:使用 MongoTemplate 的一些变体或自定义 Repository (推荐用于更简洁的代码)
虽然 MongoTemplate 核心 find 方法不直接返回 Page<T>,但在实际项目中,你通常会:
-
创建自定义 Repository 接口继承
PagingAndSortingRepository或MongoRepository:
这些接口提供了开箱即用的分页和排序方法,如findAll(Pageable pageable)。这是最推荐的方式,因为它更简洁,也符合 Spring Data 的设计理念。import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.mongodb.repository.MongoRepository; public interface UserRepository extends MongoRepository<User, String> { Page<User> findByStatus(String status, Pageable pageable); } // 在 Service 中使用: // @Autowired // private UserRepository userRepository; // // Pageable pageable = PageRequest.of(0, 10, Sort.by("username")); // Page<User> userPage = userRepository.findByStatus("ACTIVE", pageable); -
使用 Spring Data Projections (如果只需要部分字段):
结合 Repository 和 Pageable,可以返回投影接口或 DTO 的分页结果。 -
通过
MongoOperations(MongoTemplate 实现的接口) 扩展方法 (不常见,但可以实现):
可以创建自己的工具类或服务方法来封装分页逻辑。
Query 对象与 Pageable 的交互:
当你调用 query.with(pageable) 时,Spring Data MongoDB 会从 Pageable 对象中提取以下信息并设置到 Query 对象中:
pageable.getOffset()->query.skip(offset)pageable.getPageSize()->query.limit(pageSize)pageable.getSort()->query.with(sort)(设置排序对象)
示例:结合查询条件、分页和排序
// MongoTemplate mongoTemplate = ...;
// 分页参数
int pageNumber = 1; // 第二页 (页码从0开始)
int pageSize = 5;
Sort sort = Sort.by(Sort.Order.asc("lastName"), Sort.Order.desc("points"));
Pageable pageable = PageRequest.of(pageNumber, pageSize, sort);
// 查询条件:部门为 "Support" 且激活状态为 true
Query query = new Query(
new Criteria().andOperator(
where("department").is("Support"),
where("isActive").is(true)
)
);
// 获取总数 (基于未分页的查询)
long totalCount = mongoTemplate.count(Query.of(query).limit(-1).skip(-1), User.class); // 克隆query并移除分页
// 应用分页到原始查询对象
query.with(pageable);
// 获取当前页数据
List<User> pagedUsers = mongoTemplate.find(query, User.class);
// 构建 Page 对象
Page<User> userPage = new PageImpl<>(pagedUsers, pageable, totalCount);
// 使用结果
System.out.println("Content for page " + userPage.getNumber() + ": " + userPage.getContent());
System.out.println("Total users matching criteria: " + userPage.getTotalElements());
关于 mongoTemplate.count() 的注意事项:
- 传递给
mongoTemplate.count()的Query对象不应该包含skip()和limit()设置,否则count()的结果会是你期望的当前页数据量(如果limit小于总数)而不是总匹配记录数。 - 排序对于
count()操作通常是不必要的,MongoDB 在计算数量时会忽略它。 - 一个安全的做法是克隆查询对象并移除分页/排序信息,或者创建一个新的仅包含条件的查询对象用于
count。
或者更简单地,如果你的Query countQuery = Query.of(query); // 克隆查询条件 countQuery.limit(-1); // 确保没有 limit countQuery.skip(-1); // 确保没有 skip countQuery.setSortObject(null); // 移除排序 long total = mongoTemplate.count(countQuery, User.class);query在调用count之前还没有被with(pageable)修改过,可以直接用。
总结:
- 使用
Sort对象定义排序规则,并用query.with(Sort)应用。 - 使用
PageRequest.of(pageNumber, pageSize, sort)创建Pageable对象。 - 调用
query.with(Pageable)将分页和排序信息应用到查询。 - 分别执行
mongoTemplate.count()获取总记录数和mongoTemplate.find()获取当前页数据。 - 使用
PageImpl将结果封装成Page<T>。 - 强烈推荐使用 Spring Data Repositories (如
MongoRepository) 来处理分页和排序,代码会更简洁且不易出错。
3462

被折叠的 条评论
为什么被折叠?



