最近,我有机会使用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