杰瑞科技汇

Java如何查询MongoDB数据?

环境准备

在开始之前,请确保你已经准备好了以下环境:

Java如何查询MongoDB数据?-图1
(图片来源网络,侵删)
  1. Java 开发环境: JDK 8 或更高版本。
  2. MongoDB 驱动: 将 MongoDB Java 驱动添加到你的项目中。
    • Maven (pom.xml):
      <dependency>
          <groupId>org.mongodb</groupId>
          <artifactId>mongodb-driver-sync</artifactId>
          <version>4.11.1</version> <!-- 请使用最新版本 -->
      </dependency>
    • Gradle (build.gradle):
      implementation 'org.mongodb:mongodb-driver-sync:4.11.1' // 请使用最新版本
  3. 一个正在运行的 MongoDB 实例

基础连接和操作

我们需要连接到 MongoDB 服务器,MongoDB Java 驱动推荐使用 MongoClient 来管理连接。

MongoClientMongoDatabase

MongoClient 是连接到 MongoDB 的主要入口,它通常在整个应用程序的生命周期中是单例的。

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 服务器,默认为 localhost:27017
        String uri = "mongodb://localhost:27017";
        try (MongoClient mongoClient = MongoClients.create(uri)) {
            // 连接到指定的数据库,如果数据库不存在,MongoDB 会在第一次插入数据时自动创建
            MongoDatabase database = mongoClient.getDatabase("myTestDB");
            System.out.println("成功连接到数据库: " + database.getName());
            // 获取一个集合 (Collection),类似于关系型数据库中的表
            // 如果集合不存在,它也将在第一次插入数据时被创建
            // database.getCollection("users");
        } catch (Exception e) {
            System.err.println("连接 MongoDB 失败: " + e.getMessage());
        }
    }
}

注意: MongoClient 实现了 AutoCloseable 接口,因此推荐使用 try-with-resources 语句来自动管理资源,确保连接被正确关闭。


核心查询操作

查询操作主要通过 collection.find() 方法完成,该方法返回一个 FindIterable<Document> 对象,你可以通过链式调用来添加查询条件、排序、限制等。

Java如何查询MongoDB数据?-图2
(图片来源网络,侵删)

1 查询所有文档

find() 方法不带任何参数会返回集合中的所有文档。

import com.mongodb.client.*;
import org.bson.Document;
public class FindAllExample {
    public static void main(String[] args) {
        String uri = "mongodb://localhost:27017";
        try (MongoClient mongoClient = MongoClients.create(uri)) {
            MongoDatabase database = mongoClient.getDatabase("myTestDB");
            MongoCollection<Document> collection = database.getCollection("users");
            // 查询所有文档
            // .find() 返回一个 FindIterable
            // .forEach() 是一个便捷方法,用于迭代结果集
            collection.find().forEach(document -> System.out.println(document.toJson()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2 精确查询

使用 Filters 类(或 Document)来构建查询条件。

示例数据: 假设 users 集合中有如下文档:

{ "name": "Alice", "age": 30, "city": "New York" }
{ "name": "Bob", "age": 25, "city": "London" }
{ "name": "Charlie", "age": 35, "city": "New York" }

查询 city 为 "New York" 的所有用户:

import com.mongodb.client.*;
import org.bson.Document;
import static com.mongodb.client.model.Filters.*;
public class FindOneExample {
    public static void main(String[] args) {
        String uri = "mongodb://localhost:27017";
        try (MongoClient mongoClient = MongoClients.create(uri)) {
            MongoDatabase database = mongoClient.getDatabase("myTestDB");
            MongoCollection<Document> collection = database.getCollection("users");
            // 使用 Filters.eq 进行精确匹配
            // .find() 返回 FindIterable,我们可以用 .first() 来获取第一个匹配的文档
            Document user = collection.find(eq("city", "New York")).first();
            if (user != null) {
                System.out.println("找到用户: " + user.toJson());
            } else {
                System.out.println("未找到符合条件的用户。");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3 查询多个文档

如果你想获取所有匹配的文档,可以使用 forEachinto 方法。

// 使用 forEach 遍历
collection.find(eq("city", "New York")).forEach(doc -> System.out.println(doc.toJson()));
// 使用 into 将结果存入 List
List<Document> usersInNY = collection.find(eq("city", "New York")).into(new ArrayList<>());
System.out.println("找到 " + usersInNY.size() + " 个用户。");

4 复合查询

使用 Filters 类中的 and, or, gt (greater than), lt (less than) 等方法组合查询条件。

查询 city 为 "New York" 并且 age 大于 28 的用户:

import static com.mongodb.client.model.Filters.*;
// ...
// 复合查询
collection.find(and(
    eq("city", "New York"),
    gt("age", 28)
)).forEach(doc -> System.out.println(doc.toJson()));

查询 city 为 "London" 或者 age 小于 30 的用户:

// ...
// or 查询
collection.find(or(
    eq("city", "London"),
    lt("age", 30)
)).forEach(doc -> System.out.println(doc.toJson()));

5 投影

只返回文档中的特定字段,而不是整个文档。

查询 namecity,但不返回 _id:

// .projection() 用于指定返回的字段
// 1 表示包含,0 表示排除
// _id 默认是包含的,如果想排除,需要明确指定为 0
collection.find(eq("city", "New York"))
          .projection(fields(include("name", "city"), excludeId())) // 或者 new Document("name", 1).append("city", 1)
          .forEach(doc -> System.out.println(doc.toJson()));

输出可能如下:

{ "name": "Alice", "city": "New York" }
{ "name": "Charlie", "city": "New York" }

6 排序和分页

使用 .sort().limit() / .skip() 方法。

查询所有用户,按 age 降序排列,并跳过前 1 个,只取接下来的 2 个:

// .sort() 接收一个 Document,键为字段名,值为 1 (升序) 或 -1 (降序)
collection.find()
          .sort(new Document("age", -1)) // 按 age 降序
          .skip(1)  // 跳过 1 个
          .limit(2) // 只取 2 个
          .forEach(doc -> System.out.println(doc.toJson()));

使用 POJO (Plain Old Java Object)

直接操作 Document 虽然灵活,但在大型项目中不够类型安全,推荐使用 POJO。

1 创建 POJO 类

// User.java
import org.bson.types.ObjectId;
import java.util.Objects;
public class User {
    private ObjectId id;
    private String name;
    private int age;
    private String city;
    // 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; }
    public String getCity() { return city; }
    public void setCity(String city) { this.city = city; }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", city='" + city + '\'' +
                '}';
    }
}

2 进行类型安全的查询

使用 collection.find(query, User.class)

import com.mongodb.client.model.Filters;
import com.mongodb.client.result.DeleteResult;
import org.bson.Document;
import org.bson.types.ObjectId;
public class PojoQueryExample {
    public static void main(String[] args) {
        String uri = "mongodb://localhost:27017";
        try (MongoClient mongoClient = MongoClients.create(uri)) {
            MongoDatabase database = mongoClient.getDatabase("myTestDB");
            MongoCollection<User> collection = database.getCollection("users", User.class);
            // 查询 city 为 "New York" 的所有用户,结果自动映射到 User 对象
            collection.find(Filters.eq("city", "New York"))
                      .forEach(user -> System.out.println(user));
            // 查询单个文档
            User user = collection.find(Filters.eq("name", "Alice")).first();
            System.out.println("查找到的 Alice: " + user);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意: 要让 MongoDB 驱动能将 Document 转换为你的 POJO,POJO 必须有无参构造函数,并且字段名与文档中的键名一致(或者使用 @BsonProperty 注解来映射)。


更新和删除

1 更新

使用 updateOne (更新单个匹配的文档) 或 updateMany (更新所有匹配的文档)。

// ...
MongoCollection<Document> collection = database.getCollection("users");
// 将 name 为 "Bob" 的用户的 age 改为 26
UpdateResult updateResult = collection.updateOne(
    eq("name", "Bob"),
    new Document("$set", new Document("age", 26))
);
System.out.println("匹配的文档数: " + updateResult.getMatchedCount());
System.out.println("修改的文档数: " + updateResult.getModifiedCount());

$set: 如果字段不存在,则创建它;如果存在,则更新它。 $inc: 增加一个字段的数值。

// 将 Alice 的 age 增加 1
collection.updateOne(
    eq("name", "Alice"),
    new Document("$inc", new Document("age", 1))
);

2 删除

使用 deleteOne (删除单个匹配的文档) 或 deleteMany (删除所有匹配的文档)。

// ...
MongoCollection<Document> collection = database.getCollection("users");
// 删除所有 city 为 "London" 的用户
DeleteResult deleteResult = collection.deleteMany(eq("city", "London"));
System.out.println("删除的文档数: " + getDeletedCount());
// 删除 name 为 "Charlie" 的单个用户
collection.deleteOne(eq("name", "Charlie"));

异步操作 (推荐用于高性能应用)

现代驱动支持异步操作,可以避免阻塞线程,提高应用吞吐量,你需要使用 mongodb-driver-reactivestreams 依赖。

1 添加依赖

<!-- Maven -->
<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver-reactivestreams</artifactId>
    <version>4.11.1</version> <!-- 使用与同步驱动一致的版本 -->
</dependency>

2 异步查询示例

异步操作返回 Publisher,你需要使用 Reactive Streams 的 API(如 subscribe)或与 Project Reactor/RxJava 等库集成。

import com.mongodb.client.result.*;
import com.mongodb.reactivestreams.client.*;
import org.bson.Document;
import static com.mongodb.client.model.Filters.*;
public class AsyncQueryExample {
    public static void main(String[] args) {
        String uri = "mongodb://localhost:27017";
        // 创建异步客户端
        MongoClient mongoClient = MongoClients.create(uri);
        MongoDatabase database = mongoClient.getDatabase("myTestDB");
        MongoCollection<Document> collection = database.getCollection("users");
        // 异步查询
        Publisher<Document> findPublisher = collection.find(eq("city", "New York"));
        // 订阅发布者以处理结果
        MongoCollection publisher = collection; // 为了编译通过,实际中需要转换
        collection.find(eq("city", "New York")).subscribe(
            // onNext: 当有文档到达时
            document -> System.out.println("收到文档: " + document.toJson()),
            // onError: 当发生错误时
            throwable -> System.err.println("查询出错: " + throwable.getMessage()),
            // onComplete: 当所有文档处理完成时
            () -> System.out.println("查询完成。")
        );
        // 异步更新
        collection.updateOne(eq("name", "Alice"), new Document("$set", new Document("age", 31))
        .subscribe(
            result -> System.out.println("更新成功,修改了 " + result.getModifiedCount() + " 个文档"),
            throwable -> System.err.println("更新出错: " + throwable.getMessage())
        );
        // 注意:在主线程中,应用可能会在异步操作完成前就退出。
        // 在实际应用中,你需要一个更健壮的生命周期管理机制。
        // 这里为了演示,我们让主线程等待一下
        try {
            Thread.sleep(2000); // 简单等待,不推荐在生产环境中使用
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mongoClient.close();
    }
}

操作 同步 API (mongodb-driver-sync) 异步 API (mongodb-driver-reactivestreams)
连接 MongoClients.create() MongoClients.create()
获取集合 database.getCollection("name", Pojo.class) database.getCollection("name", Pojo.class)
查询 collection.find(filter).forEach(...) collection.find(filter).subscribe(...)
更新 collection.updateOne(filter, update) collection.updateOne(filter, update).subscribe(...)
删除 collection.deleteOne(filter) collection.deleteOne(filter).subscribe(...)
结果处理 阻塞式迭代 (forEach, first(), into) 响应式订阅 (onNext, onError, onComplete)

对于大多数新项目,推荐使用 POJO 进行类型安全的操作,如果你的应用需要处理高并发、低延迟的请求,异步操作是更好的选择,对于简单的脚本或小型应用,同步 API 已经足够强大和易用。

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