下一篇《ORM查詢語言(OQL)簡介--執行個體篇》
一、SQL與ORM
關聯式資料庫(RDBMS)的查詢有SQL(Structured Query Language)結構化查詢語言 (SQL),相比進階程式語言(命令式語言)而言,SQL主要描述想要做什麼,而不是命令式語言的具體如何做,因此,SQL也被稱為第四代語言(4GL),它為現代大多數的關聯式資料庫系統所支援。SQL的核心是對“關係”的操作,資料庫理論研究證明,SQL是關係上完備的,但是當代大多數進階語言都是物件導向的,進階語言程式要跟關聯式資料庫進行互動,SQL就成了必須的橋樑,由於SQL基於的“關係”和程式語言的“對象”是不同的體系,它們之間要完成很好的互動,就得有一個“映射”過程,實現這個過程的程式,就是ORM(Object/Relation Mapping)。
應用程式調用ORM的方法,ORM自動產生相應的SQL語句到資料庫進行查詢,然後ORM將接收到的關係資料對應成實體物件。如果沒有使用ORM,那麼通常應用程式會拆分出一個資料訪問層(DAL)來產生SQL語句並執行相應的查詢。所以,ORM出現後,在一定程度上,它可以取代DAL,這使得你少了一個層的工作量,對於提高工作效率是很重要的。
是應用程式使用ORM和使用傳統的DAL的一個。
(圖1:兩種資料訪問架構)
二、ORM帶來的問題
使用ORM後,再也不用去寫那些枯燥的DAL代碼了,不用拼接那些可能存在安全問題或者敲錯欄位名的SQL語句,但是我們發現,僅僅使用ORM它反而喪失了SQL的靈活性,這也是不少人拒絕使用ORM的理由。我們看看很多人的ORM是怎麼定義資料操作介面的,他們常常把這些介面方法由實體類去實現,從而製造一個個充血的實體類:
public interface IEntity<T> where T:class
{
void Add();
void Update();
void Delete();
List<T> GetAll();
T GetOne(int id);
}
使用充血實體類,在使用上還是比較方便的,但是Insert \Update\Delete 都只能操作一條資料,GetAll 方法不僅僅把全部資料拿出來了,而且還可能把不需要的欄位值也拿了出來,那個GetOne方法,也許實際上表的主鍵並不是int 類型的,那麼這些定義就會有問題。。。
所以,我們見到很多使用了ORM的項目,不管資料是否全部需要,先拿出來再說,不管主鍵是不是int 類型,先定一個方法在那裡,大不了是個空方法,不管當前實體是否需要Delete功能(比如某些系統使用者資料是不能刪除的),都基類給直接實現了。。。。。。
三、ORM查詢語言 1,分離關注點
那麼,這些問題ORM能夠解決嗎?ORM本來是完成“對象-關係映射”的,但這裡大多數的ORM都包含了“產生SQL”的功能,而要實現SQL那樣的靈活性,那麼我們必須分離出ORM這個關注點,將“產生SQL”的功能從ORM中抽取出來,這樣我們就能夠有更多的精力致力於發明一個物件導向的,用於ORM查詢的語言,這就是OQL。
ORM查詢語言,其實早就有了,從早期的Hibernate的HQL,到MS的Linq(Linq2SQL,EF其實內部都是使用Linq產生的SQL),它們都可以產生複雜的SQL語句,它們都是直接作用於ORM架構的。幾乎在與Linq同一時期,PDF.NET也發明了自己的ORM查詢語言,稱為OQL。下面提到的OQL,都是指的PDF的OQL。
2,PDF.NET的ORM架構
PDF.NET的ORM架構套件括4個部分:
- EntityObject :PDF.NET實體類;
- OQL:ORM查詢語言,以實體類對象為操作對象,產生查詢運算式,供實體查詢對象使用。
- AdoHelper:資料訪問提供者抽象類別,封裝了對ADO.NET的各種訪問,包括事物操作;架構預設提供了OledbProvider、OdbcProvider、AccessProvider、SqlServerProvider、OracleProvider等,要支援更多的資料庫,只需要繼承AdoHelper即可。
- EntityQuery<T> :實體查詢對象,它是一個O/R Mapping對象,它操作涉及的物件類型是一個實體類;在對象內部,它會把OQL轉換成SQL,然後調用AdoHelper完成查詢。
(圖2:PDF.NET OQL 架構)
如果僅從查詢調用端來觀察,我們發現OQL,跟SQL邏輯上是等價的,一個是“對象化”的查詢,一個是“結構化”的查詢:
對象化查詢:OQL->ORM-> Objects
等於
結構化查詢:SQL ->DB-> DataSet
如果最終效果Object==DataSet,那麼OQL==SQL。
所以,OQL的設計目標,就是要它產生的SQL語句效果基本達到手寫的SQL語句一樣。由於SQL的具體實現又有很多不同的版本,所以很多時候SqlServer用的SQL語句在Oracle 上不一定能夠使用,只有那些完全標準的SQL語句才是通用的,因此,OQL的設計,也必須是這樣標準的SQL規範,目前,實現的是SQL92標準規範。
3,OQL查詢範式
下面是OQL支援的查詢範式舉例,注意下面的定義裡面使用了“BNF”範式,為了避免大家誤會,這裡補充下BFN的內容,詳細內容請參考這個連結:http://baike.baidu.com/view/1137652.htm
巴科斯範式的內容
在雙引號中的字("word")代表著這些字元本身。而double_quote用來代表雙引號。
在雙引號外的字(有可能有底線)代表著文法部分。
角括弧( < > )內包含的為必選項。
方括弧( [ ] )內包含的為可選項。
大括弧( { } )內包含的為可重複0至無數次的項。
豎線( | )表示在其左右兩邊任選一項,相當於"OR"的意思。
::= 是“被定義為”的意思。
1,資料查詢:
OQL q=OQL.From(entityObject)
[.[InnerJoin|LeftJoin|RightJoin](entityObject2).On(entityObject.PK,entityObject2.FK)]
[.[InnerJoin|LeftJoin|RightJoin](entityObject3).On(entityObject.PK,entityObject3.FK)]
.Select([entityObjectX.Property1][,entityObjectX.Property2][{,…}])
.Where([<entityObject.Property1>[,entityObject.Property2][,…]]|[OQL2]|[OQLCompare])
.GroupBy(entityObjectX.PropertyN,”<asc>|<desc>”)
.HavingBy(entityObjectX.PropertyM)
.End;
如果需要分頁,僅需要這樣操作:
q.Limit(分頁大小[,頁碼[,總記錄數]]);
執行該方法,會產生特定資料庫平台的分頁SQL語句。
2,資料統計:
OQL q=OQL.From(entityObject)
.Select().Count(entityObject.PropertyX,<””>|<"CountAsName">)
.Where([<entityObject.Property1>[,entityObject.Property2][,…]]|[OQL2]|[OQLCompare])
.End;
3,資料更新:
OQL q=OQL.From(entityObject)
.Update([entityObject.Property1][,entityObject.Property2][{,…}])
.Where([<entityObject.Property1>[,entityObject.Property2][,…]]|[OQL2]|[OQLCompare])
.End;
4,資料刪除:
OQL q=OQL.From(entityObject)
.Delete()
.Where([<entityObject.Property1>[,entityObject.Property2][,…]]|[OQL2]|[OQLCompare])
.End;
下篇我們將使用執行個體來講解OQL的具體使用,敬請期待。
註:PDF.NET現在已經開源,有關架構的詳細資料,請看官網介紹:http://www.pwmis.com/sqlmap
開源資訊介紹:
節前送禮:PDF.NET(PWMIS資料開發架構)V4.5版開源 ---------------------分界線-----------------------------------------文章發布後,熱心網友 對我提了一些意見,由於不涉及個人隱私,這裡貼上他的原文,如果他看到有異議的話請及時跟我聯絡。在此感謝他提出的下列意見! 另外大家覺得好請點個推薦,反對的話請留個理由,謝謝。---------------------分界線-----------------------------------------
shawn(630235793) 1:10:46
大體瀏覽了一下,感覺還不錯
shawn(630235793) 1:14:32
只是博文過於技術細節化而缺少了架構解決的具體問題域、面向的使用者類型,以及整體架構思想與基於關係資料訪問架構的差異性描述,讓讀者一上來很難理解ORM架構的意圖。
回複:
這些問題的確沒有表述清楚,也是因為我的撰文水平有限,沒有想到這些問題,也不知道該怎樣來表述。
PDF.NET的OQL要解決的主要問題就是讓ORM操作能夠有SQL那樣的靈活性,現有大多數ORM架構都是基於CRUD方法層級的操作,還沒有像SQL那樣具有語言層級的操作,要不然它怎麼會被稱為4GL呢?現在,我覺得LINQ也具有了這樣的能力,而我架構中的OQL,也有這樣的能力,所以我大膽的稱呼它是一個“ORM Query Laguage”,就像SQL是提供給RDBMS的查詢引擎使用一樣,OQL是提供給ORM使用的。
所以,OQL面向的使用者是那些喜歡ORM方式來訪問資料庫,又喜歡SQL的靈活性的技術人員,或者是提供給喜歡其中一種(ORM或者SQL)而不太喜歡另外一種方式的人,讓他們有機會體會到另一種方式的優勢。
整體思想就是,用物件導向的方式來操作資料庫,用OO的方式來寫SQL!
PS:OQL與LINQ相比,它更接近於SQL風格,用慣了SQL的人,第一次接觸LINQ是很不習慣的,至少我是如此。
shawn(630235793) 2012-10-6 1:39:15
資料訪問架構設計的初始設想,首先應該是滿足調用層的使用要求,換句話說請求是事務性的,還是非事務性的。如果使用者的請求是事務性的,在訪問層應該提供事務性的處理機制。而不是應用程式層自己來對是否事務性進行處理。這些應該放在訪問層的對外互動介面處提供給使用者來選擇比較合理。
所以,架構內部的分層,我感覺還應該再多考慮一下比較好。
回複:
實體層的介面是有的,只是這個圖裡面不好放置而且不是重點,省略了。
是否使用事物,是放在訪問層的對外互動介面處提供給使用者來選擇的。
shawn(630235793) 2012-10-6 1:47:13
畢竟資料訪問架構對於使用者來講就應該屏蔽所有資料庫之間操作的差異性,所有與資料庫相關的一切操作都封裝於內部。對於使用者來講這些都是完全不必去考慮的,只需要提出具體請求是什麼就可以了。對於如何解讀使用者請求、如何根據使用者選擇的具體資料庫,而將請求翻譯成底層資料庫操作指令等等,這些都是訪問層內部機制完成的。
回複:
正如你所說,架構正是這樣去做的,OQL屏蔽了SQL不同資料庫之間的差異,它會根據具體使用的資料庫,去產生本地化的SQL。
廣州-海華²º¹²<harvey.cai@qq.com> 17:04:10
跟 linq 有什麼相似/區別?
廣州-海華²º¹²<harvey.cai@qq.com> 17:05:45
這篇博文裡主推的理念,讓人想到 linq。
pdf.net 主推的應該是:linq 般好用,但是效能卓越
回複:
LINQ是.NET專屬的特性,“Language-integrated Query (LINQ)”,它是整合在.NET語言中的,這是它的先天優勢。LINQ基於運算式樹狀架構,所以它要求必須是.NET平台而且架構版本要求在.NET3.5及以上。
PDF.NET的OQL跟LINQ一樣都是ORM架構使用的語言,但是OQL文法更接近於SQL,很容易上手,而且,OQL沒有使用.NET的進階特性,這使得它只要是物件導向的語言而且支援泛型即可實現,因此將它移植到C++、Java是完全可能的,而且在.NET平台上,它也僅需.NET2.0版本的支援。