1. 概述
本文将演示如何使用 MongoDB Java Driver 执行日期相关的 CRUD 操作,包括:
- 创建和更新包含日期字段的文档
- 查询、更新和删除日期字段在指定范围内的文档
2. 环境准备
2.1 Maven 依赖
确保已安装 MongoDB(未安装可参考官方指南)。在 pom.xml
中添加依赖:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.6.0</version>
</dependency>
2.2 POJO 数据模型
定义 POJO 表示数据库文档:
public class Event {
private String title;
private String location;
private LocalDateTime dateTime;
public Event() {}
public Event(String title, String location, LocalDateTime dateTime) {
this.title = title;
this.location = location;
this.dateTime = dateTime;
}
// 标准 setter/getter
}
关键点:
- 必须有无参构造函数(MongoDB 默认使用)
- 日期字段使用
LocalDateTime
而非String
(避免格式化问题)
2.3 MongoDB 客户端配置
注册 PojoCodecProvider
实现 POJO 序列化:
CodecProvider codecProvider = PojoCodecProvider.builder().automatic(true).build();
CodecRegistry codecRegistry = fromRegistries(getDefaultCodecRegistry(), fromProviders(codecProvider));
创建数据库连接:
MongoClient mongoClient = MongoClients.create(uri);
MongoDatabase db = mongoClient.getDatabase("calendar").withCodecRegistry(codecRegistry);
MongoCollection<Event> collection = db.getCollection("my_events", Event.class);
3. 创建含日期字段的文档
利用 LocalDateTime
的便捷 API 构造对象:
Event pianoLessonsEvent = new Event("Piano lessons", "Foo Blvd",
LocalDateTime.of(2022, 6, 4, 11, 0, 0));
Event soccerGameEvent = new Event("Soccer game", "Bar Avenue",
LocalDateTime.of(2022, 6, 10, 17, 0, 0));
插入数据库:
InsertOneResult pianoLessonsInsertResult = collection.insertOne(pianoLessonsEvent);
InsertOneResult soccerGameInsertResult = collection.insertOne(soccerGameEvent);
验证插入结果:
assertNotNull(pianoLessonsInsertResult.getInsertedId());
assertNotNull(soccerGameInsertResult.getInsertedId());
4. 按日期条件查询文档
精确匹配日期时间
使用 eq
过滤器:
LocalDateTime dateTime = LocalDateTime.of(2022, 6, 10, 17, 0, 0);
Event event = collection.find(eq("dateTime", dateTime)).first();
验证查询结果:
assertEquals("Soccer game", event.title);
assertEquals("Bar Avenue", event.location);
assertEquals(dateTime, event.dateTime);
日期范围查询
使用 BasicDBObject
和 $gte
/$lte
操作符:
LocalDateTime from = LocalDateTime.of(2022, 06, 04, 12, 0, 0);
LocalDateTime to = LocalDateTime.of(2022, 06, 10, 17, 0, 0);
BasicDBObject object = new BasicDBObject();
object.put("dateTime", BasicDBObjectBuilder.start("$gte", from).add("$lte", to).get());
List<Event> events = new ArrayList<>(collection.find(object).into(new ArrayList<>()));
验证结果(仅返回足球比赛):
assertEquals(1, events.size());
assertEquals("Soccer game", events.get(0).title);
assertEquals("Bar Avenue", events.get(0).location);
assertEquals(dateTime, events.get(0).dateTime);
5. 更新文档
5.1 更新单个文档的日期字段
使用 updateOne()
和 currentDate()
:
Document document = new Document().append("title", "Piano lessons");
Bson update = Updates.currentDate("dateTime");
UpdateOptions options = new UpdateOptions().upsert(false);
UpdateResult result = collection.updateOne(document, update, options);
关键点:
upsert(false)
禁用自动插入(无匹配时不创建新文档)- 验证更新数量:
assertEquals(1, result.getModifiedCount());
5.2 批量更新日期范围内的文档
使用 updateMany()
和日期范围查询:
LocalDate updateManyFrom = LocalDate.of(2022, 1, 1);
LocalDate updateManyTo = LocalDate.of(2023, 1, 1);
Bson query = and(gte("dateTime", updateManyFrom), lt("dateTime", updateManyTo));
Bson updates = Updates.currentDate("dateTime");
UpdateResult result = collection.updateMany(query, updates);
验证更新数量:
assertEquals(2, result.getModifiedCount());
6. 删除日期范围内的文档
使用 deleteMany()
和日期范围查询:
LocalDate deleteFrom = LocalDate.of(2022, 1, 1);
LocalDate deleteTo = LocalDate.of(2023, 1, 1);
Bson query = and(gte("dateTime", deleteFrom), lt("dateTime", deleteTo));
DeleteResult result = collection.deleteMany(query);
验证删除数量:
assertEquals(2, result.getDeletedCount());
7. 处理时区问题
MongoDB 强制使用 UTC 存储日期。若需时区支持,需额外存储时区偏移量:
public String timeZoneOffset;
更新构造函数:
public Event(String title, String location, LocalDateTime dateTime, String timeZoneOffset) {
this.title = title;
this.location = location;
this.dateTime = dateTime;
this.timeZoneOffset = timeZoneOffset;
}
创建带时区的事件(注意 dateTime
必须为 UTC 时间):
LocalDateTime utcDateTime = LocalDateTime.of(2022, 6, 20, 11, 0, 0);
Event pianoLessonsTZ = new Event("Piano lessons", "Baz Bvld", utcDateTime, ZoneOffset.ofHours(2).toString());
InsertOneResult pianoLessonsTZInsertResult = collection.insertOne(pianoLessonsTZ);
assertNotNull(pianoLessonsTZInsertResult.getInsertedId());
查询后转换时区:
OffsetDateTime dateTimeWithOffset = OffsetDateTime.of(
pianoLessonsTZ.dateTime,
ZoneOffset.of(pianoLessonsTZ.timeZoneOffset)
);
8. 总结
本文完整演示了使用 Java 操作 MongoDB 日期数据的最佳实践:
- 使用
LocalDateTime
替代String
避免格式化问题 - 通过
PojoCodecProvider
实现 POJO 自动映射 - 灵活运用日期范围查询(
$gte
/$lte
) - 批量更新/删除文档的技巧
- 时区处理的解决方案(存储偏移量 + 手动转换)
完整示例代码见 GitHub 仓库。