当前使用版本(必填,否则不予处理)

implementation 'com.baomidou:mybatis-plus-boot-starter:3.4.3.4'

该问题是如何引起的?(确定最新版也有问题再提!!!)

实体类 Template.java

@TableName(value = "t_template", autoResultMap = true)
public class Template implements Serializable {
    // 其他字段省略
    @TableField(value = "F_config", typeHandler = PojoTypeHandler.class)
    private Map<String, MyPojo> config;
}

DTO 类,MyPojo.java

public class MyPojo {
    private String foo;
    private String bar;
    private Map<String, MyPojo> subLayer;
}

自定义 TypeHandler PojoTypeHandler.java

@MappedTypes({Object.class})
@MappedJdbcTypes(JdbcType.VARCHAR)
public class PojoTypeHandler extends BaseTypeHandler<Object> {
    private static final ObjectMapper mapper = new ObjectMapper();

    static {
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.registerModule(new JavaTimeModule());
    }

    private Class<?> clazz;

    public PojoTypeHandler(Class<?> clazz) {
        if (clazz == null) throw new IllegalArgumentException("Type argument cannot be null");
        this.clazz = clazz;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, this.toJson(parameter));
    }

    @Override
    public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return this.toObject(rs.getString(columnName), clazz);
    }

    @Override
    public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return this.toObject(rs.getString(columnIndex), clazz);
    }

    @Override
    public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return this.toObject(cs.getString(columnIndex), clazz);
    }

    private String toJson(Object object) {
        try {
            return mapper.writeValueAsString(object);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private Object toObject(String content, Class<?> clazz) {
        if (content != null && !content.isEmpty()) {
            try {
                return mapper.readValue(content, clazz);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } else {
            return null;
        }
    }
}

数据库 F_config 字段为 json 类型

重现步骤(如果有就写完整)

配置代码结构如上,构造一个 Template json 对象,用 save 方法可以正常存入数据库中,但是使用 getById 从数据库中查询出来的时候,丢失了 MyPojo 的类型信息。

期望返回中的 config 字段为 Map 类型,实际返回为 LinkedHashMap ,如下图,图中的 PlaybackConfig 即是这里的 MyPojo

类型定义 MyBatis-Plus 嵌套 DTO 类的 Map 字段,使用自定义的 typehandler 映射,可以正常插入,但是查询出来的类型信息丢失了

实际返回 MyBatis-Plus 嵌套 DTO 类的 Map 字段,使用自定义的 typehandler 映射,可以正常插入,但是查询出来的类型信息丢失了

报错信息

没有报错信息

Comment From: mabyoung

@wangx1ng 您好,有个疑问,如果返回的是 LinkedHashMap,使用Map 来接收,不会报类型转换错误吗。 方便提供一下完整的demo,或者贴一下PlaybackConfig定义、完整的detailedConfig返回值、调用示例等相关信息吗

Comment From: miemieYaho

泛型信息需要你自己维护,

Comment From: wangx1ng

@mabyoung 你好,把包含 detailedConfig 字段的实体查出来的时候,不会报任何错误,但是将 detailedConfig 取出来赋值给 Map 的时候会报类型转换错误,如样例中所示,Playbackconfig 的定义中,除了又有一个嵌套的 Map 类型的字段,没有任何特别的。

Comment From: wangx1ng

@miemieYaho 你好,请问,”需要我自己维护泛型信息“的含义是指: 在使用通用的 typehandler (@MappedTypes({Object.class}) )查出实体后,需要在客户代码中做手动类型转换吗?

目前我为每个需要自定义 typehanler 的字段都编写了具体类型的 typehandler,想知道能否多个需要自定义 typehandler 的字段都可以共用一个 PojoTypeHandler