国产一级一区二区_segui88久久综合9999_97久久夜色精品国产_欧美色网一区二区

掃一掃
關(guān)注微信公眾號(hào)

一種新的思維方式?NoSQL數(shù)據(jù)建模
2010-07-29   網(wǎng)絡(luò)

關(guān)系數(shù)據(jù)庫(kù)已經(jīng)統(tǒng)治數(shù)據(jù)存儲(chǔ)30 多年了,但是無模式(或NoSQL)數(shù)據(jù)庫(kù)的逐漸流行表明變化正在發(fā)生。盡管 RDBMS 為在傳統(tǒng)的客戶端服務(wù)器架構(gòu)中存儲(chǔ)數(shù)據(jù)提供了一個(gè)堅(jiān)實(shí)的基礎(chǔ),但它不能輕松地(或便宜地)擴(kuò)展到多個(gè)節(jié)點(diǎn)。在高度可伸縮的 Web 應(yīng)用程序(比如 Facebook 和 Twitter)的時(shí)代,這是一個(gè)非常不幸的弱點(diǎn)。

盡管關(guān)系數(shù)據(jù)庫(kù)的早期替代方案(還記得面向?qū)ο蟮臄?shù)據(jù)庫(kù)嗎?)不能解決真正緊急的問 題,NoSQL 數(shù)據(jù)庫(kù)(比如 Google 的 Bigtable 和 Amazon 的 SimpleDB)卻作為對(duì) Web 的高可伸縮性需求的直接響應(yīng)而崛起。本質(zhì)上,NoSQL 可能是一個(gè)殺手問題的殺手應(yīng)用程序 —隨著 Web 2.0 的演變,Web 應(yīng)用程序開發(fā)人員可能會(huì)遇到更多,而不是更少這樣的應(yīng)用程序。

在這期 Java 開發(fā) 2.0 中,我將向您介紹無模式數(shù)據(jù)建模,這是經(jīng)過關(guān)系思維模式訓(xùn)練的許多開發(fā)人員使用 NoSQL 的主要障礙。您將了解到,從一個(gè)域模型(而不是關(guān)系模型)入手是簡(jiǎn)化您的改變的關(guān)鍵。如果您使用 Bigtable(如我的示例所示),您可以借助 Gaelyk:Google App Engine 的一個(gè)輕量級(jí)框架擴(kuò)展。

NoSQL:一種新的思維方式?

當(dāng)開發(fā)人員談?wù)摲顷P(guān)系或 NoSQL 數(shù)據(jù)庫(kù)時(shí),經(jīng)常提到的第一件事是他們需要改變思維方式。我認(rèn)為,那實(shí)際上取決于您的初始數(shù)據(jù)建模方法。如果您習(xí)慣通過首先建模數(shù)據(jù)庫(kù)結(jié)構(gòu)(即首先確定表及 其關(guān)聯(lián)關(guān)系)來設(shè)計(jì)應(yīng)用程序,那么使用一個(gè)無模式數(shù)據(jù)存儲(chǔ)(比如 Bigtable)來進(jìn)行數(shù)據(jù)建模則需要您重新思考您的做事方式。但是,如果您從域模型開始設(shè)計(jì)您的應(yīng)用程序,那么 Bigtable 的無模式結(jié)構(gòu)將看起來更自然。

非關(guān)系數(shù)據(jù)存儲(chǔ)沒有聯(lián)接表或主鍵,甚至沒有外鍵這個(gè)概念(盡管這兩種類型的鍵以一種更松散的 形式出現(xiàn))。因此,如果您嘗試將關(guān)系建模作為一個(gè) NoSQL 數(shù)據(jù)庫(kù)中的數(shù)據(jù)建模的基礎(chǔ),那么您可能最后以失敗告終。從域模型開始將使事情變得簡(jiǎn)單;實(shí)際上,我已經(jīng)發(fā)現(xiàn),域模型下的無模式結(jié)構(gòu)的靈活性正在重新煥發(fā)生 機(jī)。

從關(guān)系數(shù)據(jù)模型遷移到無模式數(shù)據(jù)模型的相對(duì)復(fù)雜程度取決于您的方法:即您從基于關(guān)系的設(shè)計(jì)開 始還是從基于域的設(shè)計(jì)開始。當(dāng)您遷移到 CouchDB 或 Bigtable 這樣的數(shù)據(jù)庫(kù)時(shí),您 的確會(huì)喪失 Hibernate(至少現(xiàn)在)這樣的成熟的持久存儲(chǔ)平臺(tái)的順暢感覺。另一方面,您卻擁有能夠親自構(gòu)建它的 “綠地效果”。在此過程中,您將深入了解無模式數(shù)據(jù)存儲(chǔ)。

實(shí)體和關(guān)系

無模式數(shù)據(jù)存儲(chǔ)賦予您首先使用對(duì)象來設(shè)計(jì)域模型的靈活性(Grails 這樣的較新的框架自動(dòng)支持這種靈活性)。您的下一步工作是將您的域映射到底層數(shù)據(jù)存儲(chǔ),這在使用 Google App Engine 時(shí)再簡(jiǎn)單不過了。

在文章 “Java 開發(fā) 2.0:針對(duì) Google App Engine 的 Gaelyk” 中,我介紹了 Gaelyk —— 一個(gè)基于 Groovy 的框架,該框架有利于使用 Google 的底層數(shù)據(jù)存儲(chǔ)。那篇文章的主要部分關(guān)注如何利用 Google 的 Entity對(duì)象。下面的示例(來自那篇文章)將展示對(duì)象實(shí)體如何在 Gaelyk 中工作。

清單1. 使用 Entity 的對(duì)象持久存儲(chǔ)

  1. def ticket = new Entity("ticket")
  2. ticket.officer = params.officer
  3. ticket.license = params.plate
  4. ticket.issuseDate = offensedate
  5. ticket.location = params.location
  6. ticket.notes = params.notes
  7. ticket.offense = params.offense

這種對(duì)象持久存儲(chǔ)方法很有效,但容易看出,如果您頻繁使用票據(jù)實(shí)體 —例如,如果您正在各種 servlet 中創(chuàng)建(或查找)它們,那么這種方法將變得令人厭煩。使用一個(gè)公共 servlet(或 Groovlet)來為您處理這些任務(wù)將消除其中一些負(fù)擔(dān)。一種更自然的選擇——我將稍后展示——將是建模一個(gè) Ticket對(duì)象。

返回比賽

我不會(huì)重復(fù) Gaelyk 簡(jiǎn)介中的那個(gè)票據(jù)示例,相反,為保持新鮮感,我將在本文中使用一個(gè)賽跑主題,并構(gòu)建一個(gè)應(yīng)用程序來展示即將討論的技術(shù)。

如圖 1 中的 “多對(duì)多” 圖表所示,一個(gè) Race擁有多個(gè) Runner,一個(gè) Runner可以屬于多個(gè) Race。

圖1. 比賽和參賽者

如果我要使用一個(gè)關(guān)系表結(jié)構(gòu)來設(shè)計(jì)這個(gè)關(guān)系,至少需要 3 個(gè)表:第 3 表將是鏈接一個(gè) “多對(duì)多” 關(guān)系的聯(lián)接表。所幸我不必局限于關(guān)系數(shù)據(jù)模型。相反,我將使用 Gaelyk(和 Groovy 代碼)將這個(gè) “多對(duì)多” 關(guān)系映射到 Google 針對(duì) Google App Engine 的 Bigtable 抽象。事實(shí)上,Gaelyk 允許將 Entity當(dāng)作 Map,這使得映射過程相當(dāng)簡(jiǎn)單。

無模式數(shù)據(jù)存儲(chǔ)的好處之一是無須事先知道所有事情,也就是說,與使用關(guān)系數(shù)據(jù)庫(kù)架構(gòu)相比,可 以更輕松地適應(yīng)變化。(注意,我并非暗示不能更改架構(gòu);我只是說,可以更輕松地適應(yīng)變化。)我不打算定義我的域?qū)ο笊系膶傩?—我將其推遲到 Groovy 的動(dòng)態(tài)特性(實(shí)際上,這個(gè)特性允許創(chuàng)建針對(duì) Google 的 Entity對(duì)象的域?qū)ο蟠恚O喾矗覍盐业臅r(shí)間花費(fèi)在確定如何查找對(duì)象并處理關(guān)系上。這是 NoSQL 和各種利用無模式數(shù)據(jù)存儲(chǔ)的框架還沒有內(nèi)置的功能。

Model 基類

我將首先創(chuàng)建一個(gè)基類,用于容納 Entity對(duì)象的一個(gè)實(shí)例。然后,我將允許一些子類擁有一些動(dòng)態(tài)屬性,這些動(dòng)態(tài)屬性將通過 Groovy 的方便的 setProperty方法添加到對(duì)應(yīng)的 Entity實(shí)例。setProperty針對(duì)對(duì)象中實(shí)際上不存在的任何屬性設(shè)置程序調(diào)用。(如果這聽起來聳人聽聞,不用擔(dān)心,您看到它的實(shí)際運(yùn)行后就會(huì) 明白。)

清單2展示了位于我的示例應(yīng)用程序的一個(gè) Model實(shí)例的第一個(gè) stab:

清單2. 一個(gè)簡(jiǎn)單的 Model 基類

  1. package com.b50.nosql
  2. import com.google.appengine.api.datastore.DatastoreServiceFactory
  3. import com.google.appengine.api.datastore.Entity
  4. abstract class Model {
  5. def entity
  6. static def datastore = DatastoreServiceFactory.datastoreService
  7. public Model(){
  8. super()
  9. }
  10. public Model(params){
  11. this.@entity = new Entity(this.getClass().simpleName)
  12. params.each{ key, val ->
  13. this.setProperty key, val
  14. }
  15. }
  16. def getProperty(String name) {
  17. if(name.equals("id")){
  18. return entity.key.id
  19. }else{
  20. return entity."${name}"
  21. }
  22. }
  23. void setProperty(String name, value) {
  24. entity."${name}" = value
  25. }
  26. def save(){
  27. this.entity.save()
  28. }
  29. }

注意抽象類如何定義一個(gè)構(gòu)造函數(shù),該函數(shù)接收屬性的一個(gè) Map ——我總是可以稍后添加更多構(gòu)造函數(shù),稍后我就會(huì)這么做。這個(gè)設(shè)置對(duì)于 Web 框架十分方便,這些框架通常采用從表單提交的參數(shù)。Gaelyk 和 Grails 將這樣的參數(shù)巧妙地封裝到一個(gè)稱為 params的對(duì)象中。這個(gè)構(gòu)造函數(shù)迭代這個(gè) Map并針對(duì)每個(gè) “鍵 / 值” 對(duì)調(diào)用 setProperty方法。

檢查一下 setProperty方法就會(huì)發(fā)現(xiàn) “鍵” 設(shè)置為底層 entity的屬性名稱,而對(duì)應(yīng)的 “值” 是該 entity的值。

Groovy 技巧

如前所述,Groovy 的動(dòng)態(tài)特性允許我通過 get和 set Property方法捕獲對(duì)不存在的屬性的方法調(diào)用。這樣,清單 2 中的 Model的子類不必定義它們自己的屬性 —它們只是將對(duì)一個(gè)屬性的所有調(diào)用委托給這個(gè)底層 entity對(duì)象。

清單 2 中的代碼執(zhí)行了一些特定于 Groovy 的操作,值得一提。首先,可以通過在一個(gè)屬性前面附加一個(gè) @來繞過該屬性的訪問器方法。我必須對(duì)構(gòu)造函數(shù)中的 entity對(duì)象引用執(zhí)行上述操作,否則我將調(diào)用 setProperty方法。很明顯,在這個(gè)關(guān)頭調(diào)用 setProperty將打破這種模式,因?yàn)? setProperty方法中的 entity變量將是 null。

其次,構(gòu)造函數(shù)中的調(diào)用 this.getClass().simpleName將設(shè)置 entity的 “種類” —— simpleName屬性將生成一個(gè)不帶包前綴的子類名稱(注意,simpleName的確是對(duì) getSimpleName的調(diào)用,但 Groovy 允許我不通過對(duì)應(yīng)的 JavaBeans 式的方法調(diào)用來嘗試訪問一個(gè)屬性)。

最后,如果對(duì) id屬性(即,對(duì)象的鍵)進(jìn)行一個(gè)調(diào)用,getProperty方法很智能,能夠詢問底層 key以獲取它的 id。在 Google App Engine 中,entities的 key屬性將自動(dòng)生成。

Race 子類

定義 Race子類很簡(jiǎn)單,如清單 3 所示:

清單3. 一個(gè) Race 子類

  1. package com.b50.nosql
  2. class Race extends Model {
  3. public Race(params){
  4. super(params)
  5. }
  6. }

當(dāng)一個(gè)子類使用一列參數(shù)(即一個(gè)包含多個(gè) “鍵 / 值” 對(duì)的 Map)實(shí)例化時(shí),一個(gè)對(duì)應(yīng)的 entity將在內(nèi)存中創(chuàng)建。要持久存儲(chǔ)它,只需調(diào)用 save方法。

清單4. 創(chuàng)建一個(gè) Race 實(shí)例并將其保存到 GAE 的數(shù)據(jù)存儲(chǔ)

  1. import com.b50.nosql.Runner
  2. def iparams = [:]
  3. def formatter = new SimpleDateFormat("MM/dd/yyyy")
  4. def rdate = formatter.parse("04/17/2010")
  5. iparams["name"] = "Charlottesville Marathon"
  6. iparams["date"] = rdate
  7. iparams["distance"] = 26.2 as double
  8. def race = new Race(iparams)
  9. race.save()

清單4 是一個(gè) Groovlet,其中,一個(gè) Map(稱為 iparams)創(chuàng)建為帶有 3 個(gè)屬性 ——一次比賽的名稱、日期和距離。(注意,在 Groovy 中,一個(gè)空白 Map通過 [:]創(chuàng)建。)Race的一個(gè)新實(shí)例被創(chuàng)建,然后通過 save方法存儲(chǔ)到底層數(shù)據(jù)存儲(chǔ)。

可以通過 Google App Engine 控制臺(tái)來查看底層數(shù)據(jù)存儲(chǔ),確保我的數(shù)據(jù)的確在那里,如圖 2 所示:

圖2. 查看新創(chuàng)建的Race

查找程序方法生成持久存儲(chǔ)的實(shí)體

現(xiàn)在我已經(jīng)存儲(chǔ)了一個(gè) Entity,擁有查找它的能力將有所幫助。接下來,我可以添加一個(gè) “查找程序” 方法。在本例中,我將把這個(gè) “查找程序” 方法創(chuàng)建為一個(gè)類方法(static)并且允許通過名稱查找這些 Race(即基于 name屬性搜索)。稍后,總是可以通過其他屬性添加其他查找程序。

我還打算對(duì)我的查找程序采用一個(gè)慣例,即指定:任何名稱中不帶單詞 all的查找程序都企圖找到 一個(gè)實(shí)例。名稱中包含單詞 all的查找程序(如 findAllByName)能夠返回一個(gè)實(shí)例 Collection或 List。清單 5 展示了 findByName查找程序:

清單5. 一個(gè)基于 Entity 名稱搜索的簡(jiǎn)單查找程序

  1. static def findByName(name){
  2. def query = new Query(Race.class.simpleName)
  3. query.addFilter("name", Query.FilterOperator.EQUAL, name)
  4. def preparedQuery = this.datastore.prepare(query)
  5. if(preparedQuery.countEntities() > 1){
  6. return new Race(preparedQuery.asList(withLimit(1))[0])
  7. }else{
  8. return new Race(preparedQuery.asSingleEntity())
  9. }
  10. }

這個(gè)簡(jiǎn)單的查找程序使用 Google App Engine 的 Query和 PreparedQuery類型來查找一個(gè)類型為 “Race” 的實(shí)體,其名稱(完全)等同于傳入的名稱。如果有超過一個(gè) Race符合這個(gè)標(biāo)準(zhǔn),查找程序?qū)⒎祷匾粋€(gè)列表的第一項(xiàng),這是分頁限制 1(withLimit(1))所指定的。

對(duì)應(yīng)的 findAllByName與上述方法類似,但添加了一個(gè)參數(shù),指定 您想要的實(shí)體個(gè)數(shù),如清單 6 所示:

清單 6. 通過名稱找到全部實(shí)體

  1. static def findAllByName(name, pagination=10){
  2. def query = new Query(Race.class.getSimpleName())
  3. query.addFilter("name", Query.FilterOperator.EQUAL, name)
  4. def preparedQuery = this.datastore.prepare(query)
  5. def entities = preparedQuery.asList(withLimit(pagination as int))
  6. return entities.collect { new Race(it as Entity) }
  7. }

與前面定義的查找程序類似,findAllByName通過名稱找到 Race實(shí)例,但是它返回 所有 Race。順便說一下,Groovy 的 collect方法非常靈活:它允許刪除創(chuàng)建 Race實(shí)例的對(duì)應(yīng)的循環(huán)。注意,Groovy 還支持方法參數(shù)的默認(rèn)值;這樣,如果我沒有傳入第 2 個(gè)值,pagination將擁有值 10。

清單7. 查找程序的實(shí)際運(yùn)行

  1. def nrace = Race.findByName("Charlottesville Marathon")
  2. assert nrace.distance == 26.2
  3. def races = Race.findAllByName("Charlottesville Marathon")
  4. assert races.class == ArrayList.class

清單 7中的查找程序按照既定的方式運(yùn)行:findByName返回一個(gè)實(shí)例,而 findAllByName返回一個(gè) Collection(假定有多個(gè) “Charlottesville Marathon”)。

“參賽者” 對(duì)象沒有太多不同

現(xiàn)在我已能夠創(chuàng)建并找到 Race的實(shí)例,現(xiàn)在可以創(chuàng)建一個(gè)快速的 Runner對(duì)象了。這個(gè)過程與創(chuàng)建初始的 Race實(shí)例一樣簡(jiǎn)單,只需如清單 8 所示擴(kuò)展 Model:

清單 8. 創(chuàng)建一個(gè)參賽者很簡(jiǎn)單

  1. package com.b50.nosql
  2. class Runner extends Model{
  3. public Runner(params){
  4. super(params)
  5. }
  6. }

看看 清單 8,我感覺自己幾乎完成工作了。但是,我還需創(chuàng)建參賽者和比賽之間的鏈接。當(dāng)然,我將把它建模為一個(gè) “多對(duì)多” 關(guān)系,因?yàn)槲蚁M业膮①愓呖梢詤⒓佣囗?xiàng)比賽。

沒有架構(gòu)的域建模

Google App Engine 在 Bigtable 上面的抽象不是一個(gè)面向?qū)ο蟮某橄螅患矗也荒茉瓨哟鎯?chǔ)關(guān)系,但可以共享鍵。因此,為建模多個(gè) Race和多個(gè) Runner之間的關(guān)系,我將在每個(gè) Race實(shí)例中存儲(chǔ)一列 Runner鍵,并在每個(gè) Runner實(shí)例中存儲(chǔ)一列 Race鍵。

我必須對(duì)我的鍵共享機(jī)制添加一點(diǎn)邏輯,但是,因?yàn)槲蚁M傻?API 比較自然 —我不想詢問一個(gè) Race以獲取一列 Runner鍵,因此我想要一列 Runner。幸運(yùn)的是,這并不難實(shí)現(xiàn)。

在清單 9 中,我已經(jīng)添加了兩個(gè)方法到 Race實(shí)例。但一個(gè) Runner實(shí)例被傳遞到 addRunner方法時(shí),它的對(duì)應(yīng) id被添加到底層 entity的 runners屬性中駐留的 id的 Collection。如果有一個(gè)現(xiàn)成的 runners的 collection,則新的 Runner實(shí)例鍵將添加到它;否則,將創(chuàng)建一個(gè)新的 Collection,且這個(gè) Runner的鍵(實(shí)體上的 id屬性)將添加到它。

清單9. 添加并檢索參賽者

  1. def addRunner(runner){
  2. if(this.@entity.runners){
  3. this.@entity.runners << runner.id
  4. }else{
  5. this.@entity.runners = [runner.id]
  6. }
  7. }
  8. def getRunners(){
  9. return this.@entity.runners.collect {
  10. new Runner( this.getEntity(Runner.class.simpleName, it) )
  11. }
  12. }

當(dāng)清單 9 中的 getRunners方法調(diào)用時(shí),一個(gè) Runner實(shí)例集合將從底層的 id集合創(chuàng)建。這樣,一個(gè)新方法(getEntity)將在 Model類中創(chuàng)建,如清單 10 所示:

清單10. 從一個(gè)id 創(chuàng)建一個(gè)實(shí)體

  1. def getEntity(entityType, id){
  2. def key = KeyFactory.createKey(entityType, id)
  3. return this.@datastore.get(key)
  4. }

getEntity方法使用 Google 的 KeyFactory類來創(chuàng)建底層鍵,它可以用于查找數(shù)據(jù)存儲(chǔ)中的一個(gè)單獨(dú)實(shí)體。

最后,定義一個(gè)新的構(gòu)造函數(shù)來接受一個(gè)實(shí)體類型,如清單 11 所示:

清單11. 一個(gè)新添加的構(gòu)造函數(shù)

  1. public Model(Entity entity){
  2. this.@entity = entity
  3. }

如清單 9、10和 11、以及 圖 1的對(duì)象模型所示,我可以將一個(gè) Runner添加到任一 Race,也可以從任一Race獲取一列 Runner實(shí)例。在清單 12 中,我在這個(gè)等式的 Runner方上創(chuàng)建了一個(gè)類似的聯(lián)系。清單 12 展示了 Runner類的新方法。

清單12. 參賽者及其比賽

  1. def addRace(race){
  2. if(this.@entity.races){
  3. this.@entity.races << race.id
  4. }else{
  5. this.@entity.races = [race.id]
  6. }
  7. }
  8. def getRaces(){
  9. return this.@entity.races.collect {
  10. new Race( this.getEntity(Race.class.simpleName, it) )
  11. }
  12. }

這樣,我就使用一個(gè)無模式數(shù)據(jù)存儲(chǔ)創(chuàng)建了兩個(gè)域?qū)ο蟆?/p>

通過一些參賽者完成這個(gè)比賽

此前我所做的是創(chuàng)建一個(gè) Runner實(shí)例并將其添加到一個(gè) Race。如果我希望這個(gè)關(guān)系是雙向的,如圖1中我的對(duì)象模型所示,那么我也可以添加一些 Race實(shí)例到一些Runner,如清單 13 所示:

清單 13. 參加多個(gè)比賽的多個(gè)參賽者

  1. def runner = new Runner([fname:"Chris", lname:"Smith", date:34])
  2. runner.save()
  3. race.addRunner(runner)
  4. race.save()
  5. runner.addRace(race)
  6. runner.save()

將一個(gè)新的 Runner添加到 race并添加對(duì)Race的save的調(diào)用后,這個(gè)數(shù)據(jù)存儲(chǔ)已使用一列ID 更新,如圖 3 中的屏幕快照所示:

圖3. 查看一項(xiàng)比賽中的多個(gè)參賽者的新屬性

通過仔細(xì)檢查Google App Engine 中的數(shù)據(jù),可以看到,一個(gè)Race實(shí)體現(xiàn)在擁有了一個(gè)Runners 的list,如圖 4 所示。

圖4. 查看新的參賽者列表

同樣,在將一個(gè) Race添加到一個(gè)新創(chuàng)建的 Runner實(shí)例之前,這個(gè)屬性并不存在,如圖 5 所示。

5. 一個(gè)沒有比賽的參賽者

但是,將一個(gè) Race關(guān)聯(lián)到一個(gè) Runner后,數(shù)據(jù)存儲(chǔ)將添加新的 races ids 的 list。

圖6. 一個(gè)參加比賽的參賽者

無模式數(shù)據(jù)存儲(chǔ)的靈活性正在刷新 —屬性按照需要自動(dòng)添加到底層存儲(chǔ)。作為開發(fā)人員,我無須更新或更改架構(gòu),更談不上部署架構(gòu)了!

NoSQL 的利弊

當(dāng)然,無模式數(shù)據(jù)建模也有利有弊。回顧上面的比賽應(yīng)用程序,它的一個(gè)優(yōu)勢(shì)是非常靈活。如果我 決定將一個(gè)新屬性(比如 SSN)添加到一個(gè) Runner,我不必進(jìn)行大幅更改 —事實(shí)上,如果我將該屬性包含在構(gòu)造函數(shù)的參數(shù)中,那么它就會(huì)自動(dòng)添加。對(duì)那些沒有使用一個(gè) SSN 創(chuàng)建的舊實(shí)例而言,發(fā)生了什么事情?什么也沒發(fā)生!它們擁有一個(gè)值為 null的字段。

另一方面,我已經(jīng)明確表明要犧牲一致性和完整性來換取效率。這個(gè)應(yīng)用程序的當(dāng)前數(shù)據(jù)架構(gòu)沒有 向我施加任何限制 —理論上我可以為同一個(gè)對(duì)象創(chuàng)建無限個(gè)實(shí)例。在 Google App Engine 引擎的鍵處理機(jī)制下,它們都有惟一的鍵,但其他屬性都是一致的。更糟糕的是,級(jí)聯(lián)刪除不存在,因此如果我使用相同的技術(shù)來建模一個(gè) “一對(duì)多” 關(guān)系并刪除父節(jié)點(diǎn),那么我得到一些無效的子節(jié)點(diǎn)。當(dāng)然,我可以實(shí)現(xiàn)自己的完整性檢查 —但關(guān)鍵是,我必須親自動(dòng)手(就像完成其他任務(wù)一樣)。

使用無模式數(shù)據(jù)存儲(chǔ)需要嚴(yán)明的紀(jì)律。如果我創(chuàng)建各種類型的 Races —有些有名稱,有些沒有,有些有 date屬性,而另一些有 race_date屬性 —那么我只是在搬起石頭砸自己(或使用我的代碼的人)的腳。

當(dāng)然,也有可能聯(lián)合使用 JDO、JPA 和 Google App Engine。在多個(gè)項(xiàng)目上使用過關(guān)系模型和無模式模型后,我可以說 Gaelyk 的低級(jí) API 最靈活,使用最方便。使用 Gaelyk 的另一個(gè)好處是能夠深入了解 Bigtable 和一般的無模式數(shù)據(jù)存儲(chǔ)。

結(jié)束語

流行時(shí)尚來了又去,有時(shí)無需理會(huì)它們(明智的建議來自一個(gè)衣櫥里滿是休閑服的家伙)。但 NoSQL 看起來不太像一種時(shí)尚,更像是高度可伸縮的 Web 應(yīng)用程序開發(fā)的一個(gè)新興基礎(chǔ)。NoSQL 數(shù)據(jù)庫(kù)不會(huì)替代 RDBMS,但是,它們將補(bǔ)充它。無數(shù)成功的工具和框架基于關(guān)系數(shù)據(jù)庫(kù),RDBMSs 本身似乎沒有面臨過時(shí)的危險(xiǎn)。

總之,NoSQL 數(shù)據(jù)庫(kù)的作用是向?qū)ο?mdash;—關(guān)系數(shù)據(jù)模型提供一個(gè)及時(shí)的替代方案。它們向我們展示,有些事情是可行的,并且對(duì)于一些特定的、高度強(qiáng)制的用例甚至更好。無模式 數(shù)據(jù)庫(kù)最適用于需要高速數(shù)據(jù)檢索和可伸縮性的多節(jié)點(diǎn) Web 應(yīng)用程序。它們還有一個(gè)極好的副作用,即允許開發(fā)人員從一個(gè)面向域的視角、而不是關(guān)系視角進(jìn)行數(shù)據(jù)建模。

熱詞搜索:

上一篇:開源非關(guān)系型數(shù)據(jù)庫(kù)Hibari云雀發(fā)布
下一篇:NoSQL生態(tài)系統(tǒng)大檢閱 不同特性大比拼

分享到: 收藏
国产一级一区二区_segui88久久综合9999_97久久夜色精品国产_欧美色网一区二区
亚洲五月婷婷| 亚洲国产精品久久久久久女王| 欧美亚洲专区| 韩国精品久久久999| 尤妮丝一区二区裸体视频| 国产精品久线观看视频| 国产精品久久久一区二区| 激情自拍一区| 中文日韩在线视频| 亚洲欧美bt| 亚洲特色特黄| 欧美激情一区在线观看| 久久一区二区精品| 老色鬼精品视频在线观看播放| 欧美日韩在线看| 国产亚洲欧美日韩日本| 国产精品久久久久久久久久久久 | 黑人操亚洲美女惩罚| 国产视频欧美| 国内精品免费午夜毛片| 在线综合视频| 欧美一区深夜视频| 99re热精品| 有码中文亚洲精品| 久久综合图片| 91久久精品一区二区别| 国产精品红桃| 欧美一区二区在线免费观看| 国产一区在线视频| 欧美激情1区2区3区| 久久精品午夜| 欧美成人福利视频| 午夜精品亚洲| 午夜精品福利在线| 欧美图区在线视频| 亚洲欧美另类综合偷拍| 欧美精品v国产精品v日韩精品| 亚洲第一搞黄网站| 在线免费一区三区| 欧美大尺度在线| 亚洲精品美女在线观看| 久久久av毛片精品| 亚洲精品欧美极品| 国产精品嫩草影院av蜜臀| 久久亚洲视频| 亚洲欧洲视频| 国产日韩在线视频| 久久久国产精彩视频美女艺术照福利| 影音先锋日韩有码| 国产精品久久999| 亚洲一级影院| 国产一区二区三区的电影| 欧美99在线视频观看| 在线视频欧美日韩| 欧美jizzhd精品欧美喷水| 一本久道久久久| 国产偷国产偷亚洲高清97cao| 欧美精品日韩精品| 91久久亚洲| 国产一区二区三区黄视频| 欧美巨乳波霸| 麻豆av一区二区三区| 国产欧美日韩在线视频| 欧美激情视频一区二区三区免费 | 欧美黄在线观看| 欧美大片网址| 91久久久久久久久| 欧美丰满高潮xxxx喷水动漫| 亚洲美女精品一区| 国产一区av在线| 亚洲曰本av电影| 久久xxxx精品视频| 午夜精品999| 精品福利电影| 国产精品拍天天在线| 欧美在线视频日韩| 亚洲香蕉伊综合在人在线视看| 国产日韩一区二区三区| 欧美一级淫片播放口| 一区二区三区日韩欧美精品| 国产亚洲精品成人av久久ww| 亚洲性视频网站| 欧美精品三级| 性做久久久久久| 国产精品自拍在线| 日韩亚洲欧美一区二区三区| 国产精品日本| 久久夜色精品国产亚洲aⅴ| 一区在线观看| 欧美精品1区2区3区| 亚洲一区二区三区四区五区午夜 | 国产三级精品三级| 久久精品一区二区三区不卡牛牛| 一区二区三区视频在线| 国产精品qvod| 欧美视频1区| 欧美成人三级在线| 精品999网站| 亚洲毛片在线观看.| 91久久黄色| 91久久精品国产| 亚洲精品视频免费观看| 国产精品免费视频xxxx| a91a精品视频在线观看| 国产精品美腿一区在线看| 国产精品一区亚洲| 一本久道久久综合中文字幕| 国内精品一区二区| 欧美高清不卡在线| 欧美大片免费久久精品三p| 欧美成人国产| 欧美大片在线观看一区二区| 欧美精品一区在线观看| 亚洲国产精品久久久久婷婷884| 国产日韩欧美| 亚洲国产精品第一区二区| 亚洲欧洲精品一区二区三区波多野1战4| 在线日本高清免费不卡| 亚洲图片欧美午夜| 欧美在线视频一区二区| 欧美高清在线一区| 国产精品制服诱惑| 亚洲高清一二三区| 亚洲一区二区三区精品动漫| 午夜精品久久久久| 久久xxxx精品视频| 欧美精品一区三区| 国产精品一香蕉国产线看观看| 国产一区二区三区直播精品电影| 亚洲欧洲一区二区在线观看 | 亚洲福利视频一区| 99国产精品视频免费观看| 久久精品国产99国产精品澳门| 欧美日本免费| 激情久久综合| 午夜精品国产| 欧美日在线观看| 伊人久久婷婷色综合98网| 亚洲一区999| 欧美日本乱大交xxxxx| 精品91视频| 欧美亚洲日本网站| 国产精品白丝jk黑袜喷水| 亚洲黄色性网站| 久久亚洲一区二区三区四区| 国产精品美女在线| 亚洲免费大片| 欧美成在线视频| 在线观看一区二区精品视频| 午夜日韩福利| 国产精品国产自产拍高清av王其| 91久久亚洲| 美国十次了思思久久精品导航| 国产欧美日韩麻豆91| 亚洲一区二区三区在线观看视频| 欧美日韩人人澡狠狠躁视频| 亚洲黄色高清| 久久久91精品国产一区二区精品| 国产综合香蕉五月婷在线| 久久99在线观看| 亚洲第一主播视频| 欧美精品日韩精品| 午夜伦欧美伦电影理论片| 狠狠久久亚洲欧美| 在线视频日韩| 国产精品日日摸夜夜摸av| 亚洲午夜激情| 国产精品一区二区久激情瑜伽| 免费国产自线拍一欧美视频| 久久中文字幕一区二区三区| 欧美喷潮久久久xxxxx| 亚洲高清资源综合久久精品| 欧美中文在线视频| 国产酒店精品激情| 一区二区三区久久久| 一区二区三区成人| 国产精品福利av| 欧美电影在线免费观看网站| 亚洲图片你懂的| 悠悠资源网亚洲青| 国产精品久久久久av免费| 久久精品国产99国产精品澳门| 亚洲精品女av网站| 国产精品网站在线播放| 久久久久久久波多野高潮日日| 亚洲黄色尤物视频| 国产一区二区主播在线| 欧美激情欧美狂野欧美精品 | 免费观看在线综合| 中国亚洲黄色| 国产自产在线视频一区| 国产九色精品成人porny| 久久久国产精品一区| 在线视频欧美日韩| 国产综合色产| 欧美日韩综合在线| 亚洲成人资源| 国产一区二区黄| 国产精品一区视频| 国产精品日韩|