当今NoSQL领域中有很多有力的竞争者通过多种方式来处理v量数据问题。其中重要的解决Ҏ(gu)之一是MongoDB。MongoDB是面向文档的q构化存储Ҏ(gu)Q用JSON格式来展现、查询和修改数据?/span>
MongoDB文档相当完备Q扩展规模与安装一L单。它提供冗余、切片、烦引以及map/reduce{概忉|持。MongoDB的开源社区非常大且非常活跃。MongoDB在很多大型品中被实际运用,如:Disney, Craigslist, Foursquare, Github 和SourceForge。MongoDB是一个开源项目,?/span>10gen.com建立q维护,该公司由DoubleClick的前L行h员创立。同Ӟ10gen也提供了极好的商业支持与参与?/span>
MongoDB作ؓ一个可用NoSQLҎ(gu)h很多优势。我刚开始接触NoSQL数据库了解了一pdZJava的方案,q且׃大量的时间来弄懂什么是列家族,Hadoop与HBase的关p,ZooKeeper到底是什么。当我终于全部清楚之后,发现Cassandra与HBase实是对于NoSQL领域非常可靠、可信赖的解x案。但与其他的解决Ҏ(gu)相比QMongoDB让我在能够开始写代码之前Q不用理解那么多的概c?/span>
与其他Y件相|MongoDB也存在缺陗经q一D|间用MongoDBQ我列Dl历qƈ需要注意的一些事情,我成?#8220;Gotchas”Q?/span>
MongoDB基本是用JavaScript客户端命令行E序来进行复杂Q务管理的Q如数据整合和简单信息处理,~程都是完全使用JavaScript语言来的。本文中Q我们会展示命o行的使用CZ。现在有大量的MongoDB客户端品提供,q且由MongoDBC来支持驱动。通常每种~程语言都有驱动Qƈ且所有流行的语言都有包括Q一些不那么行的也包含在内。这文章展CZ使用MongoDB的Java驱动Qƈ使用一个ORM库(MJORMQ与之进行比较?/span>
在解决的众多有意思的问题中,最q?span>NoSQL数据存储在开发者中主要的问题趋势就是对象关pL。对象关pL就是将传统中保存在关系型数据库中的持久化数据映ؓ在应用程序中使用的对象。这使得~程语言使用h更加畅和自然?/span>
MongoDB面向文档的架构得它非常适合对象关系映射Q因为文档本w就是以对象形式存储的。可惜没有太多的MongoDB的Java对象关系映射库,但是q是有一些,?/span>morphia-(A type-safe Java library for MongoDB)Q?span> spring-data(SpringData目的MongoDB实现)
q些ORM库大量用了注解Q因Z些原因对我不适合Q其中最重要的就是这些被注解的对象在多个目中的兼容性问题。这让我开始了mongo-Java-orm 或?"MJORM" (发音 me-yorm)目Q一个MongoDB的Java对象关系映射目。MJORM是在MIT许可之下Qƈ且在发布在了google code project。项目采?span>maven构徏Qƈ且maven构g仓库托管于google code版本控制服务器。MJORM的最新可用发布版本ؓ0.15Q已l由一些项目用与生环境中?/span>
Maven的用者首先应当在pom.xml中加入MJORM的maven仓库Q得MJORM构g可用?/span>
<repository>
<id>mjorm-webdav-maven-repo</id>
<name>mjorm maven repository</name>
<url>http://mongo-Java-orm.googlecode.com/svn/maven/repo/</url>
<layout>default</layout>
</repository>
然后加入依赖:
<dependency>
<groupId>com.googlecode</groupId>
<artifactId>mongo-Java-orm</artifactId>
<version>0.15</version>
</dependency>
q样可以在应用中引?span>MJORM代码。假如没有用mavenQ则你需要手动下载MJORM的pom.xml中列丄所有依赖?/span>
依赖已经导入Q可以开始编码了。我们从POJO开?
class Author {
private String firstName;
private String lastName;
// ... setters and getters ...
}
class Book {
private String id;
private String isbn;
private String title;
private String description;
private Author author;
// ... setters and getters ...
}
我们在这个对象模型中的描q是Q作者有ID、姓和名Q书有ID、ISNB、标题、描q和作者?/span>
你可能注意到书的id属性是一个字W串Q这是ؓ了适应MongoDB的对象IDcd。MongoDB的ID是一?2字节的二q制值显CZؓ一个十六进制的字符丌ӀMongoDB要求集合中的每个文档都必L一个唯一idQ但不要求一定要是ObjectId。目前MJORM只支持ObjectIdQƈ且显CZؓ字符丌Ӏ?/span>
你也可能注意CAuthor没有id字段。这是因为Book是它的父文档Q因此不需要有id。记住,MongoDB只要求集合中的文档在根别的id?/span>
下一个步骤就是徏?span>XML映射文gQMJORM能够MongoDB文档转换为对象。我们ؓ每个文档创徏一个对象作为示范,无论所有的映射攑֜一个XML文g中还是分开都是可以的?/span>
Author.mjorm.xml
:
<?xml version="1.0"?>
<descriptors>
<object class="Author">
<property name="firstName" />
<property name="lastName" />
</object>
</descriptors>
Book.mjorm.xml
:
<?xml version="1.0"?>
<descriptors>
<object class="Book">
<property name="id" id="true" auto="true" />
<property name="isbn" />
<property name="title" />
<property name="description" />
<property name="author" />
</object>
</descriptors>
q些映射文g能够很好的自解释?/span>descriptors
元素是根元素Q必d含在每个映射文g中。在它下面是object
元素定义了文档与之对应的cR?/span>Object
包含?/span>
property
元素主要用于描述POJO中的属性以及这些属性如何与MongoDB中的文档惛_应?/span>property
元素臛_必须包含一?/span>name
属性,q个元素是POJO和MongoDB的文档中的属性名U?/span>column
属性则是可选的Q用于特定一个在MongoDB文档中的可选属性名U?/span>
property
元素当中?span>id属性应该是对象的唯一识别。一个对象只能有一?/span>property
元素包含id属性?/span>auto
的设|会使得MJORM在持久化时ؓ该属性自动生成一个倹{?/span>
可以?span>google code的MJORM目主页中查看XML映射文g的更多细节描q?/span>
我们创徏了数据模型以及映文Ӟ使得MJORM可以从MongoDB序列号以及反序列号POJO。我们可以进行一些有意思的事情了,首先打开MongoDB的链接:
Mongo mongo = new Mongo(
new MongoURI("mongodb://localhost/mjormIsFun")); // 10gen driver
Mongo
对象是由10gen~写的Java驱动提供的。示例中q接了一个本地的MongoDB实例中的mjormIsFun数据库。接下来我们创徏MJORM ObjectMapper
。目?/span>ObjectMapper
在MJORM中的唯一实现是XmlDescriptorObjectMapper
Q用XMLl构描述信息。可能之后会增加Ҏ(gu)解或其他l构定义的支持?/span>
XmlDescriptorObjectMapper objectMapper = new XmlDescriptorObjectMapper();
mapper.addXmlObjectDescriptor(new File("Book.mjorm.xml"));
mapper.addXmlObjectDescriptor(new File("Author.mjorm.xml"));
建立好了XmlDescriptorObjectMapper
q且加入了映文件。接下来建立由MJORM提供?/span>MongoDao
对象的实例?/span>
DB db = mongo.getDB("mjormIsFun"); // 10gen driver
MongoDao dao = new MongoDaoImpl(db, objectMapper);
首先我们要获?span>10gen驱动提供的DB对象实例。然后用DB?/span>ObjectMapper
建立MongoDao
。我们准备开始持久化数据Q徏立一?/span>Book
然后保存到MongoDB中?/span>
Book book = new Book();
book.setIsbn("1594743061");
book.setTitle("MongoDB is fun");
book.setDescription("...");
book = dao.createObject("books", book);
System.out.println(book.getId()); // 4f96309f762dd76ece5a9595
首先建立Book
对象q且填|然后调用MongoDao
?/span> createObject
Ҏ(gu)Q将Book
对象传入"books
" 的集合中。MJORM会按照之前的xml映射文g?/span>Book
转换?/span>DBObject
(q是10gen的Java驱动使用的基本类?Qƈ保存一个新的文档进"books
" 集合。MJORMq回Book对象Ӟid属性会被填充。请注意QMongoDB默认是不需要在使用前徏立数据库或集合的Q系l会在需要时自动创徏Q这可能会造成某些困扰。在MongoDB的命令行中查看Book对象大概如下Q?/span>
> db.books.find({_id:ObjectId("4f96309f762dd76ece5a9595")}).pretty()
{
"_id": ObjectId("4f96309f762dd76ece5a9595"),
"isbn": "1594743061",
"title": "MongoDB is fun",
"description": "..."
}
我们来看看假如不?span>MJORM而直接?0gen的Java驱动Q如何?/span>createObject
Ҏ(gu)Q?/span>
Book book = new Book();
book.setIsbn("1594743061");
book.setTitle("MongoDB is fun");
book.setDescription("...");
DBObject bookObj = BasicDBObjectBuilder.start()
.add("isbn", book.getIsbn())
.add("title", book.getTitle())
.add("description", book.getDescription())
.get();
// 'db' is our DB object from earlier
DBCollection col = db.getCollection("books");
col.insert(bookObj);
ObjectId id = ObjectId.class.cast(bookObj.get("_id"));
System.out.println(id.toStringMongod()); // 4f96309f762dd76ece5a9595
下面q行对象的查?span>:
Book book = dao.readObject("books", "4f96309f762dd76ece5a9595", Book.class);
System.out.println(book.getTitle()); // "MongoDB is fun"
readObject
Ҏ(gu)Ҏ(gu)l定文档的id从指定的集合中读取文档,转换为对象(再次使用映射文gQƈq回?/span>
敏锐的读者会注意?span>Bookq没有指定AuthorQ仍然保存了。这归咎于MongoDB的结构不敏感的特性。我们不能要求集合中的文档包含所有属性(id属性是必须的)Q所有在MongoDB中没有Author的Book是可以的。我们现在ؓBookd一个Authorq且更新一下:
Author author = new Author();
author.setFirstName("Brian");
author.setLastName("Dilley");
book.setAuthor(author);
dao.updateObject("books", "4f96309f762dd76ece5a9595", book);
现在Book包含了AuthorQƈ且在MongoDB中持久化了。现在在命o行查看了BookQ?/span>
> db.books.find({_id:ObjectId("4f96309f762dd76ece5a9595")}).pretty()
{
"_id": ObjectId("4f96309f762dd76ece5a9595"),
"isbn": "1594743061",
"title": "MongoDB is fun",
"description": "..."
"author": {
"firstName": "Brian",
"lastName": "Dilley"
}
}
可以看到持久化的Book中已l包含了author。不使用MJORM来操作一遍:
Author author = new Author();
author.setFirstName("Brian");
author.setLastName("Dilley");
book.setAuthor(author);
DBObject bookObj = BasicDBObjectBuilder.start()
.add("isbn", book.getIsbn())
.add("title", book.getTitle())
.add("description", book.getDescription())
.push("author")
.add("firstName", author.getFirstName())
.add("lastName", author.getLastName())
.pop()
.get();
DBCollection col = db.getCollection("books");
col.update(new BasicDBObject("_id", bookObj.get("_id")), bookObj);
对于MongoDao
Ҏ(gu)的深入讨论已l超Z本文的范围。对于将MJORM有兴用于实际项目中的用户强烈徏议了解一下MJORM目提供的相x档,或?/span>MongoDao
接口提供的相关用法?/span>
希望q篇文章?span>MongoDB和MJORM的亮Ҏ(gu)所展示。MongDB是一个优U的呃NoSQL数据存储Q有着大量优秀的特性,会是NoSQL市场中长期竞争者。若你会在一个Java目中用MongoDBQ希望你也能够考虑使用MJORM作ؓ你的ORM框架。十分欢q大家提交特性需求、错误异常报告、文档和源码修正?/span>
Brian Dilley 是一个经验丰富的高工程师以及项目领|?/span>Java/Java EE /Spring Framework/Linux内部l构理解和管理有着过13q的l验?/span>Brian对于创业公司有很多经验,推向市场Q构?/span>/l护产品{。他?/span>Iaas?/span>cloud?/span>PHP?/span>Linux的专Ӟ熟?zhn)产品的采购、安装及配置定义Q以及公司的软硬件架构包括负载均衡、数据库、微博等。可?/span>follow Brian?/span> Twitter ?/span>