MongoDB介紹
當今NoSQL領域中有很多有力的競爭者通過多種方式來處理海量數據問題。其中重要的解決方案之一就是MongoDB。MongoDB是面向文檔的弱結構化存儲方案,使用JSON格式來展現、查詢和修改數據。
MongoDB文檔相當完備,擴展規模與安裝一樣簡單。它提供冗余、切片、索引以及map/reduce等概念支持。MongoDB的開源社區非常大且非?;钴S。MongoDB在很多大型產品中被實際運用,如:Disney, Craigslist, Foursquare, Github 和SourceForge。MongoDB是一個開源項目,由10gen.com建立并維護,該公司由DoubleClick的前任執行人員創立。同時,10gen也提供了極好的商業支持與參與建設。
MongoDB 與 NoSQL: 缺陷與優勢
MongoDB作為一個可用NoSQL方案具有很多優勢。我剛開始接觸NoSQL數據庫了解了一系列基于Java的方案,并且花了大量的時間來弄懂什么是列家族,Hadoop與HBase的關系,ZooKeeper到底是什么。當我終于全部清楚之后,發現Cassandra與HBase確實是對于NoSQL領域非常可靠、可信賴的解決方案。但與其他的解決方案相比,MongoDB讓我在能夠開始寫代碼之前,不用理解那么多的概念。
與其他軟件相似,MongoDB也存在缺陷。經過一段時間使用MongoDB,我列舉經歷過并需要注意的一些事情,我成為“Gotchas”:
- 不要按照關系型數據庫來思考。這很明顯,MongoDB使得構建和執行復雜查詢變得非常容易。當實際使用的時候,你會主要關注于效率問題(像我一樣)。
- MongoDB的索引是二進制的樹。如果你不是很熟悉B-tree,可能需要了解一下。這些都涉及到構建符合提供查詢條件需求的建立索引的方式。
- 小心的設計索引結構。這涉及到上面提到的B-tree。剛開始我的索引包含文檔中的很多字段,以防我會使用到他們。不要犯同樣的錯誤。我有一個很小集合的索引(大約1千萬記錄)增長到超過17GB的空間,比集合本身還大。你應該不會想要索引一個包含成百上千個實體的列表字段。
- MongoDB采用了非常有意思的方式來實現NoSQL:采用BSON作為存儲,JSON作為展示,JavaScript用于管理和Map/Reduce。因此也引起了一些小問題比如這個 (破壞了Number和Long的相等操作),在MongoDB逐漸流行之后,可能會不斷的展示出來。
MongoDB, 命令行與驅動
MongoDB基本是使用JavaScript客戶端命令行程序來進行復雜任務管理的,如數據整合和簡單信息處理,編程都是完全使用JavaScript語言來的。本文中,我們會展示命令行的使用示例?,F在有大量的MongoDB客戶端產品提供,并且由MongoDB社區來支持驅動。通常每種編程語言都有驅動,并且所有流行的語言都有包括,一些不那么流行的也包含在內。這篇文章展示了使用MongoDB的Java驅動,并使用一個ORM庫(MJORM)與之進行比較。
介紹 MJORM: MongoDB的ORM方案
在解決的眾多有意思的問題中,最近NoSQL數據存儲在開發者中主要的問題趨勢就是對象關系映射。對象關系映射就是將傳統中保存在關系型數據庫中的持久化數據映射為在應用程序中使用的對象。這使得編程語言使用起來更加流暢和自然。
MongoDB面向文檔的架構使得它非常適合對象關系映射,因為文檔本身就是以對象形式存儲的??上]有太多的MongoDB的Java對象關系映射庫,但是還是有一些,如morphia-(A type-safe Java library for MongoDB), spring-data(SpringData項目的MongoDB實現)
這些ORM庫大量使用了注解,因為一些原因對我不適合,其中最重要的就是這些被注解的對象在多個項目中的兼容性問題。這讓我開始了mongo-Java-orm 或者 "MJORM" (發音 me-yorm)項目,一個MongoDB的Java對象關系映射項目。MJORM是在MIT許可之下,并且在發布在了google code project。項目采用maven構建,并且maven構件倉庫托管于google code版本控制服務器。MJORM的最新可用發布版本為0.15,已經由一些項目使用與生產環境中。
開始使用ORM
加入MJORM 庫
Maven的使用者首先應當在pom.xml中加入MJORM的maven倉庫,使得MJORM構件可用。
<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>
這樣就可以在應用中引入MJORM代碼。假如沒有使用maven,則你需要手動下載MJORM的pom.xml中列舉的所有依賴。
建立 POJOs
依賴已經導入,可以開始編碼了。我們從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 ...
}
我們在這個對象模型中的描述是,作者有ID、姓和名,書有ID、ISNB、標題、描述和作者。
你可能注意到書的id屬性是一個字符串,這是為了適應MongoDB的對象ID類型。MongoDB的ID是一個12字節的二進制值顯示為一個十六進制的字符串。MongoDB要求集合中的每個文檔都必須有一個唯一id,但不要求一定要是ObjectId。目前MJORM只支持ObjectId,并且顯示為字符串。
你也可能注意到了Author沒有id字段。這是因為Book是它的父文檔,因此不需要有id。記住,MongoDB只要求集合中的文檔在根級別的id。
創建XML映射文件
下一個步驟就是建立XML映射文件,MJORM能夠將MongoDB文檔轉換為對象。我們為每個文檔創建一個對象作為示范,無論將所有的映射放在一個XML文件中還是分開都是可以的。
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>
這些映射文件能夠很好的自解釋。descriptors
元素是根元素,必須包含在每個映射文件中。在它下面是object
元素定義了文檔與之對應的類。Object
包含的
property
元素主要用于描述POJO中的屬性以及這些屬性如何與MongoDB中的文檔想對應。property
元素至少必須包含一個name
屬性,這個元素就是POJO和MongoDB的文檔中的屬性名稱。column
屬性則是可選的,用于特定一個在MongoDB文檔中的可選屬性名稱。
property
元素當中的id屬性應該是對象的唯一識別。一個對象只能有一個property
元素包含id屬性。auto
的設置會使得MJORM在持久化時為該屬性自動生成一個值。
可以在google code的MJORM項目主頁中查看XML映射文件的更多細節描述。
整合POJO與XML
我們創建了數據模型以及映射文件,使得MJORM可以從MongoDB序列號以及反序列號POJO。我們可以進行一些有意思的事情了,首先打開MongoDB的鏈接:
Mongo mongo = new Mongo(
new MongoURI("mongodb://localhost/mjormIsFun")); // 10gen driver
Mongo
對象是由10gen編寫的Java驅動提供的。示例中連接了一個本地的MongoDB實例中的mjormIsFun數據庫。接下來我們創建MJORM ObjectMapper
。目前ObjectMapper
在MJORM中的唯一實現就是XmlDescriptorObjectMapper
,使用XML結構描述信息。可能之后會增加對注解或其他結構定義的支持。
XmlDescriptorObjectMapper objectMapper = new XmlDescriptorObjectMapper();
mapper.addXmlObjectDescriptor(new File("Book.mjorm.xml"));
mapper.addXmlObjectDescriptor(new File("Author.mjorm.xml"));
建立好了XmlDescriptorObjectMapper
并且加入了映射文件。接下來建立由MJORM提供的MongoDao
對象的實例。
DB db = mongo.getDB("mjormIsFun"); // 10gen driver
MongoDao dao = new MongoDaoImpl(db, objectMapper);
首先我們要獲得10gen驅動提供的DB對象實例。然后使用DB和ObjectMapper
建立MongoDao
。我們準備開始持久化數據,建立一個Book
然后保存到MongoDB中。
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
對象并且填值,然后調用MongoDao
的 createObject
方法,將Book
對象傳入"books
" 的集合中。MJORM會按照之前的xml映射文件將Book
轉換為DBObject
(這是10gen的Java驅動使用的基本類型),并保存一個新的文檔進"books
" 集合。MJORM返回Book對象時,id屬性會被填充。請注意,MongoDB默認是不需要在使用前建立數據庫或集合的,系統會在需要時自動創建,這可能會造成某些困擾。在MongoDB的命令行中查看Book對象大概如下:
> db.books.find({_id:ObjectId("4f96309f762dd76ece5a9595")}).pretty()
{
"_id": ObjectId("4f96309f762dd76ece5a9595"),
"isbn": "1594743061",
"title": "MongoDB is fun",
"description": "..."
}
我們來看看假如不用MJORM而直接使用10gen的Java驅動,如何使用createObject
方法:
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
下面進行對象的查詢:
Book book = dao.readObject("books", "4f96309f762dd76ece5a9595", Book.class);
System.out.println(book.getTitle()); // "MongoDB is fun"
readObject
方法根據給定文檔的id從指定的集合中讀取文檔,轉換為對象(再次使用映射文件)并返回。
敏銳的讀者會注意到Book還沒有指定Author,仍然保存了。這歸咎于MongoDB的結構不敏感的特性。我們不能要求集合中的文檔包含所有屬性(id屬性是必須的),所有在MongoDB中沒有Author的Book是可以的。我們現在為Book添加一個Author并且更新一下:
Author author = new Author();
author.setFirstName("Brian");
author.setLastName("Dilley");
book.setAuthor(author);
dao.updateObject("books", "4f96309f762dd76ece5a9595", book);
現在Book就包含了Author,并且在MongoDB中持久化了?,F在在命令行查看了Book:
> db.books.find({_id:ObjectId("4f96309f762dd76ece5a9595")}).pretty()
{
"_id": ObjectId("4f96309f762dd76ece5a9595"),
"isbn": "1594743061",
"title": "MongoDB is fun",
"description": "..."
"author": {
"firstName": "Brian",
"lastName": "Dilley"
}
}
可以看到持久化的Book中已經包含了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
方法的深入討論已經超出了本文的范圍。對于將MJORM有興趣用于實際項目中的用戶強烈建議了解一下MJORM項目提供的相關文檔,或者MongoDao
接口提供的相關用法。
總結
希望這篇文章對MongoDB和MJORM的亮點有所展示。MongDB是一個優秀的呃NoSQL數據存儲,有著大量優秀的特性,會是NoSQL市場中長期競爭者。若你會在一個Java項目中使用MongoDB,希望你也能夠考慮使用MJORM作為你的ORM框架。十分歡迎大家提交特性需求、錯誤異常報告、文檔和源碼修正。
作者 Bio
Brian Dilley 是一個經驗豐富的高級工程師以及項目領導,在Java/Java EE /Spring Framework/Linux內部結構理解和管理有著超過13年的經驗。Brian對于創業公司有很多經驗,推向市場,構建/維護產品等。他是Iaas、cloud、PHP和Linux的專家,熟悉產品的采購、安裝及配置定義,以及公司的軟硬件架構包括負載均衡、數據庫、微博等??梢?/span>follow Brian的 Twitter 。