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

掃一掃
關注微信公眾號

一種新的思維方式?NoSQL數據建模
2010-07-29   網絡

關系數據庫已經統治數據存儲30 多年了,但是無模式(或NoSQL)數據庫的逐漸流行表明變化正在發生。盡管 RDBMS 為在傳統的客戶端服務器架構中存儲數據提供了一個堅實的基礎,但它不能輕松地(或便宜地)擴展到多個節點。在高度可伸縮的 Web 應用程序(比如 Facebook 和 Twitter)的時代,這是一個非常不幸的弱點。

盡管關系數據庫的早期替代方案(還記得面向對象的數據庫嗎?)不能解決真正緊急的問 題,NoSQL 數據庫(比如 Google 的 Bigtable 和 Amazon 的 SimpleDB)卻作為對 Web 的高可伸縮性需求的直接響應而崛起。本質上,NoSQL 可能是一個殺手問題的殺手應用程序 —隨著 Web 2.0 的演變,Web 應用程序開發人員可能會遇到更多,而不是更少這樣的應用程序。

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

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 的對象持久存儲

  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

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

返回比賽

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

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

圖1. 比賽和參賽者

如果我要使用一個關系表結構來設計這個關系,至少需要 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 基類

  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. }

注意抽象類如何定義一個構造函數,該函數接收屬性的一個 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 子類

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

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

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

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

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

圖2. 查看新創建的Race

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

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

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

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

  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. }

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

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

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

  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實例,但是它返回 所有 Race。順便說一下,Groovy 的 collect方法非常靈活:它允許刪除創建 Race實例的對應的循環。注意,Groovy 還支持方法參數的默認值;這樣,如果我沒有傳入第 2 個值,pagination將擁有值 10。

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

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

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

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

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

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

看看 清單 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. 添加并檢索參賽者

  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. }

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

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

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

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

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

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

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

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

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

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

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

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

  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()

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

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

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

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

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

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

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

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

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

NoSQL 的利弊

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

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

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

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

結束語

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

總之,NoSQL 數據庫的作用是向對象——關系數據模型提供一個及時的替代方案。它們向我們展示,有些事情是可行的,并且對于一些特定的、高度強制的用例甚至更好。無模式 數據庫最適用于需要高速數據檢索和可伸縮性的多節點 Web 應用程序。它們還有一個極好的副作用,即允許開發人員從一個面向域的視角、而不是關系視角進行數據建模。

熱詞搜索:

上一篇:開源非關系型數據庫Hibari云雀發布
下一篇:NoSQL生態系統大檢閱 不同特性大比拼

分享到: 收藏
国产一级一区二区_segui88久久综合9999_97久久夜色精品国产_欧美色网一区二区
91精品国产全国免费观看| 99国产精品视频免费观看| 国模冰冰炮一区二区| 欧美一级生活片| 韩国精品在线观看| 国产精品久久久99| 在线播放一区二区三区| 毛片一区二区三区| 国产亚洲综合在线| 91免费观看视频在线| 亚洲aaa精品| 国产网红主播福利一区二区| 色综合天天综合网国产成人综合天| 亚洲二区在线视频| 久久久精品人体av艺术| 欧美自拍偷拍一区| 美国毛片一区二区三区| 综合久久久久综合| 精品美女一区二区| 色婷婷av一区二区三区软件| 久久精品久久99精品久久| 亚洲日本在线a| 久久久久国产精品人| 欧美色手机在线观看| 国产精品一级二级三级| 亚洲成av人**亚洲成av**| 国产精品网站一区| 欧美变态tickling挠脚心| 欧美视频三区在线播放| 不卡一区二区在线| 国产一区二区三区在线观看免费视频 | 国产精品99久久久久久久vr| 亚洲一线二线三线久久久| 国产欧美一区视频| 日韩欧美色综合| 欧美蜜桃一区二区三区| 91啦中文在线观看| 97久久久精品综合88久久| 国产一区二区三区最好精华液| 日韩成人一区二区| 亚洲第一主播视频| 亚洲图片欧美一区| 亚洲四区在线观看| 中文字幕在线观看不卡视频| 欧美精品一区二区高清在线观看| 欧美日韩亚洲丝袜制服| 欧美在线一区二区三区| 99久久er热在这里只有精品15| 另类小说色综合网站| 蜜桃av噜噜一区| 久久国产生活片100| 精品在线一区二区三区| 美日韩一区二区三区| 精品一区二区三区久久久| 久久国产精品无码网站| 韩国欧美国产1区| 国产剧情av麻豆香蕉精品| 国精品**一区二区三区在线蜜桃| 麻豆免费精品视频| 国产一区二区三区不卡在线观看 | 久久女同精品一区二区| 久久久精品黄色| 国产精品久久网站| 亚洲日本va午夜在线电影| 最新不卡av在线| 一区二区激情视频| 日韩激情视频在线观看| 久久精品国产网站| 国产精品一区二区果冻传媒| 久草这里只有精品视频| 成人一区二区三区| 91麻豆福利精品推荐| 欧美日韩精品高清| 精品久久五月天| 中文字幕精品—区二区四季| 亚洲人吸女人奶水| 午夜视频在线观看一区二区三区| 日韩高清在线不卡| 国产v日产∨综合v精品视频| 91年精品国产| 欧美精品v国产精品v日韩精品| 精品入口麻豆88视频| 亚洲国产精品av| 亚洲无人区一区| 国产成人日日夜夜| 在线视频欧美精品| 久久综合九色综合97婷婷女人| 欧美激情综合五月色丁香| 一区二区三区四区蜜桃| 精品亚洲成a人| 欧日韩精品视频| 久久久噜噜噜久久人人看| 亚洲女性喷水在线观看一区| 男女男精品视频网| 99综合影院在线| 精品久久久久久久久久久久久久久| 国产精品青草久久| 免费观看成人av| 在线欧美日韩精品| 国产亚洲成aⅴ人片在线观看| 亚洲国产美女搞黄色| 国产精品一级在线| 日韩一区二区影院| 亚洲午夜激情av| 成人网在线免费视频| 精品日韩在线一区| 视频一区视频二区在线观看| av在线不卡网| 久久精品日韩一区二区三区| 日韩国产欧美一区二区三区| 在线观看亚洲a| 中文字幕亚洲在| 成人看片黄a免费看在线| 欧美一级久久久久久久大片| 亚洲欧洲精品一区二区精品久久久 | 精品嫩草影院久久| 午夜精品久久久久久久蜜桃app| 成人av网在线| 久久久久国色av免费看影院| 精品一区二区日韩| 欧美日韩在线播放三区四区| 亚洲美女一区二区三区| 91丝袜美腿高跟国产极品老师 | 国产精品福利一区二区三区| 国产酒店精品激情| 精品久久久久久亚洲综合网| 麻豆精品视频在线观看免费| 欧美一区二区三区在线视频| 婷婷国产在线综合| 欧美色图一区二区三区| 亚洲靠逼com| 日本高清无吗v一区| 亚洲精品国产精华液| 在线视频欧美精品| 亚洲一区av在线| 欧美在线视频不卡| 亚洲成av人片一区二区| 欧美丰满少妇xxxxx高潮对白| 日本成人在线网站| 精品日韩成人av| 精一区二区三区| 国产精品久久久久久久久久免费看| 成人午夜激情视频| 最新日韩av在线| 在线视频国内自拍亚洲视频| 午夜国产精品影院在线观看| 欧美一级久久久| 国产不卡在线一区| 亚洲人成网站精品片在线观看| 欧美在线免费视屏| 欧美电视剧免费全集观看| 欧美日韩国产色站一区二区三区| 五月天激情综合| 亚欧色一区w666天堂| 国产一区二区三区不卡在线观看 | 久久一区二区三区国产精品| 久久99精品国产.久久久久久| 久久综合九色欧美综合狠狠| 成人av免费在线观看| 亚洲综合在线五月| 精品久久五月天| 色拍拍在线精品视频8848| 日本系列欧美系列| 国产午夜精品久久久久久久| 91久久线看在观草草青青 | 国产一区二区视频在线播放| 中文字幕一区二区三区色视频| 欧美亚洲综合一区| 成人综合在线观看| 丝袜诱惑亚洲看片| 国产精品视频你懂的| 欧美无乱码久久久免费午夜一区 | 亚洲免费观看高清在线观看| 欧美一区在线视频| 不卡的看片网站| 蜜臀av一区二区在线观看 | 成人动漫一区二区在线| 青青国产91久久久久久| 自拍偷拍国产亚洲| 久久亚洲一区二区三区明星换脸 | 精品制服美女久久| 亚洲国产日韩在线一区模特| 国产精品国产三级国产三级人妇| 91精品国产色综合久久ai换脸 | 一区二区三区在线影院| 欧美午夜电影网| 国产精品1024| 偷偷要91色婷婷| 亚洲免费观看高清| 日本一区二区视频在线| 日韩一区二区三区高清免费看看| 色婷婷狠狠综合| 91在线观看免费视频| 成人动漫中文字幕| 国产福利不卡视频| 国产精品一区二区免费不卡 | 中文字幕一区二区三| 久久久久久97三级| 亚洲精品一区在线观看| 日韩欧美一区二区久久婷婷|