MongoDB无法找到泛型类Id的编解码器,求解决方案
问题描述
使用Kotlin泛型内联值类作为类型化标识,代码如下:
@JvmInline @Serializable value class Id<T>( @Contextual val value: ObjectId = ObjectId() )
在所有领域模型中使用该类:
@Serializable data class Test( override var _id: Id<Test> = Id(), val value: String, ) @Serializable data class Status( override var _id: Id<Status> = Id(), var test_id: Id<Test>, val value: String ) // ...其他领域模型
数据库初始化正常,文档存储格式正确,但从数据库读取数据时抛出错误:
org.bson.codecs.configuration.CodecConfigurationException:
Can't find a codec for CodecCacheKey{clazz=class Id, types=null}.
解决方案
1. 实现自定义BSON Codec
MongoDB无法自动识别泛型内联值类,需要为Id<T>实现自定义Codec,处理其与ObjectId的相互转换:
class IdCodec<T> : Codec<Id<T>> { private val objectIdCodec = ObjectIdCodec() override fun encode(writer: BsonWriter, value: Id<T>, encoderContext: EncoderContext) { objectIdCodec.encode(writer, value.value, encoderContext) } override fun getEncoderClass(): Class<Id<T>> { @Suppress("UNCHECKED_CAST") return Id::class.java as Class<Id<T>> } override fun decode(reader: BsonReader, decoderContext: DecoderContext): Id<T> { val objectId = objectIdCodec.decode(reader, decoderContext) return Id(objectId) } }
2. 注册Codec到Mongo客户端
初始化MongoClient时,将自定义IdCodec注册到CodecRegistry:
val codecRegistry = CodecRegistries.fromRegistries( MongoClientSettings.getDefaultCodecRegistry(), CodecRegistries.fromCodecs(IdCodec<Any>()) ) val client = MongoClients.create( MongoClientSettings.builder() .codecRegistry(codecRegistry) .build() )
3. 处理泛型擦除问题
由于Kotlin泛型擦除,MongoDB无法区分不同类型参数的Id<T>,但通常上述通用Codec已能满足需求——所有Id<T>最终存储为ObjectId,读取时会自动实例化为对应Id<T>实例。如果需要严格类型区分,可在Codec中结合类型参数做额外校验,但一般场景下无需此操作。
4. 配合Kotlinx Serialization的补充配置
若同时使用Kotlinx Serialization,需确保@Contextual注解生效,或在序列化模块中注册ObjectId的序列化器:
val module = SerializersModule { contextual(ObjectId::class, ObjectIdSerializer) } val format = Json { serializersModule = module }
(注:ObjectIdSerializer可自行实现,或使用org.litote.kmongo:kmongo-serialization等第三方库提供的实现)
内容的提问来源于stack exchange,提问作者user15861750
相关产品推荐
相关产品推荐

