作者:林學(xué)鵬
ORM的全稱(chēng)是Object Relational Mapping,即對象關(guān)系映射。它的實(shí)質(zhì)就是將關(guān)系數據(庫)中的業(yè)務(wù)數據用對象的形式表示出來(lái),并通過(guò)面向對象(Object-Oriented)的方式將這些對象組織起來(lái),實(shí)現系統業(yè)務(wù)邏輯的過(guò)程。在ORM過(guò)程中最重要的概念是映射(Mapping),通過(guò)這種映射可以使業(yè)務(wù)對象與數據庫分離。從面向對象來(lái)說(shuō),數據庫不應該和業(yè)務(wù)邏輯綁定到一起,ORM則起到這樣的分離作用,使數據庫層透明,開(kāi)發(fā)人員真正的面向對象。圖 1簡(jiǎn)單說(shuō)明了ORM在多層系統架構中的這個(gè)作用。

圖1:ORM在多層系統架構中的作用
目前大多數項目或產(chǎn)品都使用關(guān)系型數據庫實(shí)現業(yè)務(wù)數據的存儲,這樣在開(kāi)發(fā)過(guò)程中,常常有一些業(yè)務(wù)邏輯需要直接用寫(xiě)SQL語(yǔ)句實(shí)現,但這樣開(kāi)發(fā)的結果是:遍地布滿(mǎn)SQL語(yǔ)句。這些高藕合的SQL語(yǔ)句給系統的改造和升級帶來(lái)很多無(wú)法預計的障礙。為了提高項目的靈活性,特別是快速開(kāi)發(fā),ORM是一個(gè)不錯的選擇。舉個(gè)簡(jiǎn)單的例子:在使用ORM的系統中,當數據庫模型改變時(shí),不再需要理會(huì )邏輯代碼和SQL語(yǔ)句中涉及到該模型的所有改動(dòng),只需要將該模型映射的對象稍作改動(dòng),甚至不做改動(dòng)就可以滿(mǎn)足要求。

![]() | 一、ORM的工具實(shí)現:Grove |
![]() | 二、Grove在開(kāi)發(fā)中的實(shí)際應用 |
![]() | 三、總結 |
優(yōu)秀的ORM工具不僅可以幫助我們很好的理解對象及對象的關(guān)系,而且工具本身會(huì )幫助我們維護這些關(guān)系?;谶@個(gè)理念,我設計了基于.NET的ORM工具——Grove ORM Development Toolkit。
Grove ORM Development Toolkit包含Grove和Toolkit兩部分內容。Grove為ORM提供對象持久、關(guān)系對象查詢(xún)、簡(jiǎn)單事務(wù)處理、簡(jiǎn)單異常管理等功能。數據持久包括一些對象的Insert、Delete、Update、Retrieve等功能,關(guān)系對象查詢(xún)則提供一些基于對象的復雜關(guān)系查詢(xún),包括對應到數據庫功能的子查詢(xún)、關(guān)聯(lián)查詢(xún)(JOIN)、函數支持(count、avg、max、min)、聚合等。Toolkit是基于VS.NET 2002/2003的VSIP開(kāi)發(fā)的外接程序,職責是幫助開(kāi)發(fā)人員快速映射關(guān)系數據庫中的業(yè)務(wù)模型到符合Grove要求的映射實(shí)體類(lèi),以及映射數據庫中復雜關(guān)系查詢(xún)到Grove要求的關(guān)系映射實(shí)體,暫時(shí)只提供C#支持。圖 2是Grove內部類(lèi)實(shí)現關(guān)系圖。

圖 2: Grove內部類(lèi)實(shí)現關(guān)系圖
在ORM實(shí)現的前期工作中,為了實(shí)現屏蔽各種數據庫之間的操作差異,我們需要定義數據操作公有接口,封裝基本的數據庫Insert,Update,Delete,Query等操作。
public interface IDbOperator{int ExecNonQuery(string cmdText);int ExecDataSet(string cmdText,DataSet entity);object ExecScalar(string cmdText);…}再定義一個(gè)數據庫操作工廠(chǎng)類(lèi),實(shí)現各種不同類(lèi)型數據的適配。
public abstract class DbOperatorFactory:IDbOperator
然后實(shí)現各種數據庫的操作類(lèi),以SQLServer為例。
internal class SqlDbOperator:DbOperatorFactory
完成后,就是ORM主角——實(shí)體(Entity)的實(shí)現。ORM中實(shí)體的定義可以通過(guò)實(shí)體類(lèi)自身包含數據模型元數據的方式實(shí)現,也可以通過(guò)定義XML的元描述實(shí)現。下面講述了通過(guò)實(shí)體類(lèi)自身映射的實(shí)現。
實(shí)體(Entity)是實(shí)際業(yè)務(wù)數據的載體,包含業(yè)務(wù)數據模型的元描述,可以直接由數據庫中的某張表或視圖生成,也可以根據需要手工創(chuàng )建。.NET中提供了System.Attribute,該類(lèi)包含訪(fǎng)問(wèn)和測試自定義屬性的簡(jiǎn)便方法。.NET Framework預定義了一些屬性類(lèi)型并使用它們控制運行時(shí)行為。我們可以通過(guò)繼承System.Attribute基類(lèi)自定義用于描述實(shí)體對象映射時(shí)所需要的數據模型元數據,包括表名,字段名,字段長(cháng)度,字段類(lèi)型等一些常用的數據。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]public class DataTableAttribute : Attribute
AttributeUsage用來(lái)表示該自定義屬性可以被綁定在什么樣的對象上,這里表示應用在Class或者Struct之上。
抽象一些具有相同特征的屬性,使之成為自定義屬性的基類(lèi)。
[AttributeUsage(AttributeTargets.Property)]public class BaseFieldAttribute:Attribute
定義一般字段所需要的自定義屬性類(lèi)。
[AttributeUsage(AttributeTargets.Property)]public class DataFieldAttribute : BaseFieldAttribute
定義關(guān)鍵字字段所需的自定義屬性類(lèi)。
[AttributeUsage(AttributeTargets.Property)]public class KeyFieldAttribute : BaseFieldAttribute
定義外鍵字段所需要的自定義屬性類(lèi)。
[AttributeUsage(AttributeTargets.Property)]public class ForeignKeyFieldAttribute : BaseFieldAttribute
在以上自定義屬性類(lèi)完成后,我們需要一個(gè)用于訪(fǎng)問(wèn)實(shí)體在運行期綁定的自定義屬性及屬性數據的一個(gè)Help類(lèi)。
internal class TypeHelper
實(shí)體定義完成后,我們需要根據實(shí)體類(lèi)中綁定的自定義屬性構造出運行期需要的SQL語(yǔ)句,為了收集實(shí)體類(lèi)定義中的數據結構描述,我們需要定義一個(gè)類(lèi)來(lái)說(shuō)明實(shí)體在運行期所引用到的所有關(guān)于數據持久的信息,包括關(guān)鍵字字段,外鍵字段,一般字段等。
internal class TypeInfo
同時(shí)需要一個(gè)字段元數據描述類(lèi),描述字段在數據庫中的名稱(chēng),大小,是否可為空,列類(lèi)型等信息。
internal class ColumnInfo
以上條件具備后,我們需要定義一個(gè)解析類(lèi),負責轉換數據的程序類(lèi)型到數據庫字段類(lèi)型,并且構造出Insert,Update,Delete,Query等操作所需要的SQL語(yǔ)句。
internal class SqlEntityParser
將上面的操作組合起來(lái)就是實(shí)體類(lèi)對象操作員。
public class ObjectOperator
實(shí)現新增一個(gè)記錄到數據庫中,就是創(chuàng )建了一個(gè)新的實(shí)體對象,并交由對象操作員進(jìn)行持久化。
public void Insert(object o){TypeInfo info1=new TypeInfo(o.GetType());//根據實(shí)例或者實(shí)體描述信息SQLCommand sc=info1.BuildInsertCommand(o);//構造SQL命令對象DbOperator.Parameters=sc.Parameters;//賦值SQL命令所需的參數DbOperator.ExecNonQuery(sc.CommandText);//執行SQL命令}這里的SQLCommand對象封裝了SQL命令處理時(shí)所需要的一些值,包含SQL語(yǔ)句,命令參數(Parameter)等。
安裝Grove Kit要求Visual Studio 2003 及.NET Framework 1.1支持。從Grove網(wǎng)站下載安裝包之后,解壓縮GroveKit.zip,執行安裝。
在GroveKit安裝結束后,打開(kāi)VS.NET,在VS.NET的啟動(dòng)畫(huà)面上,您會(huì )看到Grove Develop Kit的標志,表示GroveKit已被正確安裝。
2.1生成映射實(shí)體類(lèi)
本文將以C# 項目為例解釋Grove在開(kāi)發(fā)中的具體應用。項目名WebApp1,操作系統 Windows 2000,數據庫SQL Server 2000,數據庫實(shí)例名:WebApp1,表結構定義如下:
| 表名 | 字段 |
| Customers | CustomerID int(4) PK CustomerName varchar(50) CustomerDesc varchar(200) Status tinyint |
| Addresses | AddressID int(4) PK CustomerID int(4) FK Address varchar(200) |
| 1. | 在VS.NET中,打開(kāi)“文件->新建->項目”,在Visual C#項目選擇ASP.NET WEB應用程序,確定后生成WebApp1項目,在項目中添加對Grove.dll的引用,Grove.dll位于GroveKit的安裝路徑下,您也可以通過(guò).NET Configuration將Grove添加到程序集緩存中。 |
| 2. | 在VS.NET中,打開(kāi)“工具->Grove Tool Kit”,在GroveToolKit中設置數據庫連接屬性,并保存。 ![]() 圖3 設置數據庫連接串 |
| 3. | 配置當前Web項目的web.config(在</configuration>之前加入以下配置) <appSettings> <add key="DBConnString" value="Server=localhost;Uid=sa;Pwd=sa;Database=WebApp1" /> </appSettings> |
| 4. | 4)在VS.NET解決方案資源管理器中選中Entities,并在GroveToolKit中選擇表名,點(diǎn)擊GroveToolKit的toolbar中的Preview Entity Class按鈕,出現該表的實(shí)體映射類(lèi)預覽窗口。 ![]() 圖4 預覽實(shí)體映射類(lèi) |
| 5. | 檢查當前預覽的實(shí)體類(lèi),點(diǎn)擊生成文件按鈕,該實(shí)體類(lèi)將被生成到解決方案資源管理器當前選中的路徑下。 |
| 6. | 重復4,5步驟就可以生成其他表的映射實(shí)體類(lèi)。 Customer.cs using System; using Grove.ORM; [DataTable("Customers")] public class Customer { int _CustomerID; string _CustomerName; string _CustomerDesc; int _Status; [KeyField("CustomerID")] public int CustomerID { get{return this._CustomerID;} set{this._CustomerID=value;} } [DataField("CustomerName")] public string CustomerName { get{return this._CustomerName;} set{this._CustomerName=value;} } [DataField("CustomerDesc")] public string CustomerDesc { get{return this._CustomerDesc;} set{this._CustomerDesc=value;} } [DataField("Status")] public int Status { get{return this._ Status;} set{this._ Status=value;} } } Address.cs using System; using Grove.ORM; [DataTable("Addresses")] public class Address { int _AddressID; int _CustomerID; string _Address; [KeyField("AddressID")] public int AddressID { get{return this._AddressID;} set{this._AddressID=value;} } [ForeignKeyField("CustomerID")] public int CustomerID { get{return this._CustomerID;} set{this._CustomerID=value;} } [DataField("Address")] public string CustomerAddress { get{return this._Address;} set{this._Address=value;} } } 代碼1.實(shí)體映射類(lèi) |
2.2對象持久化
Grove提供ObjectOperator實(shí)現對映射實(shí)體對象的數據庫持久工作,并通過(guò)IObjectQuery接口實(shí)現對復雜數據庫關(guān)系映射實(shí)體的查詢(xún),主要接口如下:
| 方法 | 說(shuō)明 |
| Insert | 新增一個(gè)對象 |
| Update | 根據條件更新一個(gè)對象 |
| Remove | 根據條件刪除一個(gè)對象 |
| RemoveChilds | 刪除所有關(guān)系對象 |
| Retrieve | 返回一個(gè)對象 |
| RetrieveChilds | 返回所有關(guān)系對象 |
| GetDataReader | 返回IDataReader |
| GetObjectSet | 返回對象集合 |
| GetObjectSource | 根據對象定義返回DataSet |
| GetCount | 從數據源返回記錄條數 |
| BeginTranscation | 在數據庫支持事務(wù)的基礎上,開(kāi)始事務(wù)處理 |
| Commit | 完成當前事務(wù) |
| Rollback | 回退當前事務(wù) |
2.3數據查詢(xún)
如一般的關(guān)系型數據庫所具有的查詢(xún)功能一樣,Grove也有著(zhù)非常豐富的查詢(xún)功能,如對象查詢(xún)、函數查詢(xún)、子查詢(xún)、排序查詢(xún)等。這里對對象查詢(xún)做簡(jiǎn)單介紹,其它查詢(xún)讀者可以自行參考Grove的開(kāi)發(fā)文檔。Grove提供ObjectQuery 幫助ObjectOperator從數據源查詢(xún)數據, ObjectOperator 需要通過(guò)ObjectQuery解析實(shí)體對象中的屬性(System.Arrtibute)定義,并構造查詢(xún)語(yǔ)句。ObjectQuery在運行時(shí)往往需要定義篩選語(yǔ)句(請參考篩選語(yǔ)句的語(yǔ)法定義)。例如,檢索Customer對象,當State 屬性等于WA的情況:
ObjectQuery query=new ObjectQuery(typeof(Customer),"this.State=‘WA‘");
當檢索需要返回所有對象時(shí),則不需要定義篩選語(yǔ)句
ObjectQuery query=new ObjectQuery(typeof(Customer),"");
2.4篩選語(yǔ)句的語(yǔ)法定義
在ObjectQuery中使用的篩選允許你在定義的時(shí)候,根據使用面向對象語(yǔ)法規則進(jìn)行定義篩選語(yǔ)句。
| 操作 | 描述 |
| !, not | 用于比較布爾型,例如: !Order.CustomerID.Contains(Customer.CustomerID) |
| <, >, <= , >= | 用于值比較,例如: Order.Quantity >= 12 |
| =, !=, <>, = = | 用于值判斷,例如: Customer.Country = ‘USA‘ and Customer.Region != ‘WA‘ |
| and, && | 用于邏輯判斷,例如: Customer.Country = ‘USA‘ and Customer.Region = ‘WA‘ |
| or, || | 用于邏輯判斷,例如: Customer.LastName = ‘Smith‘ or Customer.LastName = ‘Jones‘ |
以上就是ORM的簡(jiǎn)單實(shí)現,復雜的關(guān)系對象映射及關(guān)系映射實(shí)體的查詢(xún)也是ORM中尤為重要的一塊處理,為了屏蔽各數據庫之間的SQL差異,很多好的ORM框架都提供一種符合面向對象語(yǔ)言本身語(yǔ)法規則的Query Language支持,例如實(shí)現對數據庫函數的支持時(shí),會(huì )通過(guò)定義一些公開(kāi)的,與編程語(yǔ)言接近的語(yǔ)言來(lái)實(shí)現,比如說(shuō)定義Object.Size(),Object.Sum()等類(lèi)方法式操作語(yǔ)法,在邏輯判斷的時(shí)候提供一些語(yǔ)言本身的邏輯運算符支持,比如c#中的&&表示and,||表示or等等這些一系列的面向對象編程風(fēng)格的支持,都很好地為基于關(guān)系型數據庫支持的系統開(kāi)發(fā)向“面向對象”提供了有力的支持。Grove目前對關(guān)系對象查詢(xún)有很好的支持,感興趣可以到Grove的網(wǎng)站了解詳細信息。
聯(lián)系客服