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

MongoDB更新含抽象类的集合时触发MappingInstantiationException问题

问题解决:MongoDB更新抽象子类包装对象时无法实例化抽象类报错

错误信息

"Failed to instantiate com.models.Task using constructor public com.models.Task() with arguments "

类定义

WrapperTask

@Data
@Document(collection="tasks")
public class WrapperTask {
    @Id
    private ObjectId id;
    @BsonProperty(useDiscriminator = true)
    private Task task;

    public WrapperTask() {
    }

    public String getId() {
        return Utils.ObjectId(this.id);
    }
}

抽象类Task

@Data
@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({ 
    @Type(value = UploadTask.class, name = "uploadTask"),
    //more classes
})
@BsonDiscriminator
public abstract class Task {
    private TaskStatus status;
    public int totalCount;
    public int completedCount;

    public Task() {
        this.status = TaskStatus.NEW;
    }
}

子类UploadTask

@Data
@EqualsAndHashCode(callSuper = true)
@Document(collection = "tasks")
@TypeAlias("uploadTask")
@BsonDiscriminator
public class UploadTask extends Task {

    private LinkedList<Document> documents = new LinkedList<>();

    public UploadTask(int totalCount) {
        super();
        this.totalCount = totalCount;
    }

    public UploadTask() {
        super();
    }
}

更新逻辑

Query query = new Query(Criteria.where("_id").is(taskId));
Update update = new Update();
update = update.set("task.status", "some status");
    
if (taskType == UploadTask.class) {
    Criteria criteria = Criteria.where("document.index").is(index);           
    update = update.set("task.documents.$[document].status", "some status");
    update = update.set("task.documents.$[document].statusMessage", "some message").filterArray(criteria);    
}

mongoOperations.findAndModify(query, update, options().upsert(false), WrapperTask.class);

解决方案

核心原因

报错本质是findAndModify方法会返回更新后的文档,Spring Data MongoDB在反序列化文档时,试图实例化抽象类Task——但抽象类无法被实例化。问题出在反序列化阶段,而非更新操作本身。

方案1:改用不返回结果的更新方法(推荐)

如果业务不需要获取更新后的对象,直接使用updateFirst替代findAndModify,避免触发反序列化流程:

mongoOperations.updateFirst(query, update, WrapperTask.class);

方案2:修复反序列化的鉴别器配置(需返回对象时使用)

如果必须获取更新后的对象,需要确保Spring Data MongoDB能根据鉴别器字段正确识别子类:

  1. 修正抽象类Task的鉴别器配置:显式指定鉴别器字段名,与JSON序列化的type字段保持一致:
    @BsonDiscriminator(key = "type") // 明确指定鉴别器字段为"type"
    @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
    @JsonSubTypes({ 
        @Type(value = UploadTask.class, name = "uploadTask"),
        // 其他子类
    })
    public abstract class Task {
        // ... 原有代码
    }
    
  2. 移除子类的@Document注解:子类是嵌入在WrapperTask中的对象,不需要单独标注@Document:
    @Data
    @EqualsAndHashCode(callSuper = true)
    @TypeAlias("uploadTask")
    @BsonDiscriminator
    public class UploadTask extends Task {
        // ... 原有代码
    }
    
  3. 验证MongoDB文档结构:确保集合中task字段下存在type字段(如"type": "uploadTask"),Spring会根据该字段实例化对应的子类而非抽象类。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 13:35:31