SpringBoot+MyBatis Plus对Map中Date格式转换的处理

在 SpringBoot 项目中, 如何统一 JSON 格式化中的日期格式
问题现在的关系型数据库例如PostgreSQL/MySQL, 都已经对 JSON 类型提供相当丰富的功能, 项目中对于不需要检索但是又需要结构化的存储, 会在数据库中产生很多 JSON 类型的字段, 与 Jackson 做对象的序列化和反序列化配合非常方便.
如果 JSON 都是类定义的, 这个序列化和反序列化就非常透明 -- 不需要任何干预, 写进去是什么, 读出来就是什么. 但是如果 JSON 在 Java 代码中是定义为一个 Map, 例如 Map<String, Object> 那么就有问题了, 对于 Date 类型的数据, 在存入之前是 Date, 取出来之后就变成 Long 了.

  1. SomePO po = new SomePO();

  2. //...

  3. Map<String, Object> map = new HashMap<>();

  4. map.put("k1", new Date());

  5. po.setProperties(map);

  6. //...

  7. mapper.insert(po);

  8. //...

  9. SomePO dummy = mapper.select(po.id);

  10. // 这里的k1已经变成了 Long 类型

  11. Object k1 = dummy.getProperties().get("k1");

原因不管是使用原生的 MyBatis 还是包装后的 MyBatis Plus, 在对 JSON 类型字段进行序列化和反序列化时, 都需要借助类型判断, 调用对应的处理逻辑, 大部分情况, 使用的是默认的 Jackson 的 ObjectMapper, 而 ObjectMapper 对 Date 类型默认的序列化方式就是取时间戳, 对于早于1970年之前的日期, 生成的是一个负的长整数, 对于1970年之后的日期, 生成的是一个正的长整数.
查看 ObjectMapper 的源码, 可以看到其对Date格式的序列化和反序列化方式设置于_serializationConfig 和 _deserializationConfig 这两个成员变量中, 可以通过 setDateFormat() 进行修改
  1. public class ObjectMapper extends ObjectCodec implements Versioned, Serializable {

  2.    //...

  3.    protected SerializationConfig _serializationConfig;

  4.    protected DeserializationConfig _deserializationConfig;

  5.    //...

  6.    public ObjectMapper setDateFormat(DateFormat dateFormat) {

  7.        this._deserializationConfig = (DeserializationConfig)this._deserializationConfig.with(dateFormat);

  8.        this._serializationConfig = this._serializationConfig.with(dateFormat);

  9.        return this;

  10.    }

  11.    public DateFormat getDateFormat() {

  12.        return this._serializationConfig.getDateFormat();

  13.    }

  14. }

默认的序列化反序列化选项, 使用了一个常量 WRITE_DATES_AS_TIMESTAMPS, 在类 SerializationConfig 中进行判断, 未指定时使用的是时间戳
  1. public SerializationConfig with(DateFormat df) {

  2. SerializationConfig cfg = (SerializationConfig)super.with(df);

  3. return df == null ? cfg.with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) : cfg.without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

  4. }

实际的转换工作在 SerializerProvider 类中, 转换方法为

推荐阅读