(廣州大學(xué) 計(jì)算機(jī)科學(xué)與網(wǎng)絡(luò)工程學(xué)院, 廣東 廣州 510006)
數(shù)據(jù)庫(kù)應(yīng)用廣泛,每天有無(wú)數(shù)的開發(fā)人員在書寫與數(shù)據(jù)庫(kù)交互的代碼,這些交互代碼可以由程序員純手動(dòng)編寫,也可以使用現(xiàn)有的專業(yè)ORM框架工具生成.目前,可用的專業(yè)ORM框架工具很多,如Hibernate[1]、ADO.NET Entity Framework[2]等.Hibernate是一個(gè)開放源代碼、全自動(dòng)的ORM框架,可以自動(dòng)生成SQL語(yǔ)句、自動(dòng)執(zhí)行,使得Java程序員可以使用對(duì)象編程思維來(lái)操縱數(shù)據(jù)庫(kù).ADO.NET Entity Framework是微軟以 ADO.NET為基礎(chǔ)所發(fā)展出來(lái)的ORM解決方案[3].Entity Framework 利用了抽象化數(shù)據(jù)結(jié)構(gòu)的方式,將每個(gè)數(shù)據(jù)庫(kù)對(duì)象都轉(zhuǎn)換成應(yīng)用程序?qū)ο螅寯?shù)據(jù)庫(kù)的E-R模型完全轉(zhuǎn)成對(duì)象模型.此外,還有其他的一些專業(yè)的ORM框架,如Mybatis[4]、NHibernate[5]、Speed[6]、SqlBuilder.NET[7]等,文獻(xiàn)[8]提出了云計(jì)算環(huán)境下的一種基于Hbase的ORM設(shè)計(jì)方案.這些專業(yè)的ORM框架工具生成的代碼的優(yōu)點(diǎn)是功能豐富,使用比較方便、靈活,但也存在一些問題:
(1)有些框架代碼的執(zhí)行需要?jiǎng)討B(tài)生成SQL,并通過(guò)相應(yīng)的配置信息完成對(duì)象與關(guān)系之間的映射,因此,執(zhí)行效率不高[9];
(2)當(dāng)ORM框架的版本更新時(shí),程序員使用ORM框架的代碼也需要更新,因此,代碼隨ORM框架版本的改變而改變;
(3)ORM框架只支持部分關(guān)系數(shù)據(jù)庫(kù)或部分程序設(shè)計(jì)語(yǔ)言;
(4)ORM框架比較復(fù)雜,程序員需要進(jìn)行培訓(xùn)學(xué)習(xí)才能熟練掌握ORM框架的使用.
基于以上原因,很多對(duì)執(zhí)行效率有要求的項(xiàng)目以及不提供ORM框架的關(guān)系數(shù)據(jù)庫(kù),都必須采用純手工編寫數(shù)據(jù)庫(kù)訪問層的源代碼,但是,純手工編寫數(shù)據(jù)庫(kù)訪問層代碼容易出錯(cuò),而且需要耗費(fèi)大量時(shí)間.為了減輕開發(fā)人員的工作量,本文提出一種數(shù)據(jù)庫(kù)訪問代碼自動(dòng)生成方法,利用該方法,可以生成“純手工編寫”的源代碼,把開發(fā)人員從枯燥、重復(fù)的數(shù)據(jù)庫(kù)訪問代碼編寫工作中解放出來(lái),提高工作效率.
當(dāng)前,大多數(shù)手工編寫的代碼使用數(shù)據(jù)庫(kù)開發(fā)商提供的接口(如JDBC[10]、ADO.net)訪問數(shù)據(jù)庫(kù),使用這些接口訪問數(shù)據(jù)庫(kù)需要經(jīng)過(guò)“與數(shù)據(jù)庫(kù)建立連接、發(fā)送SQL語(yǔ)句并處理結(jié)果”的步驟,這些步驟的源代碼自動(dòng)生成需要解決三個(gè)關(guān)鍵問題:①數(shù)據(jù)庫(kù)的數(shù)據(jù)類型與程序設(shè)計(jì)語(yǔ)言中的數(shù)據(jù)類型及獲取方法之間的映射關(guān)系;②從數(shù)據(jù)庫(kù)的數(shù)據(jù)字典中獲取數(shù)據(jù)庫(kù)的元數(shù)據(jù);③自動(dòng)生成代碼的算法設(shè)計(jì).接下來(lái),本文以C#語(yǔ)言訪問SQL Server數(shù)據(jù)庫(kù)的代碼為例,描述訪問代碼自動(dòng)生成的過(guò)程.
(1)SQL Server數(shù)據(jù)類型與c#數(shù)據(jù)類型及獲取方法之間的映射關(guān)系.
SQL Server 和 .NET Framework 基于不同的類型系統(tǒng).針對(duì)不同的SQL Server數(shù)據(jù)庫(kù)引擎類型,類型SqlDataReader公開了用于返回 .NET Framework 類型的訪問器方法.本文在表1中列出了部分SQL Server數(shù)據(jù)類型與c#數(shù)據(jù)類型及獲取方法之間的映射關(guān)系,詳細(xì)的映射關(guān)系參見MSDN文檔[11].
表1 SQL Server數(shù)據(jù)類型映射
(2)從數(shù)據(jù)庫(kù)的數(shù)據(jù)字典中獲取數(shù)據(jù)庫(kù)的元數(shù)據(jù).
當(dāng)使用SQL命令在數(shù)據(jù)庫(kù)中創(chuàng)建表、視圖、存儲(chǔ)過(guò)程時(shí),數(shù)據(jù)庫(kù)將表、視圖、存儲(chǔ)過(guò)程的元數(shù)據(jù)存儲(chǔ)到系統(tǒng)表中,通過(guò)查詢數(shù)據(jù)庫(kù)系統(tǒng)表,可以獲取到創(chuàng)建表、視圖、存儲(chǔ)過(guò)程的所有元數(shù)據(jù),例如,可以使用以下SQL語(yǔ)句在SQL SERVER中獲取STUDENT表的字段元數(shù)據(jù).
select col.name columnName, tp.name dataType,col.max_length dataLength,col.precision dataPrecision,col.scale dataScale, case col.is_nullable when 1 then 'Y' when 0 then 'N' end isNullable, case col.is_identity when 1 then 'Y' when 0 then 'N' end isIdentity, case col.is_computed when 1 then 'Y' when 0 then 'N' end isComputed,sc.text defaultValue from sys.objects obj inner join sys.COLUMNS col on obj.object_id= col.object_id inner join sys.types tp on col.user_type_id=tp.user_type_id left join syscomments sc on col.default_object_id=sc.id where obj.type in ('U','V') and obj.name ='STUDENT'
每個(gè)表或視圖需要獲取的相關(guān)的元數(shù)據(jù)信息包括列名、列數(shù)據(jù)類型、數(shù)據(jù)長(zhǎng)度、可否空、是否自增字段、是否自動(dòng)計(jì)算字段、默認(rèn)值等,如果是數(shù)字,還必須考慮數(shù)字的有效位數(shù)和小數(shù)位數(shù).如果是表,還必須考慮表的約束元數(shù)據(jù),以確定主碼包括哪些字段、外碼包括哪些字段.
對(duì)于存儲(chǔ)過(guò)程,需要通過(guò)系統(tǒng)表獲取存儲(chǔ)過(guò)程的名稱、參數(shù)類型、參數(shù)個(gè)數(shù)和順序等元數(shù)據(jù),以生成調(diào)用存儲(chǔ)過(guò)程的源代碼.
(3)自動(dòng)生成代碼的算法設(shè)計(jì).
本文設(shè)計(jì)了兩種代碼生成算法即常規(guī)代碼生成算法和按SQL生成代碼.常規(guī)代碼生成算法的過(guò)程(本文以表為例):①與數(shù)據(jù)庫(kù)進(jìn)行連接,獲取表的元數(shù)據(jù)信息;②根據(jù)獲取的元數(shù)據(jù)信息和表1中的類型映射信息,生成實(shí)體類.如圖1所示,表名稱映射為類名稱,表的每個(gè)字段映射為類的屬性成員.③根據(jù)獲取的元數(shù)據(jù)信息和生成實(shí)體類,生成select、insert、update、delete等SQL語(yǔ)句對(duì)應(yīng)的靜態(tài)成員函數(shù).
create table student(sno varchar(10) primary key,sname varchar(20) not null,sage int);public partial class student{public String Sno{get;set;}public String Sname{get;set;}public Nullable
圖1 Student表與實(shí)體類student
Fig.1 Student table and student entity class
作為示例,以下生成的代碼是根據(jù)學(xué)生的學(xué)號(hào)獲取對(duì)應(yīng)的學(xué)生信息,其中,類SqlHelper封裝了訪問數(shù)據(jù)庫(kù)的靜態(tài)方法.
public static Student GetObjectByPK(String SNO)
{
Student tmp=null;
string sql="SELECT SNO,SNAME,SAGE FROM STUDENT WHERE SNO=@SNO ";
SqlParameter[] para=new SqlParameter[]{new SqlParameter("@SNO",SqlDbType.VarChar,10)};
para[0].Value=SNO;
using(SqlDataReader sdr=SqlHelper.ExecuteReader(SqlHelper.strCon, CommandType.Text, sql, para))
{
if (sdr.Read())
{
tmp=new Student();
tmp.Sno=sdr.GetSqlString(0).Value;
tmp.Sname=sdr.GetSqlString(1).Value;
if (!sdr.IsDBNull(2))
tmp.Sage=sdr.GetSqlInt32(2).Value;
}
}
return tmp;
}
按SQL生成代碼的方法首先對(duì)SQL語(yǔ)句分析,解析出SQL語(yǔ)句中包含的表以及字段信息,然后按照“常規(guī)代碼生成算法”的過(guò)程,生成對(duì)應(yīng)的靜態(tài)操作方法;對(duì)于多表查詢的SQL語(yǔ)句,可以在數(shù)據(jù)庫(kù)中創(chuàng)建為視圖,然后按照“常規(guī)代碼生成算法”的過(guò)程,生成對(duì)應(yīng)的靜態(tài)操作方法.
本文提出一種自動(dòng)生成“純手工編寫”的源代碼的方法,生成的代碼規(guī)范、易于維護(hù),減輕了程序員的編碼工作,提高了開發(fā)效率.