亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

NoSQL數據建模的示例分析

發布時間:2022-01-15 10:32:15 來源:億速云 閱讀:163 作者:小新 欄目:數據庫

這篇文章將為大家詳細講解有關NoSQL數據建模的示例分析,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

NoSQL:一種新的思維方式?

當開發人員談論非關系或 NoSQL 數據庫時,經常提到的第一件事是他們需要改變思維方式。我認為,那實際上取決于您的初始數據建模方法。如果您習慣通過首先建模數據庫結構(即首先確定表及其關聯關系)來設計應用程序,那么使用一個無模式數據存儲(比如 Bigtable)來進行數據建模則需要您重新思考您的做事方式。但是,如果您從域模型開始設計您的應用程序,那么 Bigtable 的無模式結構將看起來更自然。

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

從關系數據模型遷移到無模式數據模型的相對復雜程度取決于您的方法:即您從基于關系的設計開始還是從基于域的設計開始。當您遷移到 CouchDB 或 Bigtable 這樣的數據庫時,您 的確會喪失 Hibernate(至少現在)這樣的成熟的持久存儲平臺的順暢感覺。另一方面,您卻擁有能夠親自構建它的 “綠地效果”。在此過程中,您將深入了解無模式數據存儲。

實體和關系

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

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

清單1. 使用 Entity 的對象持久存儲

def ticket = new Entity("ticket")   ticket.officer = params.officer   ticket.license = params.plate   ticket.issuseDate = offensedate   ticket.location = params.location   ticket.notes = params.notes   ticket.offense = params.offense

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

返回比賽

我不會重復 Gaelyk 簡介中的那個票據示例,相反,為保持新鮮感,我將在本文中使用一個賽跑主題,并構建一個應用程序來展示即將討論的技術。

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

圖1. 比賽和參賽者

NoSQL數據建模的示例分析

如果我要使用一個關系表結構來設計這個關系,至少需要 3 個表:第 3 表將是鏈接一個 “多對多” 關系的聯接表。所幸我不必局限于關系數據模型。相反,我將使用 Gaelyk(和 Groovy 代碼)將這個 “多對多” 關系映射到 Google 針對 Google App Engine 的 Bigtable 抽象。事實上,Gaelyk 允許將 Entity當作 Map,這使得映射過程相當簡單。

無模式數據存儲的好處之一是無須事先知道所有事情,也就是說,與使用關系數據庫架構相比,可以更輕松地適應變化。(注意,我并非暗示不能更改架構;我只是說,可以更輕松地適應變化。)我不打算定義我的域對象上的屬性 —我將其推遲到 Groovy 的動態特性(實際上,這個特性允許創建針對 Google 的 Entity對象的域對象代理)。相反,我將把我的時間花費在確定如何查找對象并處理關系上。這是 NoSQL 和各種利用無模式數據存儲的框架還沒有內置的功能。

Model 基類

我將首先創建一個基類,用于容納 Entity對象的一個實例。然后,我將允許一些子類擁有一些動態屬性,這些動態屬性將通過 Groovy 的方便的 setProperty方法添加到對應的 Entity實例。setProperty針對對象中實際上不存在的任何屬性設置程序調用。(如果這聽起來聳人聽聞,不用擔心,您看到它的實際運行后就會明白。)

清單2展示了位于我的示例應用程序的一個 Model實例的第一個 stab:

清單2. 一個簡單的 Model 基類

package com.b50.nosql     import com.google.appengine.api.datastore.DatastoreServiceFactory    import com.google.appengine.api.datastore.Entity     abstract class Model {     def entity    static def datastore = DatastoreServiceFactory.datastoreService     public Model(){     super()    }     public Model(params){     this.@entity = new Entity(this.getClass().simpleName)     params.each{ key, val ->      this.setProperty key, val     }    }     def getProperty(String name) {     if(name.equals("id")){      return entity.key.id     }else{      return entity."${name}"   }    }     void setProperty(String name, value) {     entity."${name}" = value    }     def save(){     this.entity.save()    }     }

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

檢查一下 setProperty方法就會發現 “鍵” 設置為底層 entity的屬性名稱,而對應的 “值” 是該 entity的值。

Groovy 技巧

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

清單 2 中的代碼執行了一些特定于 Groovy 的操作,值得一提。首先,可以通過在一個屬性前面附加一個 @來繞過該屬性的訪問器方法。我必須對構造函數中的 entity對象引用執行上述操作,否則我將調用 setProperty方法。很明顯,在這個關頭調用 setProperty將打破這種模式,因為 setProperty方法中的 entity變量將是 null。

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

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

Race 子類

定義 Race子類很簡單,如清單 3 所示:

清單3. 一個 Race 子類

package com.b50.nosql    class Race extends Model {   public Race(params){    super(params)   }   }

當一個子類使用一列參數(即一個包含多個 “鍵 / 值” 對的 Map)實例化時,一個對應的 entity將在內存中創建。要持久存儲它,只需調用 save方法。

清單4. 創建一個 Race 實例并將其保存到 GAE 的數據存儲

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

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

可以通過 Google App Engine 控制臺來查看底層數據存儲,確保我的數據的確在那里,如圖 2 所示:

圖2. 查看新創建的Race

NoSQL數據建模的示例分析

查找程序方法生成持久存儲的實體

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

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

清單5. 一個基于 Entity 名稱搜索的簡單查找程序

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

這個簡單的查找程序使用 Google App Engine 的 Query和 PreparedQuery類型來查找一個類型為 “Race” 的實體,其名稱(完全)等同于傳入的名稱。如果有超過一個 Race符合這個標準,查找程序將返回一個列表的第一項,這是分頁限制 1(withLimit(1))所指定的。

對應的 findAllByName與上述方法類似,但添加了一個參數,指定 您想要的實體個數,如清單 6 所示:

清單 6. 通過名稱找到全部實體

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

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

清單7. 查找程序的實際運行

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

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

“參賽者” 對象沒有太多不同

現在我已能夠創建并找到 Race的實例,現在可以創建一個快速的 Runner對象了。這個過程與創建初始的 Race實例一樣簡單,只需如清單 8 所示擴展 Model:

清單 8. 創建一個參賽者很簡單

package com.b50.nosql    class Runner extends Model{   public Runner(params){    super(params)   }   }

看看 清單 8,我感覺自己幾乎完成工作了。但是,我還需創建參賽者和比賽之間的鏈接。當然,我將把它建模為一個 “多對多” 關系,因為我希望我的參賽者可以參加多項比賽。

沒有架構的域建模

Google App Engine 在 Bigtable 上面的抽象不是一個面向對象的抽象;即,我不能原樣存儲關系,但可以共享鍵。因此,為建模多個 Race和多個 Runner之間的關系,我將在每個 Race實例中存儲一列 Runner鍵,并在每個 Runner實例中存儲一列 Race鍵。

我必須對我的鍵共享機制添加一點邏輯,但是,因為我希望生成的 API 比較自然 —我不想詢問一個 Race以獲取一列 Runner鍵,因此我想要一列 Runner。幸運的是,這并不難實現。

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

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

def addRunner(runner){    if(this.@entity.runners){     this.@entity.runners << runner.id    }else{     this.@entity.runners = [runner.id]    }    }     def getRunners(){    return this.@entity.runners.collect {     new Runner( this.getEntity(Runner.class.simpleName, it) )    }    }

當清單 9 中的 getRunners方法調用時,一個 Runner實例集合將從底層的 id集合創建。這樣,一個新方法(getEntity)將在 Model類中創建,如清單 10 所示:

清單10. 從一個id 創建一個實體

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

getEntity方法使用 Google 的 KeyFactory類來創建底層鍵,它可以用于查找數據存儲中的一個單獨實體。

最后,定義一個新的構造函數來接受一個實體類型,如清單 11 所示:

清單11. 一個新添加的構造函數

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

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

清單12. 參賽者及其比賽

def addRace(race){    if(this.@entity.races){     this.@entity.races << race.id    }else{     this.@entity.races = [race.id]    }    }     def getRaces(){    return this.@entity.races.collect {     new Race( this.getEntity(Race.class.simpleName, it) )    }    }

這樣,我就使用一個無模式數據存儲創建了兩個域對象。

通過一些參賽者完成這個比賽

此前我所做的是創建一個 Runner實例并將其添加到一個 Race。如果我希望這個關系是雙向的,如圖1中我的對象模型所示,那么我也可以添加一些 Race實例到一些Runner,如清單 13 所示:

清單 13. 參加多個比賽的多個參賽者

def runner = new Runner([fname:"Chris", lname:"Smith", date:34])    runner.save()     race.addRunner(runner)    race.save()     runner.addRace(race)    runner.save()

將一個新的 Runner添加到 race并添加對Race的save的調用后,這個數據存儲已使用一列ID 更新,如圖 3 中的屏幕快照所示:

圖3. 查看一項比賽中的多個參賽者的新屬性

NoSQL數據建模的示例分析

通過仔細檢查Google App Engine 中的數據,可以看到,一個Race實體現在擁有了一個Runners 的list,如圖 4 所示。

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

NoSQL數據建模的示例分析

同樣,在將一個 Race添加到一個新創建的 Runner實例之前,這個屬性并不存在,如圖 5 所示。

5. 一個沒有比賽的參賽者

NoSQL數據建模的示例分析

但是,將一個 Race關聯到一個 Runner后,數據存儲將添加新的 races ids 的 list。

圖6. 一個參加比賽的參賽者

NoSQL數據建模的示例分析

無模式數據存儲的靈活性正在刷新 —屬性按照需要自動添加到底層存儲。作為開發人員,我無須更新或更改架構,更談不上部署架構了!

NoSQL 的利弊

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

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

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

當然,也有可能聯合使用 JDO、JPA 和 Google App Engine。在多個項目上使用過關系模型和無模式模型后,我可以說 Gaelyk 的低級 API 最靈活,使用最方便。使用 Gaelyk 的另一個好處是能夠深入了解 Bigtable 和一般的無模式數據存儲。

關于“NoSQL數據建模的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

鄂托克前旗| 瑞昌市| 陕西省| 望城县| 万州区| 焦作市| 古田县| 辽宁省| 兴隆县| 江永县| 九台市| 全州县| 余干县| 巴里| 大同市| 普格县| 方城县| 都兰县| 苍南县| 海口市| 金昌市| 扶绥县| 潜江市| 岗巴县| 五峰| 大埔区| 定结县| 榆中县| 夏河县| 泾阳县| 宝丰县| 略阳县| 周宁县| 轮台县| 蒙阴县| 鄂伦春自治旗| 洪洞县| 乃东县| 黔东| 安丘市| 湾仔区|