Scala MongoDB驱动插入正常但find()查询报错求助
排查Scala MongoDB驱动find()查询抛出BsonInvalidOperationException的问题
这个错误我之前处理过好几次,核心原因是MongoDB中的文档字段类型和你的Scala Machine类的字段类型不匹配,导致驱动在尝试把Bson数据反序列化为Machine对象时,调用了错误的读取方法(比如用readString()读取非String类型的字段)。下面一步步拆解原因和解决办法:
常见错误原因
- 字段类型不匹配:比如你的
Machine类里某个字段定义为String,但MongoDB集合中对应的字段存的是Int、ObjectId甚至Null类型。驱动在反序列化时,会根据类的字段类型调用对应的readXXX()方法,一旦Bson类型不匹配就会抛出这个错误。 - 字段名映射错误:Scala驱动默认用类的字段名作为Mongo文档的键,但如果数据库里用了下划线命名(比如
machine_id)而类里是驼峰式(machineId),或者用了_id作为主键但类里字段名是id,没有正确映射的话,驱动可能会读取错误的字段,导致类型不匹配。 - 自定义Codec逻辑错误:如果你为
Machine类写了自定义的Codec,decode方法里可能硬编码了错误的读取方式(比如不管字段类型都用readString())。
解决办法
1. 先查看原始文档结构,定位不匹配的字段
先暂时把查询结果换成Document类型,看看数据库里的实际数据是什么样的,这能快速帮你找到问题字段:
val collection: MongoCollection[Document] = mongoDB.getCollection(DbConstants.COLLECTION_NAME_MACHINE) collection.find().subscribe(new Observer[Document] { override def onNext(result: Document): Unit = println(s"原始文档: $result") override def onError(e: Throwable): Unit = e.printStackTrace() override def onComplete(): Unit = println("查询完成") })
对比输出的文档字段类型和Machine类的字段定义,比如看到文档里_id是ObjectId("xxx"),但类里id定义为String,这就是问题所在。
2. 修正字段类型或映射关系
如果是类型不匹配,直接调整Machine类的字段类型,或者用注解做映射和转换:
- 比如主键
_id是ObjectId类型,类里可以这样定义:
import org.bson.types.ObjectId import org.mongodb.scala.bson.annotations.BsonProperty case class Machine( @BsonProperty("_id") id: ObjectId, // 用注解映射到Mongo的_id字段 name: String, status: Int // 确保和数据库里的status字段类型一致 )
- 如果数据库里的字段是下划线命名,类里用驼峰,同样用
@BsonProperty指定对应的键名:
@BsonProperty("machine_location") val machineLocation: String
3. 检查自定义Codec(如果有)
如果你实现了Codec[Machine],一定要确保decode方法里每个字段的读取方法和Bson类型对应:
- 读取字符串用
reader.readString() - 读取整数用
reader.readInt32() - 读取ObjectId用
reader.readObjectId()
避免出现不管字段类型都调用readString()的情况。
4. 可选:使用自动映射的增强配置
如果字段名是驼峰和下划线的差异,可以配置驱动的CodecRegistry自动处理命名转换,不用每个字段加注解:
import org.mongodb.scala.MongoClientSettings import org.mongodb.scala.bson.codecs.Macros import org.bson.codecs.configuration.CodecRegistries.{fromProviders, fromRegistries} val codecRegistry = fromRegistries( MongoClientSettings.getDefaultCodecRegistry, fromProviders(Macros.createCodecProviderIgnoreNone[Machine]()) ) val mongoDB = mongoClient.getDatabase("your_db").withCodecRegistry(codecRegistry)
这样驱动会自动把类里的驼峰字段映射到数据库里的下划线字段(比如machineName ↔ machine_name)。
内容的提问来源于stack exchange,提问作者Ambareesh B
相关产品推荐
相关产品推荐

