如何避免在PostgreSQL JSONB中存储Null字段并改造转换器代码
解决PostgreSQL JSONB存储时移除Null字段的可扩展方案
需求说明
向PostgreSQL的JSONB类型存储数据时,需要避免存储值为Null的字段,同时要保留原有代码结构并具备可扩展性。现有转换代码及当前存储的含大量Null字段的JSON示例如下:
现有转换代码
public class JsonbConverter implements AttributeConverter<Map<String, Object>, String> { private final ObjectMapper objectMapper = new ObjectMapper(); @Override public String convertToDatabaseColumn(Map<String, Object> details) { String value = null; try { if (details == null || details.isEmpty()) { return value; } value = objectMapper.writeValueAsString(details); } catch (JsonProcessingException jpe) { log.error("unable to convert details to db {}", details, jpe); } return value; } @Override public Map<String, Object> convertToEntityAttribute(String dbData) { Map<String, Object> value = null; try { if (dbData == null) { return value; } value = objectMapper.readValue(dbData, new TypeReference<Map<String, Object>>() { }); } catch (IOException ioe) { log.error("unable to convert details to entity {}", dbData, ioe); } return value; } }
当前存储的JSON示例
{"categories": [{"name": "News", "label": null, "taxonomyUri": null}, {"name": "UK News", "label": null, "taxonomyUri": null}, {"name": "Crime", "label": null, "taxonomyUri": null}, {"name": "Dogs", "label": null, "taxonomyUri": null}, {"name": "Parenting advice", "label": null, "taxonomyUri": null}, {"name": "Pets", "label": null, "taxonomyUri": null}, {"name": "Police", "label": null, "taxonomyUri": null}]}
解决方案
1. 基础方案:配置ObjectMapper全局忽略Null字段
这是最简洁的实现方式,无需额外遍历处理,直接通过Jackson的配置实现Null字段过滤:
private final ObjectMapper objectMapper = new ObjectMapper() .setSerializationInclusion(JsonInclude.Include.NON_NULL);
修改后,序列化Map为JSON字符串时,所有值为Null的字段会自动被忽略,完全保留原有转换器结构。
2. 可扩展方案:自定义过滤工具类(适配复杂场景)
如果需要更灵活的过滤规则(比如指定某些字段即使为Null也要保留、过滤空字符串等),可以封装工具类处理数据,保持转换器的简洁性:
步骤1:创建Null字段过滤工具类
public class JsonNullFilter { public static Map<String, Object> removeNullFields(Map<String, Object> data) { if (data == null) return null; Map<String, Object> filtered = new HashMap<>(); for (Map.Entry<String, Object> entry : data.entrySet()) { Object value = entry.getValue(); if (value != null) { // 递归处理嵌套的Map或集合 if (value instanceof Map) { filtered.put(entry.getKey(), removeNullFields((Map<String, Object>) value)); } else if (value instanceof Collection) { filtered.put(entry.getKey(), processCollection((Collection<?>) value)); } else { filtered.put(entry.getKey(), value); } } } return filtered; } private static Collection<?> processCollection(Collection<?> collection) { List<Object> processed = new ArrayList<>(); for (Object item : collection) { if (item instanceof Map) { processed.add(removeNullFields((Map<String, Object>) item)); } else { processed.add(item); } } return processed; } }
步骤2:修改转换器集成工具类
@Override public String convertToDatabaseColumn(Map<String, Object> details) { String value = null; try { if (details == null || details.isEmpty()) { return value; } // 先过滤Null字段,再执行序列化 Map<String, Object> filteredDetails = JsonNullFilter.removeNullFields(details); value = objectMapper.writeValueAsString(filteredDetails); } catch (JsonProcessingException jpe) { log.error("unable to convert details to db {}", details, jpe); } return value; }
这种方式可以轻松扩展过滤逻辑,比如在工具类中添加参数控制特定字段的保留规则,或是处理空字符串等其他空值场景。
3. 效果验证
修改后,存储的JSON会自动移除所有Null值字段,最终结果如下:
{"categories": [{"name": "News"}, {"name": "UK News"}, {"name": "Crime"}, {"name": "Dogs"}, {"name": "Parenting advice"}, {"name": "Pets"}, {"name": "Police"}]}
内容的提问来源于stack exchange,提问作者user352290
相关产品推荐
相关产品推荐

