MongoDB Async Driver Java检查记录存在性失效,求高效实现方法
使用MongoDB Async Driver高效检查记录存在性的解决方案
先说说你当前代码的问题所在——这也是插入逻辑没执行的核心原因:
- MongoDB Async Driver的
find()方法返回的是FindIterable对象,永远不会是null,所以你写的==null判断永远不成立,自然跳不到插入逻辑。limit(0)会强制查询返回0条结果,这完全违背了你“检查记录是否存在”的初衷。
接下来给你两种高效的实现方式,以及高并发场景下的原子性方案:
方法1:用countDocuments()(简洁直观)
这种方式直接统计匹配查询条件的文档数量,只要数量为0就说明记录不存在。因为是异步操作,要通过CompletableFuture的回调来处理结果:
// 构造你的查询条件,比如按某个唯一字段查询 Bson filter = Filters.eq("username", "patoCapongo93"); collection.countDocuments(filter) .thenAccept(count -> { if (count == 0) { // 记录不存在,执行插入 Document newUser = new Document("username", "patoCapongo93") .append("email", "example@test.com"); collection.insertOne(newUser) .thenRun(() -> System.out.println("新文档插入成功")) .exceptionally(e -> { System.err.println("插入失败:" + e.getMessage()); return null; }); } }) .exceptionally(e -> { System.err.println("计数查询失败:" + e.getMessage()); return null; });
方法2:用findOne()(性能更优)
如果只是判断存在性,findOne()会在找到第一条匹配的文档后立即终止查询,不需要统计所有匹配的数量,在数据量较大时性能更好。另外可以指定只返回_id字段,减少网络传输的数据量:
Bson filter = Filters.eq("username", "patoCapongo93"); // 只查询_id字段,降低网络开销 Bson projection = Projections.include("_id"); collection.findOne(filter, projection) .thenAccept(existingDoc -> { if (existingDoc == null) { // 记录不存在,执行插入 Document newUser = new Document("username", "patoCapongo93") .append("email", "example@test.com"); collection.insertOne(newUser) .thenRun(() -> System.out.println("新文档插入成功")) .exceptionally(e -> { System.err.println("插入失败:" + e.getMessage()); return null; }); } }) .exceptionally(e -> { System.err.println("查询失败:" + e.getMessage()); return null; });
高并发场景:原子性“不存在则插入”
如果你的场景存在并发写入,上面的两种方法可能会出现竞态条件(比如两个请求同时判断记录不存在,然后都执行插入)。这种情况下更推荐使用以下两种方案:
方案1:使用replaceOne原子操作
Document newUser = new Document("username", "patoCapongo93") .append("email", "example@test.com"); collection.replaceOne( Filters.eq("username", "patoCapongo93"), newUser, new ReplaceOptions().upsert(true) ).thenAccept(result -> { if (result.getUpsertedId() != null) { System.out.println("文档是新插入的"); } else { System.out.println("文档已存在,执行了更新"); } }).exceptionally(e -> { System.err.println("操作失败:" + e.getMessage()); return null; });
方案2:唯一索引+insertOne
先给查询字段创建唯一索引:
collection.createIndex(Indexes.ascending("username"), new IndexOptions().unique(true));
之后直接执行insertOne,如果记录已存在会抛出MongoWriteException,你可以捕获这个异常来处理已存在的情况:
Document newUser = new Document("username", "patoCapongo93") .append("email", "example@test.com"); collection.insertOne(newUser) .thenRun(() -> System.out.println("新文档插入成功")) .exceptionally(e -> { if (e instanceof MongoWriteException && ((MongoWriteException) e).getError().getCode() == 11000) { System.out.println("文档已存在"); } else { System.err.println("插入失败:" + e.getMessage()); } return null; });
内容的提问来源于stack exchange,提问作者patoCapongo93
相关产品推荐
相关产品推荐

