为什么要封装?
- 简化代码:将复杂的
MongoClient,MongoDatabase,MongoCollection等对象的创建和管理隐藏起来,业务代码只需调用简单的方法即可。 - 提高复用性:数据库连接、CRUD 操作等逻辑被封装在工具类或 DAO 层,可以在整个项目中复用。
- 解耦:业务代码不再直接依赖 MongoDB Java 驱动的具体实现,如果未来需要更换数据库或驱动版本,影响范围会小很多。
- 统一管理:可以统一处理连接池、异常、日志、索引管理等。
- 易于测试:可以轻松地 Mock 封装后的接口,进行单元测试。
封装方案
我们将从三种主流的封装方式来讲解:

- 基础工具类封装:适合小型项目或快速原型开发,简单直接。
- 基于 Spring Data MongoDB 的封装:推荐用于 Spring/Spring Boot 项目,是功能最强大、生态最完善的方式。
- 自定义 DAO/Repository 模式:适合中大型项目,提供了最大的灵活性和控制力。
基础工具类封装
这种方式不依赖任何 Spring 框架,纯 Java 实现。
核心思想
创建一个 MongoDBUtil 单例类,负责管理 MongoClient 实例,并提供获取 MongoCollection 和执行 CRUD 操作的静态方法。
实现代码
MongoDBUtil.java
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoException;
import com.mongodb.MongoTimeoutException;
import com.mongodb.client.*;
import com.mongodb.client.model.Filters;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.bson.conversions.Bson;
import java.util.ArrayList;
import java.util.List;
public class MongoDBUtil {
// 1. MongoClient 是线程安全的,推荐全局单例
private static MongoClient mongoClient;
private static final String DATABASE_NAME = "your_database_name";
private static final String COLLECTION_NAME = "your_collection_name";
static {
// 初始化 MongoClient
// 建议在生产环境中使用 MongoClientSettings 进行更精细的配置,如连接池、SSL等
mongoClient = MongoClients.create("mongodb://localhost:27017");
}
/**
* 获取指定集合
* @param collectionName 集合名
* @return MongoCollection<Document>
*/
private static MongoCollection<Document> getCollection(String collectionName) {
MongoDatabase database = mongoClient.getDatabase(DATABASE_NAME);
return database.getCollection(collectionName);
}
// --- 增 ---
public static void insertOne(String collectionName, Document document) {
try {
MongoCollection<Document> collection = getCollection(collectionName);
collection.insertOne(document);
System.out.println("文档插入成功");
} catch (MongoException e) {
System.err.println("插入文档时出错: " + e);
}
}
public static void insertMany(String collectionName, List<Document> documents) {
try {
MongoCollection<Document> collection = getCollection(collectionName);
collection.insertMany(documents);
System.out.println("批量插入文档成功");
} catch (MongoException e) {
System.err.println("批量插入文档时出错: " + e);
}
}
// --- 删 ---
public static long deleteOne(String collectionName, Bson filter) {
try {
MongoCollection<Document> collection = getCollection(collectionName);
DeleteResult result = collection.deleteOne(filter);
System.out.println("删除文档成功,删除数量: " + result.getDeletedCount());
return result.getDeletedCount();
} catch (MongoException e) {
System.err.println("删除文档时出错: " + e);
return 0;
}
}
public static long deleteMany(String collectionName, Bson filter) {
try {
MongoCollection<Document> collection = getCollection(collectionName);
DeleteResult result = collection.deleteMany(filter);
System.out.println("批量删除文档成功,删除数量: " + result.getDeletedCount());
return result.getDeletedCount();
} catch (MongoException e) {
System.err.println("批量删除文档时出错: " + e);
return 0;
}
}
// --- 改 ---
public static UpdateResult updateOne(String collectionName, Bson filter, Bson update) {
try {
MongoCollection<Document> collection = getCollection(collectionName);
UpdateResult result = collection.updateOne(filter, update);
System.out.println("更新文档成功,匹配数量: " + result.getMatchedCount() + ", 修改数量: " + result.getModifiedCount());
return result;
} catch (MongoException e) {
System.err.println("更新文档时出错: " + e);
return null;
}
}
// --- 查 ---
public static Document findOne(String collectionName, Bson filter) {
try {
MongoCollection<Document> collection = getCollection(collectionName);
// 第一个参数是查询条件,第二个是投影(可选)
return collection.find(filter).first();
} catch (MongoException e) {
System.err.println("查询文档时出错: " + e);
return null;
}
}
public static List<Document> findMany(String collectionName, Bson filter) {
List<Document> resultList = new ArrayList<>();
try {
MongoCollection<Document> collection = getCollection(collectionName);
// 使用 find().iterator() 遍历大数据集,避免内存溢出
try (MongoCursor<Document> cursor = collection.find(filter).iterator()) {
while (cursor.hasNext()) {
resultList.add(cursor.next());
}
}
return resultList;
} catch (MongoException e) {
System.err.println("查询文档列表时出错: " + e);
return resultList; // 返回空列表而不是null
}
}
// 关闭连接 (通常在应用关闭时调用)
public static void close() {
if (mongoClient != null) {
mongoClient.close();
System.out.println("MongoDB 连接已关闭");
}
}
// --- 使用示例 ---
public static void main(String[] args) {
// 插入
Document doc1 = new Document("name", "张三")
.append("age", 30)
.append("city", "北京");
insertOne(COLLECTION_NAME, doc1);
// 查询
Bson filter = Filters.eq("name", "张三");
Document foundDoc = findOne(COLLECTION_NAME, filter);
System.out.println("查找到的文档: " + foundDoc.toJson());
// 更新
Bson update = new Document("$set", new Document("age", 31));
updateOne(COLLECTION_NAME, filter, update);
// 删除
deleteOne(COLLECTION_NAME, filter);
// 关闭连接
close();
}
}
优缺点
- 优点:简单、轻量级、不依赖任何框架。
- 缺点:
- 需要手动管理连接生命周期(
close())。 - 缺乏事务支持。
- 查询结果直接是
Document,需要手动转换,容易出错。 - 不支持复杂查询和聚合的便捷方法。
- 需要手动管理连接生命周期(
基于 Spring Data MongoDB 的封装(强烈推荐)
这是目前 Java 开发中使用最广泛、最优雅的方式,Spring Boot 对其提供了极佳的自动配置支持。

核心思想
利用 Spring Data 提供的 MongoTemplate 和 Repository 接口,将数据访问逻辑完全交给 Spring 框架管理,我们只需要定义接口,Spring 会自动实现它。
实现步骤
添加依赖 (pom.xml)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
配置连接 (application.yml)
spring:
data:
mongodb:
uri: mongodb://localhost:27017/your_database_name
# 或者分开写
# host: localhost
# port: 27017
# database: your_database_name
定义实体类

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.bson.types.ObjectId;
@Document(collection = "user") // 对应 MongoDB 集合名
public class User {
@Id
private ObjectId id;
private String name;
private Integer age;
private String city;
// Getters and Setters
// ... 省略 ...
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", city='" + city + '\'' +
'}';
}
}
使用 MongoTemplate (手动封装)
MongoTemplate 是 Spring Data MongoDB 的核心,它封装了所有底层操作,你可以直接注入它来使用。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
@Autowired
private MongoTemplate mongoTemplate;
// 增
public void saveUser(User user) {
mongoTemplate.save(user);
}
// 删
public void deleteUser(String id) {
Query query = new Query(Criteria.where("id").is(id));
mongoTemplate.remove(query, User.class);
}
// 改
public void updateUserAge(String id, Integer newAge) {
Query query = new Query(Criteria.where("id").is(id));
Update update = new Update().set("age", newAge);
mongoTemplate.updateFirst(query, update, User.class);
}
// 查
public User findUserById(String id) {
Query query = new Query(Criteria.where("id").is(id));
return mongoTemplate.findOne(query, User.class);
}
public List<User> findUsersByCity(String city) {
Query query = new Query(Criteria.where("city").is(city));
return mongoTemplate.find(query, User.class);
}
}
使用 Repository (全自动封装)
这是最推荐的方式,你只需要定义一个接口,Spring Data 会自动为你实现所有 CRUD 方法。
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
public interface UserRepository extends MongoRepository<User, String> {
// Spring Data 会根据方法名自动生成查询
// findByName -> db.user.find({name: ?})
List<User> findByName(String name);
// 复杂查询可以使用 @Query 注解
@Query("{'age': {'$gt': ?0}}")
List<User> findUsersByAgeGreaterThan(Integer age);
}
在 Service 中直接注入 UserRepository 即可:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserRepositoryService {
@Autowired
private UserRepository userRepository;
public List<User> findUsersOlderThan(int age) {
return userRepository.findUsersByAgeGreaterThan(age);
}
// 其他方法...
}
优缺点
- 优点:
- 零配置:Spring Boot 自动配置,开箱即用。
- 声明式:
Repository接口极大简化了数据访问层代码。 - 强大的查询能力:支持方法名查询和
@Query注解。 - 集成事务:与 Spring 事务无缝集成。
- 对象映射:自动在 POJO 和
Document之间转换。 - 生态支持:完美融入 Spring 生态,支持 AOP、缓存等。
- 缺点:
需要引入 Spring 框架,不适合非 Spring 项目。
自定义 DAO/Repository 模式
这种方式结合了方案一的直接控制和方案二的分层思想,提供了最大的灵活性。
核心思想
创建一个基础接口 BaseRepository 和其实现 BaseRepositoryImpl,然后为每个实体创建自己的 DAO 接口,继承 BaseRepository。
实现代码
基础接口 BaseRepository.java
import java.util.List;
import java.util.Optional;
public interface BaseRepository<T, ID> {
<S extends T> S save(S entity);
Optional<T> findById(ID id);
List<T> findAll();
void deleteById(ID id);
// ... 其他通用方法
}
基础实现 BaseRepositoryImpl.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import java.util.List;
import java.util.Optional;
public class BaseRepositoryImpl<T, ID> implements BaseRepository<T, ID> {
@Autowired
protected MongoTemplate mongoTemplate;
private final Class<T> entityClass;
// 通过构造函数传入实体类型
public BaseRepositoryImpl(Class<T> entityClass) {
this.entityClass = entityClass;
}
@Override
public <S extends T> S save(S entity) {
mongoTemplate.save(entity);
return entity;
}
@Override
public Optional<T> findById(ID id) {
T entity = mongoTemplate.findById(id, entityClass);
return Optional.ofNullable(entity);
}
@Override
public List<T> findAll() {
return mongoTemplate.findAll(entityClass);
}
@Override
public void deleteById(ID id) {
Query query = new Query(Criteria.where("id").is(id));
mongoTemplate.remove(query, entityClass);
}
}
具体实体 DAO UserRepository.java
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends BaseRepositoryImpl<User, String> {
// 可以在这里添加 User 特有的方法
// List<User> findByCity(String city);
}
配置 Spring 以找到我们的实现
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@Configuration
@EnableMongoRepositories(basePackages = "com.yourpackage.repository", // 你的 repository 所在包
repositoryBaseClass = BaseRepositoryImpl.class) // 指定基础实现类
public class MongoRepositoryConfig {
// ... 其他配置
}
优缺点
- 优点:
- 高度灵活:可以完全控制 DAO 的实现细节。
- 代码复用:通过
BaseRepositoryImpl实现了通用逻辑的复用。 - 类型安全:编译时就能发现类型错误。
- 易于测试:可以轻松地为自定义 DAO 编写测试。
- 缺点:
- 样板代码:需要为每个实体创建接口和配置。
- 复杂度高:比 Spring Data
Repository的方式要复杂。
总结与选择建议
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 基础工具类 | 小型项目、快速原型、非 Spring 项目 | 简单、轻量、无依赖 | 功能弱、需手动管理、易出错 |
| Spring Data MongoDB | 绝大多数 Java 项目,特别是 Spring/Spring Boot 项目 | 功能强大、生态完善、开发效率高 | 依赖 Spring 框架 |
| 自定义 DAO | 中大型项目、有特殊需求、需要极致控制 | 灵活、可控、可扩展 | 样板代码多、复杂度高 |
最终建议:
- 如果你正在使用 Spring Boot,请毫不犹豫地选择方案二(Spring Data MongoDB),这是行业标准,能让你事半功倍。
- 如果你在一个非常小的项目或非 Spring 环境中,方案一(基础工具类)足以应付,并且能快速集成。
- 如果你正在构建一个极其复杂的大型系统,并且对数据访问层有非常特殊的要求,方案三(自定义 DAO)可能会给你带来更多的控制力,但通常情况下,方案二的扩展性已经足够强大。
