摘要:采用代碼生成技術(shù)能大幅提高軟件開發(fā)的質(zhì)量和生產(chǎn)率,降低軟件開發(fā)的風(fēng)險(xiǎn)。本文將介紹了基于C#的NHibernate代碼生成器的設(shè)計(jì)與實(shí)現(xiàn)過程,并分析了常見的代碼生成技術(shù),同時(shí)結(jié)合實(shí)例說明核心源代碼。
關(guān)鍵詞:代碼生成器;C#;NHibernate;XML
中圖分類號(hào):TP311文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào):1009-3044(2008)31-0908-04
Design and Implementation of NHibernate Code Generator
JIANG Shang-ting, LI Wei
(Computer Centor, Anhui Preofessional Scientifical College of News Publishment, Hefei 230601, China)
Abstract: Code technology can be used to generate a substantial increase in the quality of software development and productivity, reduce the risk of software development. This article based on the C# code generation NHibernate Design and Implementation of the process and analysis of the common code generation technology, combined with examples of the core source code.
Key words: code generator; C#; NHibernate; XML
NHibernate是一個(gè)面向.NET環(huán)境的對(duì)象/關(guān)系數(shù)據(jù)庫(kù)映射工具。NHibernate很好的實(shí)現(xiàn)了關(guān)系型數(shù)據(jù)庫(kù)到面向?qū)ο髮?shí)體的映射,并且提供數(shù)據(jù)查詢和獲取數(shù)據(jù)的方法,從而大幅度減少開發(fā)時(shí)人工使用SQL和ADO.NET處理數(shù)據(jù)的時(shí)間,提高了效率和安全性。但是在使用NHibernate之前需要手寫大量配置文件和POJO類,并且一旦有錯(cuò)誤就會(huì)導(dǎo)致整個(gè)程序的錯(cuò)誤,也就提高了整個(gè)項(xiàng)目的復(fù)雜性。那么,如何才能有效的去完成這項(xiàng)工作?本文將向大家介紹NHibernate的代碼生成器的設(shè)計(jì)與實(shí)現(xiàn),同時(shí)大家也可以參考實(shí)現(xiàn)其他功能的代碼生成器。
1 代碼生成器的實(shí)現(xiàn)原理
綜合大部分的代碼生成器,我們不難看出,絕大多數(shù)都采用了XML、XSLT技術(shù)。其主要原因在于首先XML的國(guó)際標(biāo)準(zhǔn)化,其次XML可以表示層次結(jié)構(gòu)有利于設(shè)計(jì)和實(shí)現(xiàn)。而XSLT是一種將XML文檔轉(zhuǎn)換為其他文本文檔的語言,它是建立在XML和XPath之上的國(guó)際標(biāo)準(zhǔn),功能強(qiáng)大。兩者結(jié)合可以很容易實(shí)現(xiàn)代碼生成功能。
考慮到NHibernate是ORM工具,而XML可以很容易的實(shí)現(xiàn)數(shù)據(jù)表的層次結(jié)構(gòu),同時(shí)C#語言中包含了實(shí)現(xiàn)對(duì)XML和XSLT的操作的類庫(kù)。所以本文也將采用XML、XSLT技術(shù)來實(shí)現(xiàn)NHibernate代碼生成器。基本的原理如圖1所示。
2 代碼生成器的設(shè)計(jì)原理
要實(shí)現(xiàn)將關(guān)系數(shù)據(jù)庫(kù)模型通過代碼生成器映射為所需要的代碼,首先要將關(guān)系數(shù)據(jù)庫(kù)模型轉(zhuǎn)換為代碼生成器可以讀取的文件, 然后代碼生成器從這些輸入文件中提取出模型中的信息并生成相應(yīng)的代碼。那么在這里我們使用的是XML文檔,這樣就必須將我們的關(guān)系型數(shù)據(jù)庫(kù)模型轉(zhuǎn)換為XML文檔。為了實(shí)現(xiàn)較好的通用性,本代碼生成器可以實(shí)現(xiàn)ACCESS、MSSQL、ORACLE這三個(gè)數(shù)據(jù)庫(kù)轉(zhuǎn)換功能。接著我們將使用XSLT作為模板,完成從XML到代碼的轉(zhuǎn)換過程。從而實(shí)現(xiàn)代碼生成器的基本功能。
2.1 數(shù)據(jù)庫(kù)模型到XML文檔的轉(zhuǎn)換
這里關(guān)鍵是獲取數(shù)據(jù)庫(kù)的基本信息,包括表、字段、索引、鍵等,這次都可以通過數(shù)據(jù)庫(kù)中的系統(tǒng)表獲得,下面給出三個(gè)數(shù)據(jù)中數(shù)據(jù)庫(kù)的基本信息獲取方法。
對(duì)于MSSQLServer,數(shù)據(jù)庫(kù)中有SysObjects和SysColumns這兩個(gè)系統(tǒng)表,我們可以查詢系統(tǒng)表來獲得所有的表名稱和字段名稱以及格式,還有一個(gè)sp_helpindex 的系統(tǒng)預(yù)定義存儲(chǔ)過程來獲得指定表的字段索引信息。
對(duì)于Oracle,數(shù)據(jù)庫(kù)有一個(gè)名為Col的系統(tǒng)預(yù)定義視圖,我們可以查詢這個(gè)視圖獲得所有的表名和字段定義信息。還有一個(gè) user_ind_columns的系統(tǒng)預(yù)定義視圖,我們可以關(guān)鍵字段信息。
對(duì)于Access2000數(shù)據(jù)庫(kù),沒有這些系統(tǒng)表,因此我們使用。NET框架中的OleDB的數(shù)據(jù)連接對(duì)象的GetOleDbSchemaTable函數(shù)來獲得數(shù)據(jù)庫(kù)表和字段定義信息。
現(xiàn)在我們通過上面的方法可以獲取數(shù)據(jù)庫(kù)的信息了,后面我們要做的就是將它生成指定的XML文檔了。GetTableXML(string tableName)方法實(shí)現(xiàn)了讀取數(shù)據(jù)庫(kù)信息并生成XML字符串的功能。
/// <summary>
/// 實(shí)現(xiàn)對(duì)單個(gè)表的轉(zhuǎn)換為XML文檔的函數(shù)
/// </summary>
/// <param name=\"tableName\">表名</param>
/// <returns>XML字符串</returns>
public string getSQLTableXML(string tableName)
{string fieldType = \"\";
string strSQL = 1; //用戶查詢單個(gè)表中的字段屬性
strSQL = @\"select sysobjects.name ,syscolumns.name,systypes.name ,syscolumns.length , syscolumns.is1able ,sysobjects.type from syscolumns, sysobjects, systypes where syscolumns.id=sysobjects.id and syscolumns.xusertype=systypes.xusertype and (sysobjects.type='U' or sysobjects.type='V' ) and systypes.name <>'_default_' and systypes.name<>'sysname'and sysobjects.name ='\" + tableName + \"' order by sysobjects.name, syscolumns.name\";
DataView mydv = SQLHelper.ExecuteDataView(strSQL);
StringBuilder sb = new StringBuilder(); //構(gòu)建XML字符串
if (mydv.Count > 0)
{sb.Append(\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\");
sb.Append(\"<TableInfo xmlns:xsi=\\\"http://www.w3.org/2001/XMLSchema-instance\\\" xmlns:xsd=\\\"http://www.w3.org/2001/XMLSchema\\\">\");
sb.Append(\"using System;\");
sb.Append(\"<Schema>\");
sb.Append(\"<SchemaName>test</SchemaName>\");
sb.Append(\"<Table>\");
sb.Append(\"<TableName>\" + mydv[0][0].ToString() + \"</TableName>\");
sb.Append(\"<Fields>\");
for (int i = 0; i < mydv.Count; i++)
{sb.Append(\"<Field>\");
sb.Append(\"<Name>\" + mydv[i][1].ToString() + \"</Name>\");
fieldType = this.getType(mydv[i][2].ToString());
sb.Append(\"<FieldType>\" + fieldType + \" </FieldType>\");
sb.Append(\"<FieldLength>\" + mydv[i][2].ToString() + \" </FieldLength>\");
sb.Append(\"</Field>\");
}
sb.Append(\"</Fields>\");
sb.Append(\"</Table>\");
sb.Append(\"</Schema>\");
sb.Append(\"</TableInfo>\");
}return sb.ToString();
}
通過上面的代碼我們可以將數(shù)據(jù)庫(kù)中的一個(gè)表轉(zhuǎn)換為一個(gè)XML字符串。生成后的XML文件格式為:
<?xml version=\"1.0\" encoding=\"utf-8\"?>
<TableInfo xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
<Schema>
<SchemaName>test</SchemaName> <!--程序集的名稱-->
<Table>
<TableName>t_sys_role</TableName><!--表名的名稱-->
<Fields>
<Field>
<Name>role_id</Name><!--字段的名稱-->
<FieldType>int</FieldType><!--字段的類型-->
<FieldType>int</FieldType><!--字段的類型-->
<FieldLength>4</FieldLength><!--字段的長(zhǎng)度-->
</Field>
……
</Fields>
</Table>
</Schema>
</TableInfo>
這樣我們就完成了數(shù)據(jù)庫(kù)模型向XML文檔的轉(zhuǎn)換。這里注意轉(zhuǎn)換中要將數(shù)據(jù)庫(kù)數(shù)據(jù)類型轉(zhuǎn)換為C#語言或其他語言的數(shù)據(jù)類型。如下所示:
private string getType(string dbtype)
{switch (dbtype)
{case \"varchar\":
……
return \"string\";
case \"date\":
……
return \"DateTime\";
……}
2.2 制定生成代碼的模板
XSLT是一種將XML文檔轉(zhuǎn)換為其他文本文檔的語言,它是建立在XML和XPath之上的國(guó)際標(biāo)準(zhǔn),功能強(qiáng)大。我們這里使用XSLT作為模板,可以方便的將XML文檔轉(zhuǎn)換為具體的代碼。下面是一個(gè)簡(jiǎn)單的模板用于生成POJO類:
<?xml version=\"1.0\" encoding=\"utf-8\" ?>
<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">
<xsl:output method='text' />
<xsl:template match=\"/*\">
using System;
namespace <xsl:apply-templates select=\"http://SchemaName\" />
{public class <xsl:apply-templates select=\"http://TableName\" />
{<xsl:for-each select=\"Schema/Table/Fields/Field\"><xsl:value-of select=\"FieldType\" /> _<xsl:value-of select=\"Name\" /> ;
</xsl:for-each>
<xsl:for-each select=\"Schema/Table/Fields/Field\">
public <xsl:value-of select=\"FieldType\" /> <xsl:value-of select=\"Name\" />
{get{return _<xsl:value-of select=\"Name\" />;}
set{_<xsl:value-of select=\"Name\" /> = Value;}
}</xsl:for-each>
}</xsl:template>
</xsl:stylesheet>
當(dāng)然模板你可以自己定義,然后在程序中動(dòng)態(tài)的加載,從而實(shí)現(xiàn)多語言(VB.NET、JAVA等),并且可以設(shè)計(jì)自己的專用模板來生成自己想要的代碼。
2.3 將XML轉(zhuǎn)換為代碼
現(xiàn)在我們已經(jīng)完成了XML和XSLT這兩個(gè)文檔了,下面就是用.NET平臺(tái)下的System.Xml.Xsl. XslCompiledTransform類來實(shí)現(xiàn)生成代碼了。
xml = gc.getTableXML(\"t_sys_role\");//獲取表的基本信息存儲(chǔ)到XML文件中
System.Xml.Xsl. XslCompiledTransform transform = new System.Xml.Xsl. XslCompiledTransform ();
transform.Load(\"_HN_POJO.xslt\"); //加載POJO的XSLT文件
transform.Transform(\"role.xml\",\"role.cs\"); //使用Transform方法實(shí)現(xiàn)代碼的生成,生成文件為role.cs
//復(fù)制生成文件到指定的目錄下
System.IO.File.Copy(projectPath + @\"\\ role.cs\", txtProjectFolder.Text + @\"\\mapping\\\", true);
transform.Load(\"_HN_HBM.xslt\");//加載HBM的XSLT文件
transform.Transform(“role.xml”,”role.hbm.xml”);
System.IO.File.Copy(projectPath + @\"\\ role.hbm.xml \", txtProjectFolder.Text + @\"\\mapping\\\", true);
通過以上的代碼九完成了將role.xml轉(zhuǎn)換為role.cs的過程了。生成后的代碼效果如下:
using System;
namespace test
{ public class t_sys_role
{ int _role_id ; …
public int _role_id
{ get{return _role_id;}
set{_role_id = Value;}
}
……}
同時(shí)也生成了需要的hbm.xml文件。
3 程序構(gòu)架
程序的整個(gè)結(jié)構(gòu)如圖1所示。
其中的代碼核心部分在KitCore類庫(kù)中:
DataBaseInfo.cs、FieldInfo.cs、TableInfo.cs分別用于表述數(shù)據(jù)庫(kù)信息、字段信息、表信息。
SQLHelper.cs主要實(shí)現(xiàn)操作SQL數(shù)據(jù)庫(kù)的幫助類。(可以根據(jù)需要增加相關(guān)的數(shù)據(jù)庫(kù)操作)
GenerateCore.cs主要實(shí)現(xiàn)代碼生成的類。
NHibernate Kit應(yīng)用程序是主要完成界面的功能:MainForm.cs是主界面,AboutBox.cs是軟件信息,KitHelper.cs是一些幫助類。界面請(qǐng)參考圖2(用于數(shù)據(jù)庫(kù)的連接)、圖3(選擇生成的數(shù)據(jù)表)、圖4(設(shè)置相關(guān)的參數(shù))、圖5(生成代碼)、圖6(設(shè)置用于生成的模板)。
下面給出部分主要的源代碼:
3.1 獲取本地的SQL服務(wù)器名(圖2中的刷新按鈕)
private void btnRefresh_Click(object sender, EventArgs e)
{try
{this.Cursor = Cursors.WaitCursor; //修改鼠標(biāo)的狀態(tài)為沙漏狀
//獲取本地計(jì)算機(jī)SQL服務(wù)器名
cmbServerNames.DataSource = System.Data.Sql.SqlDataSourceEnumerator.Instance.GetDataSources();
cmbServerNames.DisplayMember = \"ServerName\";
}
……}
3.2 獲取指定服務(wù)器下的數(shù)據(jù)庫(kù)列表(圖2中的下拉框)
private void cmbDatabaseNames_DropDown(object sender, EventArgs e)
{try
{this.Cursor = Cursors.WaitCursor;
//通過SQLHelper.cs中的getData Set方法返回?cái)?shù)據(jù)源,其中select name, dbid from sysdatabases用于返回服務(wù)器中的所有數(shù)據(jù)庫(kù)。
cmbDatabaseNames.DataSource =SQLHelper.getDataSet (\"select name, dbid from sysdatabases\");
cmbDatabaseNames.DisplayMember = \"name\";
cmbDatabaseNames.ValueMember = \"dbid\";
}
……}
3.2 獲取指定數(shù)據(jù)庫(kù)下的數(shù)據(jù)表列表(圖2中的下一步)
private void btnNext_Click(object sender, EventArgs e)
{……
this.Cursor = Cursors.WaitCursor;
SetConnectionString();
DataTable dt = dataClass.ExecuteReader(\"SELECT [TableName] = so.name, [ID] = so.id FROM sysobjects so WHERE OBJECTPROPERTY(so.id, 'IsMsShipped') = 0 AND so.xtype = 'U'\");
chklistTables.Items.Clear();
foreach (DataRow dr in dt.Rows)
{chklistTables.Items.Add(dr[\"TableName\"]);}
……}
3.3 生成代碼按鈕的代碼(圖5中的三角形按鈕)
private void toolStripButton1_Click(object sender, EventArgs e)
{ //調(diào)用GenerateCore.cs中的Generate方法來生成代碼。絕大部分代碼已經(jīng)在設(shè)計(jì)原理中給出了,這里不在重復(fù)給出了
System.Threading.Thread t = new System.Threading.Thread((new System.Threading.ThreadStart(gc.Generate)));
t.Start();}
圖3、圖4、圖6主要是一些參數(shù)的設(shè)置,其代碼部分比較簡(jiǎn)單不一一列出了。
4 結(jié)束語
本文主要討論了通過XML、XSLT技術(shù)來設(shè)計(jì)NHibernate代碼生成器,使用XML來描述數(shù)據(jù)庫(kù)結(jié)構(gòu)而XSLT來完成模板功能,最后通過C#語言完成代碼的生成。目前,代碼生成技術(shù)已經(jīng)廣泛的應(yīng)用到實(shí)際的項(xiàng)目開發(fā)之中,大量的單位和個(gè)人都開進(jìn)入到這個(gè)方面。通過設(shè)計(jì)模型驅(qū)動(dòng)編碼的思想已經(jīng)逐步開始實(shí)現(xiàn)并應(yīng)用到實(shí)際項(xiàng)目之中。
參考文獻(xiàn):
[1] 陳翔, 王學(xué)斌, 吳泉源. 代碼生成技術(shù)在MDA中的實(shí)現(xiàn)[J].計(jì)算機(jī)應(yīng)用研究,2006(1):147.
[2] 范秋生. XML的代碼生成器的設(shè)計(jì)與實(shí)現(xiàn)[J].長(zhǎng)江大學(xué)學(xué)報(bào)(自然科學(xué)版),2008,5(1):211.
[3] 王忠杰,戰(zhàn)德臣,徐曉飛. 基于對(duì)象關(guān)系模型的企業(yè)應(yīng)用軟件代碼生成器[J].計(jì)算機(jī)集成制造系統(tǒng),2007(5):1021.
[4] 袁永福. C#發(fā)現(xiàn)之旅[EB/OL]. [2008-09-08].http://www.cnblogs.com/xdesigner/.