MongoDB使用ObjectId作为主键时id序列化问题

506 阅读2分钟

在使用MongoDB时,如果使用了ObjectId作为主键策略,那么在数据传输时会涉及到一个问题,就是序列化,默认情况下会返回时间戳,非常不利于展示和传递,展示如下:

{
    "id":{
        "timestamp":1688365000,
        "date":"2023-07-03T06:16:40.000+00:00"
    },
    ...
}

MongoDB主键,由时间戳(四个字节)、机器ID(三个字节)、进程ID(两个字节)、计数器(三个字节)四部分构成

64a267c8
73dbd0
58f5
dd2c87

MongoDB中创建文档时可以指定文档主键,默认不指定时会生成一个名为_id(ObjectId:默认主键生成策略,是 MongoDB 提供的默认生成方式,使用 12 字节的 BSON 类型存储主键,包含了时间戳、机器标识、进程标识和随机数)的字段作为主键,也可以使用注解@Id指定主键,可以作用于字符串、数字、对象等,除了使用@Id指定主键还可以自定义主键,例如自增序列等,但是建议使用默认生成策略。

序列化解决方案

自定义序列化策略,将ObjectId转换为String,ObjectIdSerializer.java

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.bson.types.ObjectId;
​
import java.io.IOException;
​
public class ObjectIdSerializer extends JsonSerializer<ObjectId> {
 
    @Override
    public void serialize(ObjectId objectId, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString(objectId.toHexString());
    }
}

反序列化解决方案

自定义反序列化策略,将String转换为ObjectIdObjectIdDeserializer.java,需要注意,对长度有限制,必须时标准长度。

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import org.bson.types.ObjectId;
​
import java.io.IOException;
import java.util.regex.Pattern;
​
public class ObjectIdDeserializer extends JsonDeserializer<ObjectId> {
    private static final Pattern HEX_PATTERN = Pattern.compile("^[0-9a-fA-F]{24}$");
    @Override
    public ObjectId deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        String hexString = jsonParser.readValueAs(String.class);
        if (!HEX_PATTERN.matcher(hexString).matches()) {
            throw new JsonMappingException("Invalid ObjectId: " + hexString);
        }
        return new ObjectId(hexString);
    }
}

使用

@Document(collection = "Books")
@Data
public class Book {
    @JsonSerialize(using = ObjectIdSerializer.class)
    @JsonDeserialize(using = ObjectIdDeserializer.class)
    @Id
    private ObjectId id;
}

结果

{
    "id":"64a267c873dbd058f5dd2c87",
    ...
}

总结

以上就是使用ObjectId作为主键时序列化解决方案,如果有更好的解决方案欢迎探讨。