我们将使用 MongoDB Java Driver (版本 4.x 及以上),这是目前官方推荐的方式。
准备工作
确保你的项目中已经添加了 MongoDB Java Driver 的依赖。
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' // 请使用最新版本
数据模型
假设我们有一个 users 集合,每个用户文档包含一个 tags 数组字段,用于存储用户的兴趣标签。
// users 集合中的示例文档
{
"_id": ObjectId("63f8b8e1e4b0f3a1b2c3d4e5"),
"name": "Alice",
"age": 30,
"tags": ["java", "mongodb", "programming"]
}
{
"_id": ObjectId("63f8b8f2e4b0f3a1b2c3d4e6"),
"name": "Bob",
"age": 25,
"tags": ["python", "data-science", "mongodb"]
}
{
"_id": ObjectId("63f8b900e4b0f3a1b2c3d4e7"),
"name": "Charlie",
"age": 35,
"tags": ["java", "spring-boot", "react"]
}
在 Java 中,我们可以用一个简单的 POJO (Plain Old Java Object) 来表示这个文档结构。
import org.bson.types.ObjectId;
public class User {
private ObjectId id;
private String name;
private int age;
private List<String> tags; // 关键:用 List 表示数组
// 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 List<String> getTags() { return tags; }
public void setTags(List<String> tags) { this.tags = tags; }
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", tags=" + tags +
'}';
}
}
连接到 MongoDB
在执行操作前,我们需要一个 MongoClient 来连接数据库。
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoCollection;
public class MongoConnection {
public static MongoCollection<User> getUserCollection() {
// 替换为你的 MongoDB 连接字符串
String uri = "mongodb://localhost:27017";
try (MongoClient mongoClient = MongoClients.create(uri)) {
MongoDatabase database = mongoClient.getDatabase("testdb"); // 数据库名
// 如果需要,可以配置 CodecRegistry 来自动处理 POJO
// 这里为了简单,我们后续会手动处理 Bson 转换
return database.getCollection("users", User.class);
}
}
}
注意:
MongoClients.create(uri)返回一个实现了AutoCloseable的MongoClient,将它放在try-with-resources块中是一个好习惯,可以确保连接被正确关闭。
插入包含数组的文档
插入数据非常直接,因为 Java Driver 会自动将 List 映射为 MongoDB 的数组。
import com.mongodb.client.MongoCollection;
import java.util.Arrays;
import java.util.List;
public class InsertExample {
public static void main(String[] args) {
MongoCollection<User> collection = MongoConnection.getUserCollection();
User user1 = new User();
user1.setName("Alice");
user1.setAge(30);
user1.setTags(Arrays.asList("java", "mongodb", "programming"));
User user2 = new User();
user2.setName("Bob");
user2.setAge(25);
user2.setTags(Arrays.asList("python", "data-science", "mongodb"));
// 插入单个文档
collection.insertOne(user1);
System.out.println("Inserted Alice");
// 插入多个文档
collection.insertMany(Arrays.asList(user1, user2)); // 注意:实际中可能重复插入,这里仅为示例
System.out.println("Inserted multiple users");
}
}
查询数组
查询是操作数组最核心的部分,MongoDB 提供了非常丰富的操作符来查询数组。
1 精确匹配整个数组
使用 Filters.all() 来查找数组字段包含所有指定元素的文档。
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
import org.bson.Document;
import java.util.Arrays;
public class QueryExactArray {
public static void main(String[] args) {
MongoCollection<User> collection = MongoConnection.getUserCollection();
// 查找 tags 数组同时包含 "java" 和 "mongodb" 的用户
// 注意:顺序不重要,但元素必须全部存在
collection.find(Filters.all("tags", "java", "mongodb"))
.forEach(doc -> System.out.println(doc));
}
}
2 匹配数组中的任意一个元素
使用 Filters.in() 来查找数组字段包含至少一个指定元素的文档。
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
public class QueryAnyElement {
public static void main(String[] args) {
MongoCollection<User> collection = MongoConnection.getUserCollection();
// 查找 tags 数组中包含 "mongodb" 的所有用户
collection.find(Filters.in("tags", "mongodb"))
.forEach(doc -> System.out.println(doc));
}
}
3 查询数组长度
使用 Filters.size() 来查找数组长度为指定值的文档。
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
public class QueryArraySize {
public static void main(String[] args) {
MongoCollection<User> collection = MongoConnection.getUserCollection();
// 查找 tags 数组长度恰好为 3 的用户
collection.find(Filters.size("tags", 3))
.forEach(doc -> System.out.println(doc));
}
}
4 查询数组元素的位置
Filters.elemMatch(): 当数组元素是文档时非常有用,可以匹配文档内嵌的特定条件。Filters.where(): 使用 JavaScript 表达式进行更复杂的查询。
更新数组
更新数组同样有多种方式,使用 Updates 类来构建更新操作。
1 向数组末尾添加元素
使用 Updates.push()。
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Updates;
import org.bson.Document;
public class UpdatePush {
public static void main(String[] args) {
MongoCollection<User> collection = MongoConnection.getUserCollection();
// 给 Alice 的 tags 数组末尾添加一个新标签 "docker"
collection.updateOne(
Filters.eq("name", "Alice"),
Updates.push("tags", "docker")
);
// 查看更新结果
collection.find(Filters.eq("name", "Alice")).forEach(doc -> System.out.println(doc));
}
}
更新后,Alice 的 tags 将变为 ["java", "mongodb", "programming", "docker"]。
2 向数组开头添加元素
使用 Updates.pushFirst()。
// 给 Alice 的 tags 数组开头添加一个新标签 "kubernetes"
collection.updateOne(
Filters.eq("name", "Alice"),
Updates.pushFirst("tags", "kubernetes")
);
更新后,Alice 的 tags 将变为 ["kubernetes", "java", "mongodb", "programming", "docker"]。
3 添加元素(如果不存在)
使用 Updates.addToSet(),如果元素已存在,则不会重复添加。
// 尝试给 Alice 的 tags 数组添加 "java",如果已存在则忽略
collection.updateOne(
Filters.eq("name", "Alice"),
Updates.addToSet("tags", "java")
);
4 删除数组元素
- 删除所有匹配的元素:
Updates.pull("field", value) - 删除第一个匹配的元素:
Updates.pullFirst("field", value)
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Updates;
public class UpdatePull {
public static void main(String[] args) {
MongoCollection<User> collection = MongoConnection.getUserCollection();
// 删除 Alice 的 tags 数组中所有的 "mongodb" 标签
collection.updateOne(
Filters.eq("name", "Alice"),
Updates.pull("tags", "mongodb")
);
// 查看结果
collection.find(Filters.eq("name", "Alice")).forEach(doc -> System.out.println(doc));
}
}
5 按位置更新数组元素
使用 Updates.set() 配合数组索引(从 0 开始)。
// 将 Alice 的 tags 数组中索引为 1 的元素(第二个)更新为 "golang"
collection.updateOne(
Filters.eq("name", "Alice"),
Updates.set("tags.1", "golang")
);
高级用法:数组作为嵌入式文档
数组中的元素不一定是简单的字符串或数字,也可以是复杂的文档对象。
1 数据模型和插入
假设我们想记录用户的地址历史。
import java.util.List;
public class UserWithAddress {
private ObjectId id;
private String name;
private List<Address> addresses; // 地址列表
// ... getters and setters
}
class Address {
private String street;
private String city;
private int zipCode;
// ... getters and setters
}
// 插入示例
UserWithAddress user = new UserWithAddress();
user.setName("David");
user.setAddresses(Arrays.asList(
new Address("123 Main St", "New York", 10001),
new Address("456 Oak Ave", "Boston", 02108)
));
collection.insertOne(user);
2 查询嵌入式文档数组
这是 Filters.elemMatch() 大显身手的地方,我们想查找住在 "New York" 的用户。
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Projections;
// 查找 addresses 数组中至少有一个元素的 city 是 "New York" 的用户
collection.find(Filters.elemMatch("addresses",
Filters.eq("city", "New York")))
.forEach(doc -> System.out.println(doc));
// 如果只想返回匹配的地址,而不是整个用户文档
collection.find(Filters.elemMatch("addresses", Filters.eq("city", "New York")))
.projection(Projections.include("name", "addresses.$")) // .$ 返回第一个匹配的嵌入式文档
.forEach(doc -> System.out.println(doc));
| 操作 | Java 方法/类 | MongoDB Shell (mongosh) 等价示例 |
|---|---|---|
| 插入 | collection.insertOne(user) |
db.users.insertOne({...}) |
| 精确匹配数组 | Filters.all("tags", "a", "b") |
db.users.find({tags: {$all: ["a", "b"]}}) |
| 匹配任意元素 | Filters.in("tags", "a") |
db.users.find({tags: {$in: ["a"]}}) |
| 匹配数组长度 | Filters.size("tags", 3) |
db.users.find({tags: {$size: 3}}) |
| 向数组末尾添加 | Updates.push("tags", "c") |
db.users.updateOne({}, {$push: {tags: "c"}}) |
| 添加不重复元素 | Updates.addToSet("tags", "a") |
db.users.updateOne({}, {$addToSet: {tags: "a"}}) |
| 删除所有匹配元素 | Updates.pull("tags", "a") |
db.users.updateOne({}, {$pull: {tags: "a"}}) |
| 按索引更新 | Updates.set("tags.1", "new") |
db.users.updateOne({}, {$set: {"tags.1": "new"}}) |
| 查询嵌入式文档 | Filters.elemMatch("arr", Filters.eq("k", "v")) |
db.users.find({"arr": {$elemMatch: {k: "v}}}) |
掌握这些基本操作后,你就可以在 Java 应用中灵活地处理 MongoDB 的数组数据了,核心是熟练运用 Filters 和 Updates 这两个工具类。
