杰瑞科技汇

MongoDB Java驱动如何高效连接与使用?

目录

  1. 环境准备
    • 安装 MongoDB
    • 配置 Java 开发环境
  2. 添加 MongoDB Java 驱动依赖
    • Maven
    • Gradle
  3. 连接到 MongoDB 服务器
    • 创建 MongoClient
    • 获取数据库和集合
  4. 核心 CRUD 操作
    • 创建 - 插入文档
    • 读取 - 查询文档
    • 更新 - 修改文档
    • 删除 - 删除文档
  5. 高级查询
    • 查询操作符 ($gt, $lt, $in, $regex 等)
    • 排序 (sort)
    • 分页 (limit, skip)
    • 投影 (projection)
  6. 使用 MongoCollectionFilters 辅助类

    现代化的查询构建方式

  7. 处理 BSON 类型
    • Document, Bson, ObjectId
  8. 最佳实践
    • 连接管理
    • 异常处理
    • 使用 POJO (Plain Old Java Object)
  9. 异步编程 (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());
}

使用 MongoCollectionFilters 辅助类 (推荐)

现代的驱动 API 更倾向于使用 FiltersProjectionsSorts 等辅助类,而不是直接拼凑 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 会变得繁琐且容易出错,你可以使用 CodecMongoMapper 将 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

分享:
扫描分享到社交APP
上一篇
下一篇