Spring Data Mongo中子对象的ID字段处理

Posted

技术标签:

【中文标题】Spring Data Mongo中子对象的ID字段处理【英文标题】:Id field handling in Spring Data Mongo for child objects 【发布时间】:2017-01-06 01:06:09 【问题描述】:

我一直在 Spring Boot 中使用 Spring Data MongoDB 项目,我看到我不清楚的行为。我知道 id 字段将根据 http://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mapping.conventions.id-field 转到 Mongo 存储库中的 _id。我的问题是它似乎也发生在看起来不正确的子实体上。

例如,我有这些类(为简洁起见,省略了 setter 和 getter):

public class MessageBuild 
    @Id
    private String id;

    private String name;
    private TopLevelMessage.MessageType messageType;
    private TopLevelMessage message;


public interface TopLevelMessage 
    public enum MessageType 
        MapData
    


public class MapData implements TopLevelMessage 
    private String layerType;
    private Vector<Intersection> intersections;
    private Vector<RoadSegment> roadSegments;
    

public class RoadSegment 
    private int id;
    private String name;
    private Double laneWidth;

然后我使用它创建一个对象图我使用适当的 MongoRepository 类来保存我最终得到了一个这样的示例文档(省略了 _class):


    "_id" : ObjectId("57c0c05568a6c4941830a626"),
    "_class" : "com.etranssystems.coreobjects.persistable.MessageBuild",
    "name" : "TestMessage",
    "messageType" : "MapData",
    "message" : 
        "layerType" : "IntersectionData",
        "roadSegments" : [ 
            
                "_id" : 2001,
                "name" : "Road Segment 1",
                "laneWidth" : 3.3
            
        ]
    

在这种情况下,具有名为 id 的字段的子对象在 MongoDB 存储库中将其映射转换为 _id。不是世界末日,虽然没有预料到。现在最大的问题是 REST MVC 公开了 _id 字段,不会从查询中返回。我试图在我的 RepositoryRestConfigurerAdapter 中为这个类设置exposeIdsFor,它公开了***文档的id,而不是子文档。

所以围绕我的 2 个问题/问题盘旋:

为什么子对象字段映射到_id?我的理解是,这应该只发生在顶层,因为底层的东西本身并不是真正的文档。 如果要映射字段名称,公开 id 字段的配置是否应该适用于文档中的子对象?

【问题讨论】:

【参考方案1】:

我认为 RoadSegment 不包含 getId() 是错误的吗?来自Spring's documentation:

将映射没有注释但命名为 id 的属性或字段 到 _id 字段。

我相信当 Spring Data 找到一个 id 字段时,它甚至对嵌套类也会这样做。您可以添加getId(),以便将字段命名为id 或使用@Field 进行注释:

public class RoadSegment 
    @Field("id")
    private int id;

    private String name;
    private Double laneWidth;

我同意这种 id/_id 的自动转换在我看来只能在顶层完成。

但是,Spring Data Mongo 转换的编码方式,所有java ojects都要经过exact相同的代码转换成json(包括顶层对象和嵌套对象):

public class MappingMongoConverter 
...
    protected void writeInternal(Object obj, final DBObject dbo, MongoPersistentEntity<?> entity) 
        ...
        if (!dbo.containsField("_id") && null != idProperty) 
        try 
            Object id = accessor.getProperty(idProperty);
                dbo.put("_id", idMapper.convertId(id));
             catch (ConversionException ignored) 
        

        ...
        if (!conversions.isSimpleType(propertyObj.getClass())) 
            // The following line recursively calls writeInternal with the nested object
            writePropertyInternal(propertyObj, dbo, prop); 
         else 
            writeSimpleInternal(propertyObj, dbo, prop);
        

writeInternal 在***对象上调用,然后为每个子对象(又名 SimpleTypes)递归调用。所以他们都经历了相同的添加_id的逻辑。

也许我们应该这样阅读 Spring 的文档:

Mongo 对 Mongo 文档的限制:

MongoDB 要求您为所有 文档 提供一个 _id 字段。如果你 不提供一个驱动程序将分配一个带有生成的 ObjectId 价值。

Spring Data 对 java 类的限制:

如果 Java 类中没有上述指定的字段或属性 那么驱动程序将生成一个隐式 _id 文件,但不会 映射到 Java 类的属性或字段。

【讨论】:

对我来说,这只对***文档有意义。为什么只是文档一部分的子实体的值会被翻译? 文档还说“MongoDB 要求所有文档都有一个 _id 字段”,并且子实体不是文档。它们是插入的文档的一部分。 你对我投了反对票(没有难过的感觉,我同意我没有回答“为什么”),但为了记录,我同意这仅在***别才有意义。这很可能是一个错误? 更新答案,它没有解释为什么这是一个好的行为。只是为什么会这样。 是的,这似乎是一个错误,我真的希望 Spring Data 团队的某个人能够加入并添加一些关于为什么这种行为有意义的见解。我想我有一个很好的主意,这是基于我所看到的行为。很抱歉投了反对票,我想我很暴躁,因为它没有深入问题的根源。关于为什么第二部分不起作用的任何想法?奇怪的是,子实体的 id 也被隐藏了,但是暴露设置并没有改变它。

以上是关于Spring Data Mongo中子对象的ID字段处理的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有 ObjectId 的情况下将 Mongo 与 Spring Data 一起使用

如何使用 mongo 搜索集合并返回子文档列表(Spring-data-mongo)

无法创建对具有 NULL id mongo hibernate-mongo 和 spring boot 的对象的引用

使用spring data mongo 多数据源如何切换 使用注解形式 不影响使用 MongoRepository 接口方案。

@Indexed 嵌套属性在 mongo 的 Spring-data 中不起作用

将 mongo 查询转换为 spring-data-mongo 查询