亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

為什么 FleaPHP 使用 Table Data Gateway 代替

系統(tǒng) 3056 0

[原文地址: http://www.dualface.com/blog/?p=357 ]

  許多開發(fā)者很疑惑為什么 FleaPHP 以高效開發(fā)為目標(biāo),卻沒有提供 Active Record 模式。本文嘗試詳細(xì)闡述這個問題。

Active Record 是什么?

  Active Record 模式中文名為“活動記錄”,在《企業(yè)應(yīng)用架構(gòu)模式》(PoEAA)一書中定義如下:

活動記錄(Active Record):一個對象,它包裝數(shù)據(jù)庫表或視圖中的某一行,封裝數(shù)據(jù)庫訪問,并在這些數(shù)據(jù)上增加了領(lǐng)域邏輯。

  舉個例子來說,一個圖書數(shù)據(jù)表,每一條記錄就是一本圖書的信息。那么采用 Active Record 時,每一本圖書就是一個 Active Record 對象實(shí)例。

Active Record 因 Ruby On Rails 而流行

  Active Record 之所以現(xiàn)在這么炙手可熱,甚至許多人將 Active Record 和 ORM 劃等號,完全是 Ruby On Rails 的原因。

在 Ruby On Rails 中,Active Record 除了最基本的將數(shù)據(jù)記錄和一個對象互相映射外,還提供了數(shù)據(jù)(而不是對象)間關(guān)聯(lián)關(guān)系的處理 。例如:

  一本圖書有一個或者多個作者,所以每一個圖書對象都和多個作者對象關(guān)聯(lián)。反過來一個作者可以寫多本書,所以一個作者對象也和多個圖書對象關(guān)聯(lián)。

  在 RoR 中,我們獲取一個圖書對象時,自動就獲得了該圖書對象所對應(yīng)的作者對象(本質(zhì)上是圖書數(shù)據(jù)對應(yīng)的作者數(shù)據(jù))。更進(jìn)一步,通過圖書對象關(guān)聯(lián)的作者對象,我們 可以獲取該作者所寫的所有圖書的對象實(shí)例。而這些工作,在 RoR 中只需要幾行代碼而已,以前我們需要寫上一大段代碼才能實(shí)現(xiàn)同樣的效果。

  RoR 中,對 Active Record 模式的實(shí)現(xiàn)完全利用了 Ruby 語言的靈活性,簡短幾行代碼就可以定義一個關(guān)聯(lián)。并且通過復(fù)雜的 ActiveRecord:Base 對象,提供了 CRUD(創(chuàng)建、讀取、更新、刪除)操作的默認(rèn)處理。所以使用 RoR 時,絕大部分常見的數(shù)據(jù)庫操作只需要很少量的代碼就可以完成,大大提高了開發(fā)效率。

  但 Active Record 模式也不是完美的,Active Record 存在不少缺點(diǎn)。

  • Active Record 模式需要數(shù)據(jù)表結(jié)構(gòu)和對象屬性一一對應(yīng)(至少是大部分對應(yīng)),否則將難以使用 Active Record 模式;
  • Active Record 模式并不能夠真正適合完全面向?qū)ο蟮膽?yīng)用程序。因?yàn)?Active Record 模式本質(zhì)上就要求一個對象必須和一個數(shù)據(jù)表對應(yīng)。但在完全面向?qū)ο蟮膽?yīng)用程序中,數(shù)據(jù)和操作數(shù)據(jù)的方法很可能分布在各個不同的對象中,這些對象卻并沒有和 某一個數(shù)據(jù)表完全對應(yīng),而且 Active Record 無法很好的處理對象的繼承、聚合等面向?qū)ο蟪R姷膶ο箝g關(guān)系;
  • 隨著逐漸向 Active Record 添加業(yè)務(wù)邏輯,Active Record 對象中會混入越來越多的 SQL 語句,這在更復(fù)雜的項(xiàng)目中顯然是一個不利因素。

  如果在 Active Record 模式中添加了對數(shù)據(jù)關(guān)系(注意,不是對象關(guān)系)的處理,那么還要注意性能問題:
  假如一個 Active Record 對象有多個關(guān)聯(lián)。那么我取出一個對象時,很可能就連帶取出了其他不少對象。但這些對象可能根本就是本次操作用不上的。其次,將對象更新到數(shù)據(jù)庫時,也需要對關(guān)聯(lián)的對象進(jìn)行處理,否則對關(guān)聯(lián)對象的修改就會丟失。

  雖然可以用各種技巧來避免這些情況,但毫無疑問需要開發(fā)者對 RoR 的 Active Record 很熟悉才行。否則看上去很簡單的代碼,背后則會是噩夢般的數(shù)據(jù)庫操作。

  其次,假設(shè)我們要將數(shù)據(jù)庫中每本書的單價減半,那么采用 Active Record 模式時,就必須首先讀取所有的記錄并實(shí)例化為對象,然后更新對象屬性,再寫回?cái)?shù)據(jù)庫。可想而知這樣會有多差的效率。
  當(dāng)然了,實(shí)際開發(fā)中沒有人會這樣做。開發(fā)者會編寫一個單獨(dú)的方法,用一條 SQL 語句完成對批量數(shù)據(jù)的更新。但也說明 Active Record 模式不適合批量處理數(shù)據(jù),而現(xiàn)實(shí)世界中,批量處理數(shù)據(jù)的需求隨處可見。

  不過由于 RoR 對開發(fā)效率戲劇性的提高,所以對于追求開發(fā)效率的項(xiàng)目,RoR 是一個很不錯的選擇。而且性能上的不足可以通過更新硬件或者配合其他技術(shù)手段來改善(例如 FastCGI 通常是運(yùn)行 RoR 應(yīng)用的首選)。因此在現(xiàn)實(shí)世界中, 37signals.com 公司的所有基于 RoR 開發(fā)的應(yīng)用,都獲得了良好的性能表現(xiàn)(但是同等的硬件,跑 PHP 開發(fā)的同樣功能應(yīng)用是更好還是更差呢?這個問題沒有答案)。

Active Record 與 ORM

  許多人將 Active Record 與 ORM 劃等號,這是錯誤的。ORM(對象關(guān)系映射)是將對象及對象間的關(guān)系(繼承、聚合等)映射到關(guān)系式數(shù)據(jù)庫中。由于面向?qū)ο蠛完P(guān)系式數(shù)據(jù)庫天生的不匹配,所以這種映射是相當(dāng)復(fù)雜的。

  而 Active Record 原本只是將一個數(shù)據(jù)行記錄包裝為一個對象,只是在 RoR 中由于添加了對關(guān)系的處理,而具有了一些 ORM 的特征。所以可以簡單的將 RoR 中的 Active Record 看作 ORM 的一種實(shí)現(xiàn)方式。但本質(zhì)上,RoR 中的 Active Record 是處理數(shù)據(jù)間的關(guān)系而不是對象間的關(guān)系(但支持對象繼承),因?yàn)槊恳粋€ Active Record 對象都是和數(shù)據(jù)表一一對應(yīng)的。

  那為什么在 Java 世界中,沒有大量采用 Active Record 模式呢?

  在 Java 世界中,絕大部分 ORM 都是作為中間件存在的。由于 Java 與 Ruby、PHP 等腳本語言截然不同的運(yùn)行機(jī)制。所以即便是很復(fù)雜的中間層,只要能夠在運(yùn)行時提供良好的性能,那就能夠被開發(fā)者接受。而 Hibernate 這樣的 ORM 中間件能夠提供比 Active Record 多得多的功能和靈活性,所以 Active Record 模式在 Java 世界不受歡迎就可以理解了。

  而在 .NET 世界中,大量使用的都是表數(shù)據(jù)入口(Table Data Gateway)和表模塊(Table Module)。這兩種模式由于有 Microsoft 出色的 IDE 支持,所以能夠獲得很高的開發(fā)效率,自然 .NET 開發(fā)者對 Active Record 模式也不感興趣了。

如果將 Active Record 或者 ORM 照搬到 PHP 中呢?

  許多開發(fā)者都很羨慕 Hibernate 的強(qiáng)大功能和 RoR 中 Active Record 的快速開發(fā)能力,但是這些東西如果照搬到 PHP 中,會遇到一個相當(dāng)大的麻煩:

  PHP 本質(zhì)上是解釋執(zhí)行的腳本語言,所以對于每一次 HTTP 請求,PHP 執(zhí)行環(huán)境都會將請求的 .php 文件編譯為 opcode,然后執(zhí)行 opcode,再清理所有的資源(內(nèi)存、數(shù)據(jù)庫連接、文件句柄等等)。在這種環(huán)境中,應(yīng)用程序應(yīng)該花盡可能少的時間去初始化底層框架,而是把大部分資源用 在業(yè)務(wù)邏輯的執(zhí)行上。

  但 Ruby 也是解釋執(zhí)行,為什么就可以用 Active Record,而 PHP 就不應(yīng)該呢?
簡單點(diǎn)說就是因?yàn)?PHP 在面向?qū)ο笾С稚系娜毕菔沟靡獙?shí)現(xiàn)和 RoR 同等功能 的 Active Record 模式變得非常艱難。也許你對此不以為然,那么可以實(shí)際嘗試一下使用 PHP on Trax(一個 RoR 的 PHP 克隆)。看看一次簡單的讀取操作需要載入多少文件并調(diào)用多少對象和方法。

  所以有些 PHP 框架提供的 Active Record 模式實(shí)現(xiàn)非常簡單,根本不考慮關(guān)聯(lián)問題,但這樣一來使用 Active Record 能獲得的開發(fā)效率提升就太小了。

  至于更為復(fù)雜的 ORM,目前 PHP 領(lǐng)域還沒有一個真正的成功項(xiàng)目。雖然 Propel 是目前 PHP 領(lǐng)域唯一一個具有實(shí)際工作能力的 ORM。但由于其自身的復(fù)雜性和執(zhí)行效率問題,一直沒有得到廣泛使用。即便是 Symfony 也是對 Propel 進(jìn)行裁剪后才用于處理數(shù)據(jù)庫操作。

雖然在國內(nèi) PHP 社區(qū)中常看到有人說自己做的 ORM 如何如何先進(jìn),既有高級特征,又有好的效率。但自始至終沒有看到過有人公布代碼。至于不公布的原因不外乎:還不夠成熟,成熟后再公布;我是最領(lǐng)先的,除非 有了同水平的,不然我不會公布;商業(yè)產(chǎn)品,不能泄露。而且別說是代碼,就算問問實(shí)現(xiàn)原理通常也只能得到幾句無關(guān)痛癢的回答。

所以如果你看到這篇文章后,覺得你實(shí)現(xiàn)了我認(rèn)為很難實(shí)現(xiàn)的東西,請拿出實(shí)際證據(jù)。不要再搬出諸如此類的理由,沒有論據(jù)的辯論是毫無意義的。

  那么 PHP 就注定和 Active Record 和 ORM 無源嗎?
如果這個問題的潛在意思是問:PHP 就不能找到和 Active Record 一樣好用的數(shù)據(jù)庫訪問方法嗎?那么答案是否定的。

Table Data Gateway 是一個更合理的選擇

  我仔細(xì)研究了 PoEAA 中關(guān)于表數(shù)據(jù)入口、表模塊的內(nèi)容后,又做了大量實(shí)際測試。最終決定在 FleaPHP 中采用 Table Data Gateway(表數(shù)據(jù)入口)模式來提供數(shù)據(jù)庫服務(wù)。并在此基礎(chǔ)上實(shí)現(xiàn)對關(guān)聯(lián)數(shù)據(jù)的自動處理。

表數(shù)據(jù)入口(Table Data Gateway):充當(dāng)數(shù)據(jù)表訪問入口的對象,一個實(shí)例處理表中所有的行。

表模塊(Table Module):處理某一數(shù)據(jù)庫表或視圖中所有行的業(yè)務(wù)邏輯的一個實(shí)例。

  表數(shù)據(jù)入口是封裝一個數(shù)據(jù)表的操作,而不是一個記錄行。這樣一來,表數(shù)據(jù)入口可以很方便的處理針對單個記錄和多個記錄的操作,而操作的數(shù)據(jù)就是 PHP 中的數(shù)組。實(shí)際上我初期還寫了一些對象來封裝記錄集(也就是多行記錄),不過后來發(fā)現(xiàn)完全是多此一舉。PHP 的數(shù)組功能非常強(qiáng)大,再專門用對象包裝一下弊大于利。

  針對數(shù)據(jù)表提供單純的 CRUD 操作吸引力還不夠,所以我在表數(shù)據(jù)入口的基礎(chǔ)上增加了對 HasOne、HasMany、ManyToMany 以及 BelongsTo 關(guān)聯(lián)的處理。這四種關(guān)聯(lián),基本上滿足了常見的數(shù)據(jù)關(guān)聯(lián)操作。

  不過有了自動化的關(guān)聯(lián),類似 RoR ActiveRecord 中加載過量數(shù)據(jù)的問題依然存在,所以 FleaPHP 的表數(shù)據(jù)入口對象 FLEA_Db_TableDataGateway 也提供了針對關(guān)聯(lián)的方法,讓開發(fā)者可以細(xì)粒度的控制數(shù)據(jù)庫操作。

  而且由于表數(shù)據(jù)入口是針對純數(shù)據(jù)進(jìn)行操作,而不是針對包裝了數(shù)據(jù)的對象。所以開發(fā)者可以很容易的優(yōu)化數(shù)據(jù)庫操作,例如無需讀取即可更新數(shù)據(jù)或者一次性處理大批量的數(shù)據(jù)。

  相對于 Active Record 模式,Table Data Gateway 模式有下列優(yōu)勢:

  • 表數(shù)據(jù)入口針對一個表封裝數(shù)據(jù)庫操作,這更接近傳統(tǒng) PHP 開發(fā)的思維模式;
  • 處理批量數(shù)據(jù)時,表數(shù)據(jù)入口更方便,常見操作無需額外編寫處理方法;
  • 數(shù)據(jù)以數(shù)組的形式保存和傳遞,比將每個記錄行實(shí)例化為對象具有好得多的性能;
  • 實(shí)現(xiàn)比 Active Record 簡單,每個操作執(zhí)行更少的代碼;
  • 可以很好的與表模塊(Table Module)模式配合來封裝業(yè)務(wù)邏輯。從而避免了 Active Record 中將數(shù)據(jù)庫操作和業(yè)務(wù)邏輯寫在一起的問題。

  當(dāng)然,表數(shù)據(jù)入口也有相對于 Active Record 不足的地方:

  • 由于表數(shù)據(jù)入口總是傳遞純數(shù)據(jù),所以無法像 Active Record 一樣以屬性的形式封裝對數(shù)據(jù)的操作。不過這種操作即便使用 Active Record 也要多寫不少處理代碼,而使用表數(shù)據(jù)入口時,這部分代碼只不過是轉(zhuǎn)移到了表模塊中;
  • 看上去更沒有那么面向?qū)ο蟆?上У氖羌幢悴捎?Active Record,大多數(shù)應(yīng)用程序從設(shè)計(jì)思想上也不是面向?qū)ο蟮模徊贿^用了一個對象來傳遞數(shù)據(jù)而已。

  而且 Active Record 存在的一些問題,Table Data Gateway 依然無法避免。最主要的就是表數(shù)據(jù)入口和表模塊都是和數(shù)據(jù)表一一對應(yīng),因此不適用于持久化細(xì)粒度對象。不過熟悉 .NET 的開發(fā)者應(yīng)該很容易找到解決辦法,那就是以表模塊完成大部分業(yè)務(wù)操作,而細(xì)粒度對象僅用于部分操作。這是因?yàn)?Microsoft 的開發(fā)環(huán)境一向都對表數(shù)據(jù)入口和表模塊有著偏好和最好的支持。

  不過使用表數(shù)據(jù)入口,相對于 Active Record 最大的好處就是能夠很容易的將業(yè)務(wù)邏輯操作從表數(shù)據(jù)入口對象分離到表模塊對象中,因此對于更大更復(fù)雜的項(xiàng)目,表數(shù)據(jù)入口配合表模塊的方式具有更高的可維護(hù)性。

表數(shù)據(jù)入口和表模塊的配合

  表數(shù)據(jù)入口封裝了針對數(shù)據(jù)表的操作,而表模塊則封裝了針對數(shù)據(jù)表的業(yè)務(wù)邏輯,兩者怎么配合呢?我們就以操作圖書記錄為例,看看具體如何做。

  首先,從 FLEA_Db_TableDataGateway 派生一個類,作為圖書表的表數(shù)據(jù)入口對象,例如 TableBooks。接下來建立一個空白的類,名為 ModuleBooks。

  1. class ? TableBooks extends FLEA_Db_TableDataGateway
  2. {
  3. ? ? // 只需要指明數(shù)據(jù)表名稱和主鍵字段名即可,CRUD 操作已經(jīng)有了默認(rèn)實(shí)現(xiàn)
  4. ? ? var ? $tableName = ' books ' ;
  5. ? ? var ? $primaryKey = ' book_id ' ;
  6. }
  7. ?
  8. class ? ModuleBooks
  9. {
  10. ? ? var ? $table ;
  11. }

  現(xiàn)在我們要統(tǒng)計(jì)指定年份的出版的圖書。

Step1: 在 TableBooks 中增加一個方法 countBooksRange():

  1. class ? TableBooks extends FLEA_Db_TableDataGateway
  2. {
  3. ? ? ......
  4. ? ?
  5. ? ? /**
  6. ? ?? * 統(tǒng)計(jì)指定時間區(qū)間的圖書總數(shù)
  7. ? ?? */
  8. ? ? function ? countBooksRange ( $begin , $end )
  9. ? ? {
  10. ? ? ? ? // 對參數(shù)進(jìn)行轉(zhuǎn)義,確保不會存在 SQL 攻擊漏洞
  11. ? ? ? ? $begin = $this -> _dbo -> qstr ( $begin ) ;
  12. ? ? ? ? $end = $this -> _dbo -> qstr ( $end ) ;
  13. ? ? ? ? return ? $this -> findCount ( " publish_date >= {$begin} AND publish_date <= {$end} " ) ;
  14. ? ? }
  15. }

  countBooksRange() 方法可以統(tǒng)計(jì)指定區(qū)間的圖書總數(shù),所以我們再給 ModuleBooks 增加一個 countBooksByYear() 方法來統(tǒng)計(jì)指定年份的圖書。

  1. class ? ModuleBooks
  2. {
  3. ? ? ......
  4. ? ?
  5. ? ? function ? countBooksByYear ( $year )
  6. ? ? {
  7. ? ? ? ? $begin = date ( " {$year} /1/1 " ) ;
  8. ? ? ? ? $end = date ( " {$year} /12/31 " ) ;
  9. ? ? ? ? return ? $this -> table -> countBooksRange ( $begin , $end ) ;
  10. ? ? }
  11. }

  上面的例子雖然簡單,但是很清晰的描述了表數(shù)據(jù)入口如何封裝具體的數(shù)據(jù)庫操作,而表模塊又如何利用表數(shù)據(jù)入口的方法提供更高層的接口。如果需要 可運(yùn)行的示例程序,可以參考 FleaPHP 的 SHOP 示例。這個示例中,Model 目錄下就是表模塊,而 Table 目錄下就是表數(shù)據(jù)入口。

?

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1442087


為什么 FleaPHP 使用 Table Data Gateway 代替 Active Record 來提供數(shù)據(jù)庫訪問服務(wù)


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 午夜视频福利在线 | 日本不卡高清免费 | 亚洲天天做夜夜做天天欢人人 | 亚洲综合区小说区激情区噜噜 | 国产精品模特hd在线 | 久久不射网 | 素人视频在线观看 | 国产福利精品视频 | 国产青草亚洲香蕉精品久久 | 久操视频免费观看 | 美女视频久久 | 99热这里只有精品免费国产 | 国产成人亚洲精品2020 | 日本xxxwww在线观看免费 | 欧美第一视频 | 九九热在线观看视频 | 99v视频国产在线观看免费 | 日本爱爱视频 | 国产swag在线观看 | 久久精品99 | 天天拍夜夜添久久精品中文 | 99精品视频在线观看免费播放 | 亚洲国产欧美一区 | 久草热线| 亚洲乱码在线播放 | 久久国产精品免费观看 | 九色最新网址 | 欧美日比视频 | 欧美freesex呦交6_10 | 国产亚洲精品久久久久久久软件 | 2021成人国产精品 | 色色色爱| 国产精品100页 | 国产精品国产三级国产普通话 | 一级片免费看 | 国产乱在线 | 91视频网址入口 | 国产高清在线精品 | 天天综合在线视频 | 99热中文| 欧美爱爱视频网站 |