【问题标题】:How to save and query dynamic fields in Spring Data MongoDB?如何在 Spring Data MongoDB 中保存和查询动态字段?
【发布时间】:2018-03-10 01:04:34
【问题描述】:

我正在使用 Spring boot 1.4.x 分支和 Spring Data MongoDB。

我想从HashMap 扩展一个 Pojo,使其能够动态保存新属性。

我知道我可以在 Entry 类中创建一个Map<String, Object> 属性以在其中保存我的动态值,但我不想有内部结构。我的目标是让根的入口类中的所有字段都像这样序列化它:

{
   "id":"12334234234",
   "dynamicField1": "dynamicValue1",
   "dynamicField2": "dynamicValue2"
}

所以我创建了这个 Entry 类:

@Document
public class Entry extends HashMap<String, Object> {

    @Id
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

还有这样的存储库:

public interface EntryRepository extends MongoRepository<Entry, String> {
}

当我启动我的应用程序时出现此错误:

Error creating bean with name 'entryRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.model.MappingException: Could not lookup mapping metadata for domain class java.util.HashMap!

有什么想法吗?

【问题讨论】:

  • Franck - 已回答您的问题,如果对您的理解有所帮助,请通过将答案标记为正确来确认 :)
  • 你试过扩展 BasicDBObject 吗?类似public class Entry extends BasicDBObject
  • 是的,同样的错误。

标签: java spring mongodb spring-data


【解决方案1】:

TL;博士;

  1. 不要将 Java 集合/映射类型用作实体的基类。
  2. 存储库不是满足您要求的正确工具。
  3. 如果您需要动态顶级属性,请使用 DBObjectMongoTemplate

说明

Spring Data Repositories repositories in the DDD sense 充当您定义明确的聚合的持久性网关。他们检查域类以派生适当的查询。 Spring Data 将集合和映射类型排除在实体分析之外,这就是从 Map 扩展实体失败的原因。

动态属性的存储库查询方法是可能的,但这不是主要用例。您必须使用 SpEL 查询来表达您的查询:

public interface EntryRepository extends MongoRepository<Entry, String> {

    @Query("{ ?0 : ?1 }")
    Entry findByDynamicField(String field, Object value);
}

此方法不会为您提供有关谓词值的任何类型安全性,并且只会为适当的单独查询提供丑陋的别名。

宁可直接使用DBObjectMongoTemplate 及其查询方法:

List<DBObject> result = template.find(new Query(Criteria.where("your_dynamic_field")
                                          .is(theQueryValue)), DBObject.class);

DBObject 是一个Map,它使您可以完全访问属性,而无需强制执行预定义的结构。您可以通过模板 API 创建、读取、更新和删除 DBObjects 对象。

最后一件事

如果您的聚合根声明了一些静态属性,您可以使用 Map 在嵌套级别声明动态属性:

@Document
public class Data {

    @Id
    private String id;
    private Map<String, Object> details;
}

【讨论】:

  • 这是一个很好的解释,所以我赞成。最后,我在我的 POJO 中制作了一个 Map,在 jackson 中制作了序列化器/反序列化器,以将详细信息内容放在根级别。
【解决方案2】:

这里我们可以使用JSONObject来实现

实体会是这样的

@Document
public class Data {
    @Id
    private String id;
    private JSONObject details;
    //getters and setters
}

POJO 会是这样的

public class DataDTO {
    private String id;
    private JSONObject details;
        //getters and setters
}

服务中

Data formData = new Data();
JSONObject details = dataDTO.getDetails();
details.put("dynamicField1", "dynamicValue1");
details.put("dynamicField2", "dynamicValue2");
formData.setDetails(details);
mongoTemplate.save(formData );

我已按照我的业务完成,请参考此代码并按照您的方式进行操作。这有帮助吗?

【讨论】:

  • 如果我这样做,是否总是可以查询字段?$
  • 是的,你也没有内部结构(如果你使用上面的代码格式)。您可以轻松查询字段。
  • @jai - 如何取回值并将其映射到 pojo?
猜你喜欢
  • 2018-11-25
  • 1970-01-01
  • 2016-01-08
  • 2016-07-10
  • 2013-08-24
  • 1970-01-01
  • 2015-11-13
  • 1970-01-01
  • 2014-08-25
相关资源
最近更新 更多