ORM查詢語言(OQL)簡介--概念篇
相關文章內容索引:
* 可以參考作者的《SOD框架“企業級”應用數據架構實戰》了解本文相關的內容。
一、SQL與ORM
關系數據庫(RDBMS)的查詢有SQL(Structured Query Language)結構化查詢語言,相比高級程序語言(命令式語言)而言,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是怎么定義數據操作接口的,他們常常把這些接口方法由實體類去實現,從而制造一個個充血的實體類:
{
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查詢的語言,(ORM Query Language) ,這就是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個部分:
- Entity Object :PDF.NET實體類,它繼承于基類 EntityBase,使得每一個實體類都成為一個“數據容器”;
- OQL:ORM查詢語言,以實體類對象為操作對象,生成查詢表達式,供實體查詢對象使用。
- AdoHelper:數據訪問提供程序抽象類,封裝了對ADO.NET的各種訪問,包括事物操作;框架默認提供了OledbProvider、OdbcProvider、AccessProvider、SqlServerProvider、OracleProvider等,要支持更多的數據庫,只需要繼承AdoHelper即可。
- EntityQuery<T> :實體查詢對象,它是一個O/R Mapping對象,它操作涉及的對象類型是一個實體類(類型T);在對象內部,它會把OQL轉換成SQL,然后調用AdoHelper完成查詢。
(圖2:PDF.NET OQL 架構)
如果僅從查詢調用端來觀察,我們發現OQL,跟SQL邏輯上是等價的,一個是“對象化”的查詢,一個是“結構化”的查詢:
對象化查詢:OQL->ORM-> Entity Objects
等于
結構化查詢:SQL ->DB-> DataSet
如果最終效果Entity 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,數據查詢:
[.[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;
如果需要分頁,僅需要這樣操作:
執行該方法,會生成特定數據庫平臺的分頁SQL語句。
2,數據統計:
.Select().Count(entityObject.PropertyX,<””>|<"CountAsName">)
.Where([<entityObject.Property1>[,entityObject.Property2][,…]]|[OQL2]|[OQLCompare])
.End;
3,數據更新:
.Update([entityObject.Property1][,entityObject.Property2][{,…}])
.Where([<entityObject.Property1>[,entityObject.Property2][,…]]|[OQL2]|[OQLCompare])
.End;
4,數據刪除:
.Delete()
.Where([<entityObject.Property1>[,entityObject.Property2][,…]]|[OQL2]|[OQLCompare])
.End;
下篇我們將使用實例來講解OQL的具體使用,敬請期待。
注:PDF.NET現在已經開源,有關框架的詳細信息,請看官網介紹:http://www.pwmis.com/sqlmap
開源信息介紹:
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
數據訪問框架設計的初始設想,首先應該是滿足調用層的使用要求,換句話說請求是事務性的,還是非事務性的。如果用戶的請求是事務性的,在訪問層應該提供事務性的處理機制。而不是應用層自己來對是否事務性進行處理。這些應該放在訪問層的對外交互接口處提供給用戶來選擇比較合理。
所以,框架內部的分層,我感覺還應該再多考慮一下比較好。
回復:
實體層的接口是有的,只是這個圖里面不好放置而且不是重點,省略了。
是否使用事物,是放在訪問層的對外交互接口處提供給用戶來選擇的。
畢竟數據訪問框架對于用戶來講就應該屏蔽所有數據庫之間操作的差異性,所有與數據庫相關的一切操作都封裝于內部。對于用戶來講這些都是完全不必去考慮的,只需要提出具體請求是什么就可以了。對于如何解讀用戶請求、如何根據用戶選擇的具體數據庫,而將請求翻譯成底層數據庫操作指令等等,這些都是訪問層內部機制完成的。
回復:
正如你所說,框架正是這樣去做的,OQL屏蔽了SQL不同數據庫之間的差異,它會根據具體使用的數據庫,去生成本地化的SQL。
跟 linq 有什么相似/區別?
廣州-海華2o12<harvey.cai@qq.com> 17:05:45
這篇博文里主推的理念,讓人想到 linq。
pdf.net 主推的應該是:linq 般好用,但是性能卓越
回復:
LINQ是.NET獨有的特性,“語言集成查詢”,它是集成在.NET語言中的,這是它的先天優勢。LINQ基于表達式樹,所以它要求必須是.NET平臺而且框架版本要求在.NET3.5及以上。
PDF.NET的OQL跟LINQ一樣都是ORM框架使用的語言,但是OQL語法更接近于SQL,很容易上手,而且,OQL沒有使用.NET的高級特性,這使得它只要是面向對象的語言而且支持泛型即可實現,因此將它移植到C++、Java是完全可能的,而且在.NET平臺上,它也僅需.NET2.0版本的支持。
浙公網安備 33010602011771號