最近,我有機會使用MongoDB (與humongoous一樣),這是一個用C ++編寫的面向文檔的數據庫。 它是存儲結構可能不同的文檔的理想選擇,它使用類似于JSON的格式,這意味著它支持與JSON類似的數據類型和結構。 它提供了豐富而簡單的查詢語言,仍然使我們能夠為快速檢索的關鍵字段建立索引。 文檔存儲在集合中,這有效地限制了查詢的范圍,但是對于可以存儲在集合中的異構數據的類型實際上沒有任何限制。 如果您需要學習MongoDB的基礎知識,則MongoDB站點上的文檔不錯。
Java中的MongoDB
Mongo Java驅動程序基本上將所有文檔公開為鍵值對(顯示為map和值列表)。 這意味著,如果必須使用Java存儲或檢索文檔,則必須將POJO映射到該映射接口。 以下是通常需要編寫的代碼類型示例,以將文檔從Java保存到MongoDB:
BasicDBObject doc = new BasicDBObject();doc.put("user", "carfey");BasicDBObject post1 = new BasicDBObject();
post1.put("subject", "spam & eggs");
post1.put("message", "first!");BasicDBObject post2 = new BasicDBObject();
post2.put("subject", "sorry about the spam");doc.put("posts", Arrays.asList(post1, post2));coll.insert(doc);
對于某些用例來說這很好,但是對于其他用例來說,最好有一個庫來為我們做繁瑣的工作。
輸入Morphia
Morphia是一個Java庫,其行為類似于MongoDB的ORM –它使我們能夠將Java對象無縫映射到MongoDB數據存儲。 它使用注釋來指示類存儲在哪個集合中,甚至支持多態集合。 最好的功能之一是,它可以用于基于集合或屬性級別的注釋自動為集合建立索引。 這極大地簡化了部署和推出變更。
我提到了同一集合中多種類型的多態存儲。 這可以幫助我們映射不同的文檔結構,并在某種程度上像Hibernate之類的鑒別器 。
這是一個示例,說明如何定義支持多態存儲和查詢的實體。 Return類是Order的子級,并引用相同的collection-name。 查詢或存儲數據時,Morphia將自動處理多態性。 對于非多態的集合,您幾乎會做同樣的事情,但是不會有多個使用相同集合名稱的類。
注意:這實際上不是我建議存儲在MongoDB中的數據類型的示例,因為它更適合于傳統的RDBMS,但是很好地展示了原理。
@Entity("orders") // store in the orders collection
@Indexes({ @Index("-createdDate, cancelled") }) // multi-column index
public class Order {@Id private ObjectId id; // always required@Indexedprivate String orderId;@Embedded // let's us embed a complex objectprivate Person person;@Embeddedprivate List<Item> items;private Date createdDate;private boolean cancelled;// .. getters and setters aren't strictly required// for mapping, but they would be here
}@Entity("orders") // note the same collection name
public class Return extends Order {// maintain legacy name but name it nicely in mongodb@Indexed@Property("rmaNumber") private String rma;private Date approvedDate;private Date returnDate;
}
現在,在下面,我將演示如何查詢那些多態實例。 請注意,存儲數據時我們不必做任何特殊的事情。 MongoDB將className屬性與文檔一起存儲,因此它可以支持多態獲取和查詢。 按照上面的示例,我可以通過執行以下查詢所有訂單類型:
// ds is a Datastore instance
Query<Order> q = ds.createQuery(Order.class).filter("createdDate >=", date);
List<Order> ordersAndReturns = q.asList();// and returns only
Query<Return> rq = ds.createQuery(Return.class).filter("createdDate >=", date);
List<Return> returnsOnly = rq.asList();
如果我只想查詢普通訂單,則必須使用className過濾器,如下所示。 這使我們能夠有效地禁用多態行為并將結果限制為單個目標類型。
Query<Order> q = ds.createQuery(Order.class).filter("createdDate >=", cutOffDate).filter("className", Order.class.getName());List<Order> ordersOnly = q.asList();
Morphia當前使用className屬性來過濾結果,但是在將來的某個時候可能會使用一個鑒別符列,在這種情況下,您可能不得不過濾該值。
注意:在應用程序啟動期間的某個時刻,您需要注冊映射的類,以便Morphia可以使用它們。 詳細信息請參見此處。 下面是一個簡單的示例。
Morphia m = ...
Datastore ds = ...m.map(MyEntity.class);
ds.ensureIndexes(); //creates all defined with @Indexed
ds.ensureCaps(); //creates all collections for @Entity(cap=@CappedAt(...))
文檔結構變化問題
MongoDB中面向文檔的存儲的一個不錯的功能之一是,它允許您將具有不同結構的文檔存儲在同一集合中,但是仍然可以執行結構化查詢和索引值以獲得良好的性能。
不幸的是,Morphia并不真正喜歡這種方式,因為它旨在將所有存儲的屬性映射到已知的POJO字段。 目前,我發現有兩種方法可以讓我們處理此問題。
第一個是禁用查詢驗證。 這意味著將刪除數據存儲中存在但無法映射到我們的POJO的值,而不是將其炸掉:
// drop unmapped fields quietly
Query<Order> q = ds.createQuery(Order.class).disableValidation();
另一個選擇是使用地圖將所有非結構化內容存儲在單個存儲桶元素下。 它可以包含MongoDB驅動程序支持的任何基本類型,包括列表和地圖,但不包含復雜對象,除非您已向Morphia注冊了轉換器(例如morphia.getMapper()。getConverters()。addConverter(new MyCustomTypeConverter()))。
@Entity("orders")
public class Order {// .. our base attributes hereprivate Map<String, Object> attributes; // bucket for everything else (
}
請注意,Morphia可能會在啟動時抱怨它無法驗證字段(因為泛型聲明不嚴格),但是從當前發行版(0.99)開始,它將正常工作并可以正常存儲任何屬性并檢索它們作為值的映射和列表。
注意:當它從檢索到的文檔中填充一個松散類型的映射時,它將使用基本的MongoDB Java驅動程序類型BasicDBObject和BasicDBList。 它們分別實現Map和List,因此它們將與您期望的一樣工作,只是它們與您可能存儲的任何輸入映射或列表都不相等(即使結構和內容看起來相等)。 如果要避免這種情況,可以使用@PostLoad注釋來注釋一個方法,該方法可以在文檔加載后對JDK映射和列表執行規范化。 我個人這樣做是為了確保始終看到MongoDB文檔的一致視圖,無論它們是從集合中提取還是尚未持久化。
參考: Carfey Software博客上的JCG合作伙伴提供的將MongoDB與Morphia結合使用的信息 。
相關文章 :
- Cassandra,MongoDB,CouchDB,Redis,Riak,HBase比較
- Java Code Geeks Andygene Web原型
- Java教程和Android教程列表
- 每個程序員或架構師都應該知道的9 + 7件事
翻譯自: https://www.javacodegeeks.com/2011/11/using-mongodb-with-morphia.html