敏捷、分布式、ALM過程自動化、企業(yè)應用架構(gòu)
          posts - 14, comments - 0, trackbacks - 0, articles - 1
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          MongoDB, Java 與對象關系映射

          Posted on 2012-05-09 13:46 一酌散千憂 閱讀(1546) 評論(0)  編輯  收藏 所屬分類: 翻譯NoSQL

          MongoDB介紹

          當今NoSQL領域中有很多有力的競爭者通過多種方式來處理海量數(shù)據(jù)問題。其中重要的解決方案之一就是MongoDB。MongoDB是面向文檔的弱結(jié)構(gòu)化存儲方案,使用JSON格式來展現(xiàn)、查詢和修改數(shù)據(jù)。

          MongoDB文檔相當完備,擴展規(guī)模與安裝一樣簡單。它提供冗余、切片、索引以及map/reduce等概念支持。MongoDB的開源社區(qū)非常大且非常活躍。MongoDB在很多大型產(chǎn)品中被實際運用,如:Disney, Craigslist, Foursquare, Github 和SourceForge。MongoDB是一個開源項目,由10gen.com建立并維護,該公司由DoubleClick的前任執(zhí)行人員創(chuàng)立。同時,10gen也提供了極好的商業(yè)支持與參與建設。

          MongoDB NoSQL: 缺陷與優(yōu)勢

          MongoDB作為一個可用NoSQL方案具有很多優(yōu)勢。我剛開始接觸NoSQL數(shù)據(jù)庫了解了一系列基于Java的方案,并且花了大量的時間來弄懂什么是列家族,Hadoop與HBase的關系,ZooKeeper到底是什么。當我終于全部清楚之后,發(fā)現(xiàn)Cassandra與HBase確實是對于NoSQL領域非常可靠、可信賴的解決方案。但與其他的解決方案相比,MongoDB讓我在能夠開始寫代碼之前,不用理解那么多的概念。

          與其他軟件相似,MongoDB也存在缺陷。經(jīng)過一段時間使用MongoDB,我列舉經(jīng)歷過并需要注意的一些事情,我成為“Gotchas”:

          • 不要按照關系型數(shù)據(jù)庫來思考。這很明顯,MongoDB使得構(gòu)建和執(zhí)行復雜查詢變得非常容易。當實際使用的時候,你會主要關注于效率問題(像我一樣)。
          • MongoDB的索引是二進制的樹。如果你不是很熟悉B-tree,可能需要了解一下。這些都涉及到構(gòu)建符合提供查詢條件需求的建立索引的方式。
          • 小心的設計索引結(jié)構(gòu)。這涉及到上面提到的B-tree。剛開始我的索引包含文檔中的很多字段,以防我會使用到他們。不要犯同樣的錯誤。我有一個很小集合的索引(大約1千萬記錄)增長到超過17GB的空間,比集合本身還大。你應該不會想要索引一個包含成百上千個實體的列表字段。
          • MongoDB采用了非常有意思的方式來實現(xiàn)NoSQL:采用BSON作為存儲,JSON作為展示,JavaScript用于管理和Map/Reduce。因此也引起了一些小問題比如這個 (破壞了Number和Long的相等操作),在MongoDB逐漸流行之后,可能會不斷的展示出來。

           

          MongoDB, 命令行與驅(qū)動

          MongoDB基本是使用JavaScript客戶端命令行程序來進行復雜任務管理的,如數(shù)據(jù)整合和簡單信息處理,編程都是完全使用JavaScript語言來的。本文中,我們會展示命令行的使用示例。現(xiàn)在有大量的MongoDB客戶端產(chǎn)品提供,并且由MongoDB社區(qū)來支持驅(qū)動。通常每種編程語言都有驅(qū)動,并且所有流行的語言都有包括,一些不那么流行的也包含在內(nèi)。這篇文章展示了使用MongoDB的Java驅(qū)動,并使用一個ORM庫(MJORM)與之進行比較。

          介紹 MJORM: MongoDBORM方案

          在解決的眾多有意思的問題中,最近NoSQL數(shù)據(jù)存儲在開發(fā)者中主要的問題趨勢就是對象關系映射。對象關系映射就是將傳統(tǒng)中保存在關系型數(shù)據(jù)庫中的持久化數(shù)據(jù)映射為在應用程序中使用的對象。這使得編程語言使用起來更加流暢和自然。

          MongoDB面向文檔的架構(gòu)使得它非常適合對象關系映射,因為文檔本身就是以對象形式存儲的。可惜沒有太多的MongoDB的Java對象關系映射庫,但是還是有一些,如morphia-(A type-safe Java library for MongoDB) spring-data(SpringData項目的MongoDB實現(xiàn))

          這些ORM庫大量使用了注解,因為一些原因?qū)ξ也贿m合,其中最重要的就是這些被注解的對象在多個項目中的兼容性問題。這讓我開始了mongo-Java-orm 或者 "MJORM" (發(fā)音 me-yorm)項目,一個MongoDB的Java對象關系映射項目。MJORM是在MIT許可之下,并且在發(fā)布在了google code project。項目采用maven構(gòu)建,并且maven構(gòu)件倉庫托管于google code版本控制服務器。MJORM的最新可用發(fā)布版本為0.15,已經(jīng)由一些項目使用與生產(chǎn)環(huán)境中。

          開始使用ORM

          加入MJORM

          Maven的使用者首先應當在pom.xml中加入MJORM的maven倉庫,使得MJORM構(gòu)件可用。

          <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

          依賴已經(jīng)導入,可以開始編碼了。我們從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字節(jié)的二進制值顯示為一個十六進制的字符串。MongoDB要求集合中的每個文檔都必須有一個唯一id,但不要求一定要是ObjectId。目前MJORM只支持ObjectId,并且顯示為字符串。

          你也可能注意到了Author沒有id字段。這是因為Book是它的父文檔,因此不需要有id。記住,MongoDB只要求集合中的文檔在根級別的id。

          創(chuàng)建XML映射文件

          下一個步驟就是建立XML映射文件,MJORM能夠?qū)ongoDB文檔轉(zhuǎn)換為對象。我們?yōu)槊總€文檔創(chuàng)建一個對象作為示范,無論將所有的映射放在一個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映射文件的更多細節(jié)描述。

          整合POJOXML

          我們創(chuàng)建了數(shù)據(jù)模型以及映射文件,使得MJORM可以從MongoDB序列號以及反序列號POJO。我們可以進行一些有意思的事情了,首先打開MongoDB的鏈接:

          Mongo mongo = new Mongo(
                   new MongoURI("mongodb://localhost/mjormIsFun")); // 10gen driver

          Mongo 對象是由10gen編寫的Java驅(qū)動提供的。示例中連接了一個本地的MongoDB實例中的mjormIsFun數(shù)據(jù)庫。接下來我們創(chuàng)建MJORM ObjectMapper 。目前ObjectMapper 在MJORM中的唯一實現(xiàn)就是XmlDescriptorObjectMapper,使用XML結(jié)構(gòu)描述信息。可能之后會增加對注解或其他結(jié)構(gòu)定義的支持。

          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驅(qū)動提供的DB對象實例。然后使用DB和ObjectMapper 建立MongoDao 。我們準備開始持久化數(shù)據(jù),建立一個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 對象并且填值,然后調(diào)用MongoDao 的 createObject 方法,將Book 對象傳入"books" 的集合中。MJORM會按照之前的xml映射文件將Book 轉(zhuǎn)換為DBObject (這是10gen的Java驅(qū)動使用的基本類型),并保存一個新的文檔進"books" 集合。MJORM返回Book對象時,id屬性會被填充。請注意,MongoDB默認是不需要在使用前建立數(shù)據(jù)庫或集合的,系統(tǒng)會在需要時自動創(chuàng)建,這可能會造成某些困擾。在MongoDB的命令行中查看Book對象大概如下:

          > db.books.find({_id:ObjectId("4f96309f762dd76ece5a9595")}).pretty()
          {
                   "_id":          ObjectId("4f96309f762dd76ece5a9595"),
                   "isbn":         "1594743061",
                   "title":        "MongoDB is fun",
                   "description": "..."
          }

           

          我們來看看假如不用MJORM而直接使用10gen的Java驅(qū)動,如何使用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 方法根據(jù)給定文檔的id從指定的集合中讀取文檔,轉(zhuǎn)換為對象(再次使用映射文件)并返回。

          敏銳的讀者會注意到Book還沒有指定Author,仍然保存了。這歸咎于MongoDB的結(jié)構(gòu)不敏感的特性。我們不能要求集合中的文檔包含所有屬性(id屬性是必須的),所有在MongoDB中沒有Author的Book是可以的。我們現(xiàn)在為Book添加一個Author并且更新一下:

          Author author = new Author();
          author.setFirstName("Brian");
          author.setLastName("Dilley");
           
          book.setAuthor(author);
           
          dao.updateObject("books", "4f96309f762dd76ece5a9595", book);

          現(xiàn)在Book就包含了Author,并且在MongoDB中持久化了。現(xiàn)在在命令行查看了Book:

          > db.books.find({_id:ObjectId("4f96309f762dd76ece5a9595")}).pretty()
          {
                   "_id":          ObjectId("4f96309f762dd76ece5a9595"),
                   "isbn":         "1594743061",
                   "title":        "MongoDB is fun",
                   "description": "..."
                   "author": {
                       "firstName": "Brian",
                       "lastName": "Dilley"
                   }
          }

          可以看到持久化的Book中已經(jīng)包含了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 方法的深入討論已經(jīng)超出了本文的范圍。對于將MJORM有興趣用于實際項目中的用戶強烈建議了解一下MJORM項目提供的相關文檔,或者MongoDao 接口提供的相關用法。

          總結(jié)

          希望這篇文章對MongoDB和MJORM的亮點有所展示。MongDB是一個優(yōu)秀的呃NoSQL數(shù)據(jù)存儲,有著大量優(yōu)秀的特性,會是NoSQL市場中長期競爭者。若你會在一個Java項目中使用MongoDB,希望你也能夠考慮使用MJORM作為你的ORM框架。十分歡迎大家提交特性需求、錯誤異常報告、文檔和源碼修正。

           

          作者 Bio

          Brian Dilley 是一個經(jīng)驗豐富的高級工程師以及項目領導,在Java/Java EE /Spring Framework/Linux內(nèi)部結(jié)構(gòu)理解和管理有著超過13年的經(jīng)驗。Brian對于創(chuàng)業(yè)公司有很多經(jīng)驗,推向市場,構(gòu)建/維護產(chǎn)品等。他是IaascloudPHPLinux的專家,熟悉產(chǎn)品的采購、安裝及配置定義,以及公司的軟硬件架構(gòu)包括負載均衡、數(shù)據(jù)庫、微博等。可以follow Brian Twitter

          主站蜘蛛池模板: 平顶山市| 健康| 奇台县| 色达县| 若羌县| 宁德市| 即墨市| 库尔勒市| 江孜县| 高雄县| 金华市| 临湘市| 额济纳旗| 南澳县| 安福县| 郸城县| 梁山县| 柳州市| 唐河县| 西平县| 云霄县| 孟州市| 六安市| 故城县| 冷水江市| 金山区| 浦县| 承德市| 开化县| 织金县| 花莲市| 会昌县| 博罗县| 盐亭县| 灵石县| 吉首市| 临清市| 山东省| 安达市| 上高县| 苏尼特左旗|