Java MongoDB 分页实现指南
在 Java 应用程序中使用 MongoDB 实现分页功能,通常有以下几种方法:

使用 skip() 和 limit()
这是最简单的分页方式,通过 skip() 跳过前面的文档,用 limit() 限制返回的文档数量。
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import org.bson.Document;
// 假设已初始化MongoCollection<Document> collection
int pageNumber = 1; // 页码,从1开始
int pageSize = 10; // 每页大小
// 计算跳过的文档数
int skip = (pageNumber - 1) * pageSize;
// 执行查询
MongoCursor<Document> cursor = collection.find()
.skip(skip)
.limit(pageSize)
.iterator();
// 遍历结果
while (cursor.hasNext()) {
Document doc = cursor.next();
// 处理文档
}
cursor.close();
缺点:当页数很大时,skip() 会变得非常低效,因为它需要扫描并跳过前面的所有文档。
使用范围查询(推荐)
对于大数据集,使用范围查询比 skip() 更高效,这种方法通常基于某个唯一或排序字段(如时间戳或ID)。
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import org.bson.Document;
// 假设已初始化MongoCollection<Document> collection
int pageSize = 10;
String lastId = null; // 上一页最后一条记录的ID
// 构建查询条件
Document query = new Document();
if (lastId != null) {
query.append("_id", new Document("$gt", lastId));
}
// 执行查询,按ID排序
MongoCursor<Document> cursor = collection.find(query)
.sort(new Document("_id", 1))
.limit(pageSize)
.iterator();
// 遍历结果
while (cursor.hasNext()) {
Document doc = cursor.next();
// 处理文档
lastId = doc.getObjectId("_id").toString(); // 更新lastId
}
cursor.close();
使用聚合框架(Aggregation)
对于更复杂的分页需求,可以使用聚合框架:

import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import org.bson.Document;
import java.util.Arrays;
// 假设已初始化MongoCollection<Document> collection
int pageNumber = 1;
int pageSize = 10;
List<Bson> pipeline = Arrays.asList(
new Document("$match", new Document()), // 匹配条件,可以为空
new Document("$skip", (pageNumber - 1) * pageSize),
new Document("$limit", pageSize)
);
MongoCursor<Document> cursor = collection.aggregate(pipeline).iterator();
// 遍历结果
while (cursor.hasNext()) {
Document doc = cursor.next();
// 处理文档
}
cursor.close();
使用 Spring Data MongoDB
如果你使用 Spring Data MongoDB,可以更简单地实现分页:
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface UserRepository extends MongoRepository<User, String> {
Page<User> findAll(Pageable pageable);
}
// 使用示例
Pageable pageable = PageRequest.of(pageNumber - 1, pageSize);
Page<User> users = userRepository.findAll(pageable);
性能优化建议
- 避免大页码:对于
skip()方法,避免使用非常大的页码(如第10000页),性能会很差。 - 使用索引:确保分页查询使用的字段(如排序字段)有适当的索引。
- 考虑缓存:对于不经常变化的数据,考虑缓存分页结果。
- 预计算总数:如果需要显示总记录数,可以使用
count()方法,但要注意它可能会影响性能。
完整示例(基于Spring Boot)
@Service
public class UserService {
@Autowired
private MongoTemplate mongoTemplate;
public Page<User> findUsers(int page, int size) {
// 创建分页请求
Pageable pageable = PageRequest.of(page, size);
// 执行分页查询
Query query = new Query().with(pageable);
List<User> users = mongoTemplate.find(query, User.class);
// 获取总数
long total = mongoTemplate.count(query, User.class);
// 返回分页结果
return new PageImpl<>(users, pageable, total);
}
// 使用范围查询的分页方法
public List<User> findUsersWithRange(String lastId, int size) {
Query query = new Query();
if (lastId != null) {
query.addCriteria(Criteria.where("id").gt(lastId));
}
query.limit(size);
query.with(Sort.by(Sort.Direction.ASC, "id"));
return mongoTemplate.find(query, User.class);
}
}
选择哪种方法取决于你的具体需求、数据集大小和性能要求,对于大多数应用,范围查询是最佳选择。

