You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin操作MongoDB查询Author报Jackson无setter属性set调用异常

MongoDB 数组字段追加操作报setterless属性实例化异常

问题场景

authors为MongoDB中使用的集合,结构定义如下:

authors = [
  {
    name: "Author1",
    books: [
      {title:"Book1"},
      {title:"Book2"}
    ]
  }
]

需求为向指定作者的books数组中追加新的book对象,追加前需要先根据作者名称校验对应记录是否存在,因此编写了findOneByAuthorname方法。调用该方法时抛出如下异常:

Unhandled: POST - /v1/api/addBooks. Exception class com.fasterxml.jackson.databind.exc.ValueInstantiationException:  Cannot construct instance of `com.sample.models.schema.Author`, problem: Should never call `set()` on setterless property ('books') 

使用Postman发送POST请求时传入的参数为{title: "book3"},原有业务代码如下:

@kotlinx.serialization.Serializable
data class Author ( val name: String , val books : MutableList<Bookitem>)
@kotlinx.serialization.Serializable
data class Bookitem (val title :String)

private var collection : CoroutineCollection<Author> = MongoConfig.getDatabase().getCollection("authors")

suspend fun findOneByAuthorname(Name:String) : Author {
collection.findOne(Author::name eq Name)
}
suspend fun addBooks(Name:String,book:Bookitem) : Bookitem?{

collection.findOneandUpdate(Author::name eq  Name, ,push(Author::books ,Bookitem(book)))
}

已确认排查线索:完全移除books数据类相关定义后,find方法可以正常运行。


问题根因

  • 序列化适配缺失:KMongo默认使用Jackson做POJO反序列化,未加载Kotlin适配模块时,Jackson不会通过类的主构造器实例化对象,而是尝试寻找无参构造+字段setter注入查询结果。Author类中books字段用val修饰,不会生成setter方法,Jackson尝试对该字段赋值时就会抛出无setter的实例化异常,这也是移除books字段后查询恢复正常的核心原因。同时类上标注了kotlinx.serialization的序列化注解但未配置KMongo使用对应序列化器,也会导致序列化逻辑混乱。
  • 代码存在语法与逻辑错误:addBooks方法中findOneAndUpdate的参数列表多了一个多余的逗号;push操作中重复对入参book做Bookitem构造包装,入参本身已经是目标类型;findOneByAuthorname方法缺少return关键字,查询结果无法正常返回。
  • 传参格式不规范:Postman传入的JSON没有给key加双引号,属于非标准JSON格式,也可能触发序列化解析问题。

修复方案

  1. 补全序列化适配
    最简处理方式是给Mongo客户端配置Jackson Kotlin模块,让Jackson可以正确识别Kotlin data class的主构造器,支持对val修饰的只读字段赋值,不需要将字段改为var。如果需要使用kotlinx.serialization做序列化,初始化KMongo时显式指定使用kotlinx序列化Mapper即可。
  2. 修正业务代码错误,参考实现如下:
import org.litote.kmongo.*
import org.litote.kmongo.coroutine.CoroutineCollection

@kotlinx.serialization.Serializable
data class Author(val name: String, val books: MutableList<Bookitem>)

@kotlinx.serialization.Serializable
data class Bookitem(val title: String)

// 初始化集合时显式声明泛型,避免序列化器推断错误
private val collection: CoroutineCollection<Author> = MongoConfig.getDatabase().getCollection<Author>("authors")

suspend fun findOneByAuthorname(name: String): Author? {
    // 补充return关键字,返回值设为可空类型适配作者不存在的场景
    return collection.findOne(Author::name eq name)
}

suspend fun addBooks(name: String, book: Bookitem) {
    collection.findOneAndUpdate(
        filter = Author::name eq name,
        // 移除多余逗号,直接传入已有的book对象,不需要重复构造
        update = push(Author::books, book)
    )
}
  1. 规范接口传参
    Postman发送请求时使用标准JSON格式,所有key用双引号包裹,正确参数格式为:
    {"title": "book3"}
    

内容的提问来源于stack exchange,提问作者Anandakrishnan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 01:36:14