Java环境下MongoDB的db.collection.foo()不可用及运行报错问题求助
概述
目前所有Java中使用MongoDB命令的示例都以db.collection.someMongoMethod()开头,比如db.collection.update()或db.collection.updateMany()。我找不到任何说明解释db.collection代表什么、如何创建这个对象,相关文档默认使用者已经掌握这部分知识。
核心问题
如何在Java中正确实现类似db.collection.foo()的调用?我需要在Java方法里调用db.collection.updateMany()等MongoDB方法,以下是我尝试编写的代码和对应的编译报错,其中有一段代码编译通过但运行时报错,报错信息附在代码块后。
尝试的实现代码
public void renameField(MongoDatabase db, MongoCollection<org.bson.Document> bsonCollection, String oldField, String newField) { // 编译报错:Students cannot be resolved or is not a field //db.students.update({oldField: {$exists: true}}, {$rename:{oldField:newField}}, false, true); // 编译报错:bsonCollection cannot be resolved or is not a field //db.bsonCollection.update({oldField: {$exists: true}}, {$rename:{oldField:newField}}, false, true); // 编译提示需要给bsonCollection加强转 //bsonCollection.update({oldField: {$exists: true}}, {$rename:{oldField:newField}}, false, true); // 编译提示需要修改接收方强转 //((Object) bsonCollection).update({oldField: {$exists: true}}, {$rename:{oldField:newField}}, false, true); // 编译提示参数不符合8种重载方法的要求 //bsonCollection.updateMany({oldField: {$exists: true}}, {$rename:{oldField:newField}}, false, true); Document filter = new Document(oldField, "{$exists: true}"); Document update = new Document("$rename", "{"+oldField+":"+newField+"}"); // 编译提示需要删除多余参数匹配(bson,bson)签名,或者改用updateOne //bsonCollection.updateMany(filter.toBsonDocument(), update.toBsonDocument(), false, true); bsonCollection.updateMany(filter.toBsonDocument(), update.toBsonDocument()); }
运行时错误
调用collection.updateMany时的报错栈如下:
2021-06-12 11:49:06 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet].log(175) - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is ongodb.MongoWriteException: Modifiers operate on fields but we found type string instead. For example: {$mod: {<field>: ...}} not {$rename: "{name:firstName}"}] with root cause com.mongodb.MongoWriteException: Modifiers operate on fields but we found type string instead. For example: {$mod: {<field>: ...}} not {$rename: "{name:firstName}"} at com.mongodb.client.internal.MongoCollectionImpl.executeSingleWriteRequest(MongoCollectionImpl.java:1017) ... 省略多余栈信息
构建环境
使用Spring Boot 2.4.5、Eclipse、Java 8、MongoDB 4.4,相关pom依赖如下:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${commons-lang3.version}</version><!--$NO-MVN-MAN-VER$--> </dependency> </dependencies>
疑问点
所有网上关于Java执行MongoDB命令的示例和文档都以db.collection.someMethod()开头,但我找不到任何完整示例说明db.collection由哪些对象组成、如何初始化。
1. db和collection对应的Java对象说明
Mongo Shell语法和Java驱动API的对应关系如下:
- Shell中的
db对应Java里的MongoDatabase实例,你代码中已经拿到了该参数 - Shell中的
db.collectionName(比如示例里的db.students)对应Java里的MongoCollection<org.bson.Document>实例,也就是你代码里的bsonCollection参数
如果需要自行初始化这两个对象,用以下方式即可:
// Spring Boot环境下直接注入MongoClient即可,自动配置已经帮你初始化好连接 @Autowired private MongoClient mongoClient; public void getCollectionDemo() { // 拿到对应数据库实例,对应Shell里的use 数据库名 MongoDatabase db = mongoClient.getDatabase("你的数据库名"); // 拿到对应集合实例,对应Shell里的db.集合名 MongoCollection<Document> collection = db.getCollection("你的集合名"); }
Java是静态强类型语言,不能像Shell那样直接用db.students的点号语法访问集合,必须通过getCollection方法获取集合对象。
2. 报错原因及修复方案
你遇到的运行报错是因为构造BSON参数时,把操作符对应的值写成了字符串,Mongo驱动识别为普通字段值,而非嵌套的BSON对象。
推荐修复方案(用自带工具类,避免手动拼写错误)
直接用Spring Data MongoDB提供的Filters和Updates工具类构造参数:
import org.springframework.data.mongodb.core.query.Filters; import org.springframework.data.mongodb.core.query.Updates; public void renameField(MongoCollection<org.bson.Document> bsonCollection, String oldField, String newField) { // 构造查询条件:匹配存在oldField字段的文档 Bson filter = Filters.exists(oldField); // 构造更新操作:将oldField重命名为newField Bson update = Updates.rename(oldField, newField); // 执行批量更新 bsonCollection.updateMany(filter, update); }
手动构造Document的正确写法
如果你需要手动拼接Document,注意所有操作符对应的参数都要为Document对象,不能拼接JSON字符串:
public void renameField(MongoCollection<org.bson.Document> bsonCollection, String oldField, String newField) { // 正确的filter写法,$exists对应的值是布尔值,不是字符串 Document filter = new Document(oldField, new Document("$exists", true)); // 正确的update写法,$rename对应的值是嵌套Document对象,不是拼接的字符串 Document update = new Document("$rename", new Document(oldField, newField)); bsonCollection.updateMany(filter, update); }
内容的提问来源于stack exchange,提问作者Bill Snee

