目录
- 环境准备
- 安装 MongoDB
- 配置 Java 开发环境
- 添加 MongoDB Java 驱动依赖
- Maven
- Gradle
- 连接到 MongoDB 服务器
- 创建
MongoClient - 获取数据库和集合
- 创建
- 核心 CRUD 操作
- 创建 - 插入文档
- 读取 - 查询文档
- 更新 - 修改文档
- 删除 - 删除文档
- 高级查询
- 查询操作符 (
$gt,$lt,$in,$regex等) - 排序 (
sort) - 分页 (
limit,skip) - 投影 (
projection)
- 查询操作符 (
- 使用
MongoCollection和Filters辅助类现代化的查询构建方式
- 处理 BSON 类型
Document,Bson,ObjectId
- 最佳实践
- 连接管理
- 异常处理
- 使用 POJO (Plain Old Java Object)
- 异步编程 (Reactive Streams)
环境准备
安装 MongoDB
如果你还没有安装 MongoDB,请先从 MongoDB 官网 下载并安装,安装完成后,确保 mongod 服务正在运行。
配置 Java 开发环境
确保你已经安装了 JDK (建议 JDK 8 或更高版本) 和一个 IDE (如 IntelliJ IDEA 或 Eclipse)。
添加 MongoDB Java 驱动依赖
这是使用 MongoDB 驱动的第一步,你需要将驱动库添加到你的项目中。
使用 Maven
在你的 pom.xml 文件中添加以下依赖:
<dependencies>
<!-- MongoDB Java Driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.11.1</version> <!-- 请使用最新版本 -->
</dependency>
</dependencies>
使用 Gradle
在你的 build.gradle 文件中添加以下依赖:
dependencies {
// MongoDB Java Driver
implementation 'org.mongodb:mongodb-driver-sync:4.11.1' // 请使用最新版本
}
注意: 驱动版本号会不断更新,请务必在 Maven Central Repository 查找最新的稳定版本。
连接到 MongoDB 服务器
连接是所有操作的基础,MongoDB Java 驱动通过 MongoClient 类来管理连接。
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
public class MongoConnectionExample {
public static void main(String[] args) {
// --- 连接字符串 ---
// 默认连接到本地 MongoDB 实例,端口 27017
String uri = "mongodb://localhost:27017";
try (MongoClient mongoClient = MongoClients.create(uri)) {
// --- 获取数据库 ---
// 如果数据库 "mydb" 不存在,MongoDB 会在第一次插入数据时自动创建它
MongoDatabase database = mongoClient.getDatabase("mydb");
// --- 获取集合 ---
// 集合类似于关系型数据库中的表,如果集合不存在,也会在第一次插入时创建
// database.getCollection("users");
System.out.println("成功连接到数据库: " + database.getName());
System.out.println("准备进行操作...");
} catch (Exception e) {
System.err.println("连接 MongoDB 失败: " + e.getMessage());
e.printStackTrace();
}
}
}
代码解释:
MongoClients.create(uri): 创建一个MongoClient实例,这是线程安全的,推荐在你的整个应用中只创建一个实例并重用。mongoClient.getDatabase("mydb"): 获取一个数据库对象,如果数据库不存在,MongoDB 不会立即创建它,而是在你第一次向其中写入数据时创建。try-with-resources:MongoClient实现了AutoCloseable接口,使用try-with-resources可以确保连接在使用完毕后被正确关闭,防止资源泄漏。
核心 CRUD 操作
假设我们有一个名为 users 的集合,用来存储用户信息。
C - 创建 - 插入文档
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoClients;
import org.bson.Document;
// ... (连接代码同上)
MongoDatabase database = mongoClient.getDatabase("mydb");
MongoCollection<Document> collection = database.getCollection("users");
// 创建一个文档 (BSON 对象)
Document user1 = new Document("name", "Alice")
.append("age", 30)
.append("city", "New York")
.append("interests", Arrays.asList("reading", "hiking"));
Document user2 = new Document("name", "Bob")
.append("age", 25)
.append("city", "London")
.append("interests", Arrays.asList("gaming", "coding"));
// 插入一个文档
collection.insertOne(user1);
System.out.println("用户 Alice 插入成功");
// 插入多个文档
List<Document> usersToInsert = Arrays.asList(user1, user2); // 实际中 user1 和 user2 应不同
collection.insertMany(usersToInsert);
System.out.println("批量插入成功");
R - 读取 - 查询文档
查询是 MongoDB 最强大的功能之一。
// ... (连接代码同上)
MongoCollection<Document> collection = database.getCollection("users");
// 1. 查询所有文档
FindIterable<Document> allUsers = collection.find();
System.out.println("--- 所有用户 ---");
for (Document doc : allUsers) {
System.out.println(doc.toJson());
}
// 2. 查询特定条件的文档 ( age > 28)
// 创建一个查询过滤器
Document queryFilter = new Document("age", new Document("$gt", 28));
FindIterable<Document> olderUsers = collection.find(queryFilter);
System.out.println("--- 年龄大于 28 的用户 ---");
for (Document doc : olderUsers) {
System.out.println(doc.toJson());
}
// 3. 查询并返回特定字段 (投影)
// 第二个参数是投影,1 表示包含,0 表示排除
Document projection = new Document("name", 1).append("city", 1).append("_id", 0);
FindIterable<Document> projectedUsers = collection.find().projection(projection);
System.out.println("--- 只包含 name 和 city 的用户 ---");
for (Document doc : projectedUsers) {
System.out.println(doc.toJson());
}
U - 更新 - 修改文档
// ... (连接代码同上)
MongoCollection<Document> collection = database.getCollection("users");
// 1. 更新单个文档
// 将名字为 "Alice" 的用户的年龄更新为 31
Document updateFilter = new Document("name", "Alice");
Document updateOperation = new Document("$set", new Document("age", 31));
UpdateResult updateResult = collection.updateOne(updateFilter, updateOperation);
System.out.println("匹配了 " + updateResult.getMatchedCount() + " 个文档");
System.out.println("修改了 " + updateResult.getModifiedCount() + " 个文档");
// 2. 更新多个文档
// 将所有城市为 "London" 的用户的年龄加 1
Document multiUpdateFilter = new Document("city", "London");
Document multiUpdateOperation = new Document("$inc", new Document("age", 1));
UpdateResult multiUpdateResult = collection.updateMany(multiUpdateFilter, multiUpdateOperation);
System.out.println("批量更新匹配了 " + multiUpdateResult.getMatchedCount() + " 个文档");
D - 删除 - 删除文档
// ... (连接代码同上)
MongoCollection<Document> collection = database.getCollection("users");
// 1. 删除单个文档
// 删除名字为 "Bob" 的用户
Document deleteFilter = new Document("name", "Bob");
DeleteResult deleteResult = collection.deleteOne(deleteFilter);
System.out.println("删除了 " + deleteResult.getDeletedCount() + " 个文档");
// 2. 删除多个文档
// 删除所有年龄小于 25 的用户
Document multiDeleteFilter = new Document("age", new Document("$lt", 25));
DeleteResult multiDeleteResult = collection.deleteMany(multiDeleteFilter);
System.out.println("批量删除了 " + multiDeleteResult.getDeletedCount() + " 个文档");
高级查询
使用 Filters 辅助类可以更方便、更安全地构建查询。
import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Projections.*;
import static com.mongodb.client.model.Sorts.*;
// ... (连接代码同上)
MongoCollection<Document> collection = database.getCollection("users");
// 1. 复合查询: 年龄在 25 到 30 之间,并且城市是 "New York"
FindIterable<Document> complexQuery = collection.find(
and(
gte("age", 25),
lte("age", 30),
eq("city", "New York")
)
);
System.out.println("--- 复合查询结果 ---");
for (Document doc : complexQuery) {
System.out.println(doc.toJson());
}
// 2. 排序: 按年龄降序
FindIterable<Document> sortedQuery = collection.find().sort(descending("age"));
System.out.println("--- 按年龄降序排序 ---");
for (Document doc : sortedQuery) {
System.out.println(doc.toJson());
}
// 3. 分页: 跳过前 2 个,获取接下来的 2 个
FindIterable<Document> pagedQuery = collection.find().skip(2).limit(2);
System.out.println("--- 分页查询 (第 2 页,每页 2 条) ---");
for (Document doc : pagedQuery) {
System.out.println(doc.toJson());
}
使用 MongoCollection 和 Filters 辅助类 (推荐)
现代的驱动 API 更倾向于使用 Filters、Projections、Sorts 等辅助类,而不是直接拼凑 Document,这种方式更类型安全,代码也更易读。
上面的高级查询部分已经展示了 Filters 的用法,这是构建查询的标准方式。
处理 BSON 类型
MongoDB 使用 BSON (Binary JSON) 格式,Java 驱动提供了多种方式来表示 BSON。
Document: 最常用的 BSON 类型,本质上是Map<String, Object>的子类,非常适合动态或半结构化的数据。Bson: 一个接口,表示任何 BSON 类型。Document实现了Bson接口。Filters辅助类返回的就是Bson对象。ObjectId: MongoDB 中文档的唯一 ID 类型,它不是普通的字符串。- 创建:
new ObjectId() - 从字符串转换:
new ObjectId("...") - 从文档中获取:
doc.getObjectId("_id")
- 创建:
// 插入一个带有 _id 的文档
Document docWithId = new Document("_id", new ObjectId())
.append("name", "Charlie");
collection.insertOne(docWithId);
// 查询时使用 _id
ObjectId idToFind = docWithId.getObjectId("_id");
Document foundDoc = collection.find(eq("_id", idToFind)).first();
System.out.println("通过 _id 查找到: " + foundDoc.toJson());
最佳实践
连接管理
- 单例
MongoClient: 在整个应用程序的生命周期内,只创建一个MongoClient实例,它是线程安全的,可以高效地管理连接池。 - 不要频繁创建/销毁: 创建
MongoClient是一个相对昂贵的操作,避免在每次请求时都创建新的客户端。
异常处理
MongoException: 是驱动中所有异常的基类,捕获它来处理可能发生的网络问题、服务器错误等。MongoTimeoutException: 当操作超时时抛出。MongoCommandException: 当数据库命令执行失败时抛出(查询语法错误)。
使用 POJO (Plain Old Java Object)
对于结构固定的数据,直接使用 Document 会变得繁琐且容易出错,你可以使用 Codec 或 MongoMapper 将 Java 对象直接与 MongoDB 文档进行映射。
驱动本身提供了 Codec 接口,社区也有非常成熟的库,如 MongoDB POJO Codec。
// 1. 定义你的 POJO
@BsonDiscriminator
public class User {
@BsonId
private ObjectId id;
private String name;
private int age;
// 必须有无参构造函数
public User() {}
// Getters and Setters
public ObjectId getId() { return id; }
public void setId(ObjectId id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
// 2. 在代码中使用
// 注册 POJO Codec 并创建 MongoClient
CodecRegistry pojoCodecRegistry = CodecRegistries.fromRegistries(
MongoClient.getDefaultCodecRegistry(),
CodecRegistries.fromProviders(PojoCodecProvider.builder().register(User.class).build())
);
MongoClient mongoClient = MongoClients.create(
MongoClientSettings.builder()
.applyConnectionString(new ConnectionString("mongodb://localhost:27017"))
.codecRegistry(pojoCodecRegistry)
.build()
);
// 3. 操作
MongoDatabase database = mongoClient.getDatabase("mydb");
MongoCollection<User> userCollection = database.getCollection("users_pojo", User.class);
// 插入
User user = new User();
user.setName("David");
user.setAge(40);
userCollection.insertOne(user);
// 查询
User foundUser = userCollection.find(eq("name", "David")).first();
System.out.println("查找到 POJO 用户: " + foundUser.getName());
异步编程 (Reactive Streams)
对于高吞吐量的应用,同步的 mongodb-driver-sync 可能会成为瓶颈,官方提供了基于 Reactive Streams 的异步驱动 mongodb-driver-reactivestreams。
它返回 Publisher,可以与 Project Reactor、RxJava 等响应式框架无缝集成。
Maven 依赖:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-reactivestreams</artifactId>
<version>4.11.1</version>
</dependency>
简单示例:
import com.mongodb.reactivestreams.client.MongoClients;
import com.mongodb.reactivestreams.client.MongoCollection;
import org.bson.Document;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
public class ReactiveMongoExample {
public static void main(String[] args) {
// 创建 Reactive Client
try (MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017")) {
MongoCollection<Document> collection = mongoClient.getDatabase("mydb").getCollection("users");
// 异步插入
collection.insertOne(new Document("name", "Eve"))
.subscribe(new Subscriber<Document>() {
@Override
public void onSubscribe(Subscription s) {
s.request(1); // 请求处理一个元素
}
@Override
public void onNext(Document document) {
System.out.println("插入成功: " + document);
}
@Override
public void onError(Throwable t) {
System.err.println("发生错误: " + t.getMessage());
}
@Override
public void onComplete() {
System.out.println("操作完成");
}
});
// 为了让异步操作有时间完成,主线程需要等待
// 在实际应用中,你会有一个事件循环或类似的结构
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
| 任务 | 同步驱动方法 | 异步驱动方法 | 备注 |
|---|---|---|---|
| 创建客户端 | MongoClients.create(uri) |
MongoClients.create(uri) |
异步驱动返回 MongoClient 的实现类 |
| 获取集合 | database.getCollection("name") |
database.getCollection("name") |
|
| 插入一个 | collection.insertOne(doc) |
collection.insertOne(doc) |
返回 void,通过 Subscriber 处理结果 |
| 插入多个 | collection.insertMany(docs) |
collection.insertMany(docs) |
|
| 查询一个 | collection.find(filter).first() |
collection.find(filter).first() |
返回 Publisher<T>,需要订阅 |
| 查询多个 | collection.find(filter).forEach(...) |
collection.find(filter).subscribe(...) |
同步方式使用 forEach,异步方式使用 subscribe |
| 更新一个 | collection.updateOne(filter, update) |
collection.updateOne(filter, update) |
返回 UpdateResult (同步) 或 Publisher<UpdateResult> (异步) |
| 更新多个 | collection.updateMany(filter, update) |
collection.updateMany(filter, update) |
|
| 删除一个 | collection.deleteOne(filter) |
collection.deleteOne(filter) |
返回 DeleteResult (同步) 或 Publisher<DeleteResult> (异步) |
| 删除多个 | collection.deleteMany(filter) |
collection.deleteMany(filter) |
对于大多数 Java 应用,mongodb-driver-sync 是一个非常好的起点,它简单、直观且功能强大,只有在你需要处理极高并发和低延迟的场景时,才考虑转向 mongodb-driver-reactivestreams。
