
当 entity 中使用 @type(type = "jsonb") 注解将复杂对象序列化为 jsonb 存储时,modelmapper 默认无法自动反序列化为 dto 对象,导致目标字段为 null;需结合 jackson 手动解析 jsonb 字段。
当 entity 中使用 @type(type = "jsonb") 注解将复杂对象序列化为 jsonb 存储时,modelmapper 默认无法自动反序列化为 dto 对象,导致目标字段为 null;需结合 jackson 手动解析 jsonb 字段。
在基于 Spring Boot 与 Hibernate 的项目中,常通过 @Type(type = "jsonb")(如使用 hibernate-types 库)将嵌套对象(如 AdditionalProperty)以 JSONB 格式持久化到 PostgreSQL。此时,Entity 中的对应字段类型为 Object 或 String,而 DTO/Bean 类中则定义为结构化类型(如 AdditionalPropertyDto)。ModelMapper 默认仅执行浅层属性名匹配与类型兼容转换,不具备 JSON 反序列化能力,因此对 Object 类型的 additionalProperty 字段无法自动映射,结果为 null。
✅ 正确做法:分离处理 JSONB 字段
应绕过 ModelMapper 对该字段的默认映射,改用 Jackson 的 ObjectMapper 显式完成反序列化:
public SettingDto apply(SettingEntity settingEntity) {
// 1. 使用 ModelMapper 映射基础字段(如 UUID)
SettingDto settingDto = mapper.map(settingEntity, SettingDto.class);
// 2. 单独处理 JSONB 字段:先序列化为 JSON 字符串,再反序列化为目标 DTO
ObjectMapper jsonMapper = new ObjectMapper();
try {
// 假设 getAdditionalProperty() 返回的是可序列化的 Map/String/POJO(实际取决于 hibernate-types 的配置)
Object rawValue = settingEntity.getAdditionalProperty();
if (rawValue != null) {
String jsonStr = jsonMapper.writeValueAsString(rawValue);
AdditionalPropertyDto dto = jsonMapper.readValue(jsonStr, AdditionalPropertyDto.class);
settingDto.setAdditionalProperty(dto);
}
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Failed to deserialize additionalProperty from JSONB", e);
}
return settingDto;
}
⚠️ 注意事项:
Json Schema Toolkit下载使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
- 确保 settingEntity.getAdditionalProperty() 返回值可被 Jackson 序列化(常见为 Map
、String 或自定义 POJO); - 若 additionalProperty 在数据库中为空(NULL),getAdditionalProperty() 可能返回 null,需判空避免 NPE;
- 推荐将 ObjectMapper 实例注入或作为 static final 成员复用,避免频繁创建实例影响性能;
- 如需双向映射(DTO → Entity),同样需在 SettingEntity 构建时手动调用 jsonMapper.writeValueAsString(...) 转为 String 或 Object。
? 进阶建议:自定义 ModelMapper Converter(推荐)
为保持映射逻辑集中、提升可维护性,可注册专用 Converter:
Converter<object additionalpropertydto> jsonbToDtoConverter =
ctx -> {
Object src = ctx.getSource();
if (src == null) return null;
try {
String json = new ObjectMapper().writeValueAsString(src);
return new ObjectMapper().readValue(json, AdditionalPropertyDto.class);
} catch (JsonProcessingException e) {
throw new RuntimeException("Failed to convert JSONB to AdditionalPropertyDto", e);
}
};
mapper.createTypeMap(SettingEntity.class, SettingDto.class)
.addMapping(SettingEntity::getAdditionalProperty,
(dto, value) -> dto.setAdditionalProperty(value));
mapper.addConverter(jsonbToDtoConverter);</object>
如此,mapper.map(entity, SettingDto.class) 即可全自动完成全字段映射,无需额外手动干预。











