如何在Spring Data MongoDB 中儲存和查詢動態欄位

scard發表於2021-08-16

原文: https://stackoverflow.com/questions/46466562/how-to-save-and-query-dynamic-fields-in-spring-data-mongodb

MongoDB 的一大特點就是所有的記錄都是文件形式,無所謂資料庫欄位,每一條資料都是獨立的。在使用Spring Data MongoDB 時,如果直接用關係型資料庫的用法去套,那會發現很難發揮MongoDB的特性。

我在搜尋如何解決問題的時候,發現了一篇問答正好能解決我的問題,所以我用自己淺薄的英語做個簡單的翻譯。

這是一篇 Stack Overflow 上的問答

有人提了個問題,在使用 Spring Boot 1.4.x 和 Spring Data MongoDB 時,想要動態儲存實體的欄位,而不是直接用一個已經定義好的實體的固定欄位儲存。

雖然可以在實體類內部建立一個Map屬性來存動態欄位,但提問者想要的是所有的欄位都處於MongoDB文件的頂層,不想要在欄位裡面還存在額外的資料結構。

提問者嘗試後發現,直接將實體類繼承自HashMap是不行的

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!

問題的優質回答:

先講結論:

  • 不能直接使用或者繼承java集合類作為實體類
  • Spring Data MongoDB 的 Repositories 不能實現上面的需求
  • 使用 DBObject 類配合 MongoTemplate 才能實現

分析;
Spring Data Repositories 是MongoDB用在設計好的資料結構中的持久層,為了配合物件導向設計使用。Spring Data 在分析檢查類的時候,會將集合和Map型別的類剔除出去。

Repository 的查詢方法雖然也可以實現動態欄位查詢,但不是主流的用法。

public interface EntryRepository extends MongoRepository<Entry, String> {
    @Query("{ ?0 : ?1 }")
    Entry findByDynamicField(String field, Object value);
}

這個方法不提供任何型別安全檢查,只是簡陋地為欄位提供一個別名。

最好使用 DBObject 配合 MongoTemplate 的Query方法使用。

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

DBObject 能在不定義資料結構的情況下,直接對映MongoDB文件的結果。可以直接使用 DBObject 類結合 MongoTemplate 進行增刪改查的操作。

我個人的嘗試

應該是 Spring Data MongoDB 相關團隊瞭解到了這個需求,目前的spring-data-mongodb 3.1.6 版本中,MongoTemplate 類的insert和save方法都可以直接操作Map資料,甚至對於任意實體類都能隨意操作,只要指定 collection 名稱就行。

HashMap object = new HashMap();
object.put("file_name", "object");
object.put("md5", "1827391");
HashMap aa = template.save(object, "file");

Student student = new Student("小明", "希望小學");
Student bb = template.save(student, "file");

以上程式碼均能正常執行,資料能準確存入資料庫。

相關文章