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

如何使用ModelMapper处理深度继承对象并保留type字段?

解决ModelMapper映射JPA继承实体时type字段缺失的问题

这个问题我之前也碰到过,核心原因很明确:@JsonTypeInfo是Jackson用来序列化/反序列化时识别子类的注解,它生成的type字段是Jackson动态注入的,并不是你的实体类A/AA/AB中定义的显式属性。而ModelMapper的映射逻辑是基于实体类的getter/setter或者字段来工作的,所以默认情况下它不会把这个Jackson生成的type字段映射到DTO里。

下面给你几个实用的解决方案,你可以根据自己的代码场景选择:

方案一:给抽象实体类添加显式的type属性(推荐)

这种方式最优雅,既符合Jackson的序列化逻辑,也能让ModelMapper正常识别映射,同时不会影响JPA的实体映射。

首先在抽象父类A中添加一个抽象的getType()方法,并用@JsonProperty标记,让Jackson也能识别这个方法返回值作为type字段:

@MappedSuperclass // 或者根据你的实际情况用@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) // 这里假设是单表继承,按需调整
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
    @JsonSubTypes.Type(value = AA.class, name = "AA"),
    @JsonSubTypes.Type(value = AB.class, name = "AB")
})
public abstract class A {
    // 你的其他实体属性...

    // 显式定义type的getter,由子类实现返回对应的类型标识
    @JsonProperty("type")
    public abstract String getType();
}

然后在子类AA、AB中实现这个方法,返回对应的类型名称:

@Entity
public class AA extends A {
    private User user;

    @Override
    public String getType() {
        return "AA"; // 和@JsonSubTypes中定义的name一致
    }

    // User属性的getter/setter...
}

@Entity
public class AB extends A {
    private String name;

    @Override
    public String getType() {
        return "AB"; // 和@JsonSubTypes中定义的name一致
    }

    // name属性的getter/setter...
}

接下来给对应的DTO类也添加上type属性和对应的getter/setter:

public abstract class ADto {
    private String type;

    public String getType() { return type; }
    public void setType(String type) { this.type = type; }
}

public class AADto extends ADto {
    private UserDto user; // 假设UserDto是User对应的DTO类

    // UserDto属性的getter/setter...
}

public class ABDto extends ADto {
    private String name;

    // name属性的getter/setter...
}

这样配置后,ModelMapper在映射的时候,就会自动把实体类getType()的返回值映射到DTO的type字段里,完全不需要额外的配置。

方案二:自定义ModelMapper的TypeMap转换器

如果不想修改实体类的代码,可以给ModelMapper配置自定义的类型映射规则,手动设置type字段的值。

示例配置代码如下:

// 初始化ModelMapper
ModelMapper modelMapper = new ModelMapper();

// 配置AA到AADto的映射,手动设置type字段
modelMapper.createTypeMap(AA.class, AADto.class)
    .addMapping(source -> "AA", AADto::setType);

// 配置AB到ABDto的映射,手动设置type字段
modelMapper.createTypeMap(AB.class, ABDto.class)
    .addMapping(source -> "AB", ABDto::setType);

// 如果需要支持父类A到ADto的映射,也可以添加通用逻辑
modelMapper.createTypeMap(A.class, ADto.class)
    .addMapping(source -> {
        if (source instanceof AA) {
            return "AA";
        } else if (source instanceof AB) {
            return "AB";
        }
        return null; // 处理其他未知子类的情况
    }, ADto::setType);

当你调用modelMapper.map(abInstance, ABDto.class)时,ModelMapper就会按照你配置的规则,把type字段设置为"AB"。

方案三:借助Jackson先序列化再反序列化(应急方案)

如果上面两种方案都不适合,你可以先利用Jackson把实体序列化为包含type字段的JSON字符串,再反序列化为DTO。不过这种方式会绕开ModelMapper的映射逻辑,可能丢失你之前配置的其他映射规则,只适合简单场景:

ObjectMapper objectMapper = new ObjectMapper();
// 先把实体序列化为带type字段的JSON
String jsonStr = objectMapper.writeValueAsString(abInstance);
// 再把JSON反序列化为DTO
ABDto abDto = objectMapper.readValue(jsonStr, ABDto.class);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:58:08