在應(yīng)用程序的設(shè)計(jì)中,數(shù)據(jù)庫(kù)的訪問(wèn)是非常重要的,我們通常需要將對(duì)數(shù)據(jù)庫(kù)的訪問(wèn)集中起來(lái),以保證良好的封裝性和可維護(hù)性。在.Net中,數(shù)據(jù)庫(kù)的訪問(wèn),對(duì)于微軟自家的SqlServer和其他數(shù)據(jù)庫(kù)(支持OleDb),采用不同的訪問(wèn)方法,這些類分別分布于System.Data.SqlClient和System.Data.OleDb名稱空間中。微軟后來(lái)又推出了專門用于訪問(wèn)Oracle數(shù)據(jù)庫(kù)的類庫(kù)。我們希望在編寫(xiě)應(yīng)用系統(tǒng)的時(shí)候,不因這么多類的不同而受到影響,能夠盡量做到數(shù)據(jù)庫(kù)無(wú)關(guān),當(dāng)后臺(tái)數(shù)據(jù)庫(kù)發(fā)生變更的時(shí)候,不需要更改客戶端的代碼。
有的時(shí)候,為了性能和其他原因,我們也希望提供對(duì)數(shù)據(jù)庫(kù)訪問(wèn)的緩存,特別是數(shù)據(jù)庫(kù)連接的緩存。雖然微軟給我們內(nèi)置了數(shù)據(jù)庫(kù)緩存,但是,自己控制緩存,無(wú)疑可以提供更大的靈活性和效率。
這就需要我們?cè)趯?shí)際開(kāi)發(fā)過(guò)程中將這些數(shù)據(jù)庫(kù)訪問(wèn)類再作一次封裝。這里,介紹一種在實(shí)際應(yīng)用中得到了非常好的效果的實(shí)作策略。Factory和Silgleton設(shè)計(jì)模式是使用的主要方法。
我們先來(lái)看看Factory的含義:定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類決定實(shí)例化哪一個(gè)類。Factory Method使一個(gè)類的實(shí)例化延遲到其子類。我們這里可能會(huì)處理對(duì)多種數(shù)據(jù)庫(kù)的操作,因此,需要首先定義一個(gè)操縱數(shù)據(jù)庫(kù)的接口,然后,根據(jù)數(shù)據(jù)庫(kù)的不同,由類工廠決定實(shí)例化哪個(gè)類。
下面,我們首先來(lái)定義這個(gè)訪問(wèn)接口。為了方便說(shuō)明問(wèn)題,我們?yōu)檫@個(gè)類定義了比較少的方法,其他的方法是很容易參照添加的。同時(shí)注意,我這里使用了abstract class來(lái)定義這個(gè)訪問(wèn)接口,而不是interface,理由在后面可以看到。
public abstract class DBOperator
{
public abstract IDbConnection Connection{get;} //得到數(shù)據(jù)庫(kù)連接
public abstract void Open(); //打開(kāi)數(shù)據(jù)庫(kù)連接
public abstract void Close(); //關(guān)閉數(shù)據(jù)庫(kù)連接
public abstract void BeginTrans(); //開(kāi)始一個(gè)事務(wù)
public abstract void CommitTrans(); //提交一個(gè)事務(wù)
public abstract void RollbackTrans(); //回滾一個(gè)事務(wù)
public abstract void exeSql(string strSql,string[] strParams,object[] objValues);
//執(zhí)行Sql語(yǔ)句,沒(méi)有返回值
public abstract DataSet exeSqlForDataSet(string QueryString);//執(zhí)行Sql,返回DataSet
}
然后,我們分別為Sql Server和OleDb數(shù)據(jù)庫(kù)編寫(xiě)兩個(gè)數(shù)據(jù)訪問(wèn)的具體實(shí)現(xiàn)類:
Sql Server的數(shù)據(jù)庫(kù)訪問(wèn)類:
internal class SqlDBOperator: DBOperator
{
private SqlConnection conn; //數(shù)據(jù)庫(kù)連接
private SqlTransaction trans; //事務(wù)處理類
private bool inTransaction=false; //指示當(dāng)前是否正處于事務(wù)中
public override IDbConnection Connection
{
get{return this.conn;}
}
public SqlDBOperator(string strConnection)
{
this.conn= new SqlConnection(strConnection);
}
public override void Open()
{
if(conn.State.ToString().ToUpper()!="OPEN")
this.conn.Open();
}
public override void Close()
{
if (conn.State.ToString().ToUpper()=="OPEN")
this.conn.Close();
}
public override void BeginTrans()
{
trans=conn.BeginTransaction() ;
inTransaction=true;
}
public override void CommitTrans()
{
trans.Commit();
inTransaction=false;
}
public override void RollbackTrans()
{
trans.Rollback();
inTransaction=false;
}
public override void exeSql(string strSql,string[] strParams,object[] strValues)
{
SqlCommand cmd=new SqlCommand();
cmd.Connection=this.conn ;
if(inTransaction)
cmd.Transaction=trans;
if((strParams!=null)&&(strParams.Length!=strValues.Length) )
throw new ParamValueNotMatchException("查詢參數(shù)和值不對(duì)應(yīng)!");
cmd.CommandText=strSql;
if(strParams!=null)
{
for(int i=0;i
cmd.Parameters.Add(strParams[i],strValues[i]);
}
cmd.ExecuteNonQuery();
}
public override DataSet exeSqlForDataSet(string QueryString)
{
SqlCommand cmd=new SqlCommand();
cmd.Connection=this.conn ;
if(inTransaction)
cmd.Transaction=trans;
DataSet ds = new DataSet();
SqlDataAdapter ad = new SqlDataAdapter();
cmd.CommandText=QueryString;
ad.SelectCommand =cmd;
ad.Fill(ds);
return ds;
}
}
OleDb數(shù)據(jù)庫(kù)操作的類同Sql Server數(shù)據(jù)庫(kù)操作的類非常相似,只是把相應(yīng)的Sql類替換成OleDb類。需要注意的是,因?yàn)镺leDb和Sql Server的參數(shù)傳遞方式不一致,所以,這里需要做一點(diǎn)小小的轉(zhuǎn)換,將"@參數(shù)名"類型的參數(shù)轉(zhuǎn)換成"?",這個(gè)細(xì)節(jié)希望讀者能夠注意到。代碼如下:
internal class OleDBOperator : DBOperator
{
private OleDbConnection conn;
private OleDbTransaction trans;
private bool inTransaction=false;
public OleDBOperator(string strConnection)
{
this.conn= new OleDbConnection(strConnection);
}
public override IDbConnection Connection
{
get{return this.conn;}
}
public override void Open()
{
if(conn.State.ToString().ToUpper()!="OPEN")
this.conn.Open();
}
public override void Close()
{
if (conn.State.ToString().ToUpper()=="OPEN")
this.conn.Close();
}
public override void BeginTrans()
{
trans=conn.BeginTransaction() ;
inTransaction=true;
}
public override void CommitTrans()
{
trans.Commit();
inTransaction=false;
}
public override void RollbackTrans()
{
trans.Rollback();
inTransaction=false;
}
public override void exeSql(string strSql,string[] strParams,object[] strValues)
{
OleDbCommand cmd=new OleDbCommand();
cmd.Connection=this.conn ;
if(inTransaction)
cmd.Transaction=trans;
if((strParams!=null)&&(strParams.Length!=strValues.Length) )
throw new ParamValueNotMatchException("查詢參數(shù)和值不對(duì)應(yīng)!");
cmd.CommandText=this.ChangeQueryString(strSql);
if(strParams!=null)
{
for(int i=0;i
cmd.Parameters.Add(strParams[i],strValues[i]);
}
cmd.ExecuteNonQuery();
}
public override DataSet exeSqlForDataSet(string QueryString)
{
OleDbCommand cmd=new OleDbCommand();
cmd.Connection=this.conn ;
if(inTransaction)
cmd.Transaction=trans;
DataSet ds = new DataSet();
OleDbDataAdapter ad = new OleDbDataAdapter();
cmd.CommandText=QueryString;
ad.SelectCommand =cmd;
ad.Fill(ds);
return ds;
}
}
現(xiàn)在我們已經(jīng)完成了所要的功能,下面,我們需要?jiǎng)?chuàng)建一個(gè)Factory類,來(lái)實(shí)現(xiàn)自動(dòng)數(shù)據(jù)庫(kù)切換的管理。這個(gè)類很簡(jiǎn)單,主要的功能就是根據(jù)數(shù)據(jù)庫(kù)連接字符串,判斷使用什么數(shù)據(jù)庫(kù),然后,返回適當(dāng)?shù)臄?shù)據(jù)庫(kù)操縱類。在這里,判斷的方法很簡(jiǎn)單,只是根據(jù)兩種數(shù)據(jù)庫(kù)連接字符串的不同來(lái)判斷。在實(shí)際中,隨著數(shù)據(jù)庫(kù)類的增加,判斷的方法可能會(huì)有所變化,讀者應(yīng)當(dāng)根據(jù)自己的實(shí)際情況來(lái)做相應(yīng)的調(diào)整。
public class DBOperatorFactory
{
public static DBOperator GetDBOperator(string strConnection)
{
if(strConnection.IndexOf("provider=")<0) //SqlServer
{
return new SqlDBOperator(strConnection);
}
else //other database
{
return new OleDBOperator(strConnection);
}
}
}
好了,現(xiàn)在,一切都完成了,客戶端在代碼調(diào)用的時(shí)候,可能就是采用如下形式:
DBOperator db=DBOperatorFactory.GetDBOperator(strConnection)
db.Open();
db.需要的操作
db.Close();
或者:
DBOperator db=DBOperatorFactory.GetDBOperator(strConnection)
db.Open();db.BeginTrans();
try
{
db.需要的操作
db.CommitTrans();
}
catch
{
db.RollbackTrans();
}
db.Close();
當(dāng)數(shù)據(jù)庫(kù)發(fā)生變化的時(shí)候,DBOperatorFactory會(huì)根據(jù)數(shù)據(jù)庫(kù)連接字符串自動(dòng)調(diào)用相應(yīng)的類,客戶端不會(huì)感覺(jué)到變化,也不用去關(guān)心。這樣,實(shí)現(xiàn)了良好的封裝性。當(dāng)然,前提是,你在編寫(xiě)程序的時(shí)候,沒(méi)有用到特定數(shù)據(jù)庫(kù)的特性,例如,Sql Server的專用函數(shù)。
實(shí)際上,F(xiàn)actory模式也可以不使用Factory類來(lái)實(shí)現(xiàn),而是讓接口抽象類自己來(lái)管理,這可以稱作自管理的Factory,是Factory模式的一種變形。這么做的好處,是可以免去一個(gè)Factory類,使代碼更加簡(jiǎn)練。這么做,我們需要對(duì)DBOperator類做一些改變,增加一個(gè)Instance方法。這也是對(duì)DBOperator采用抽象類而不是接口的原因(接口的方法不能有實(shí)現(xiàn)),代碼如下:
public static DBOperator Instance(string strConnection)
{
if(strConnection.IndexOf("provider=")<0) //SqlServer
{
return new SqlDBOperator(strConnection);
}
else //other database
{
return new OleDBOperator(strConnection);
}
}
然后,客戶端代碼就可能是類似于下面的形式:
DBOperator db= DBOperator.Instance(strConnection)
db.Open();
db.需要的操作
db.Close();
下面來(lái)看看連接池的做法,方法就是Singleton。
先看Singleton模式的經(jīng)典含義:保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)。推而廣之,當(dāng)我們需要精確控制類在系統(tǒng)中的實(shí)例的個(gè)數(shù)的時(shí)候,就可以使用Singleton模式。現(xiàn)在,我們需要構(gòu)建一個(gè)緩沖池,保存數(shù)據(jù)庫(kù)類的實(shí)例,正好是Singleton模式發(fā)揮作用的時(shí)候。
我們?nèi)匀蛔孌BOperator類自己來(lái)管理這個(gè)緩沖池,為了實(shí)現(xiàn)這個(gè)目的,我們需要對(duì)DBOperator類做一些變化:
首先,增加兩個(gè)變量:
static DBOperator[] ConnectionPool=new
DBOperator[int.Parse(ConfigurationSettings.AppSettings["PoolCount"])];
static int CurrentPosition=-1;
然后,對(duì)Instance方法做一些改變:
public static DBOperator Instance(string strConnection)
{
if(ApplicationConfiguration.PooledConnectionCount<1) //沒(méi)有緩沖
{
return CreateNewDBOperator(strConnection);
}
else
{
CurrentPosition++;
if(CurrentPosition==ApplicationConfiguration.PooledConnectionCount)
CurrentPosition=0;
if(ConnectionPool[CurrentPosition]==null)
{
ConnectionPool[CurrentPosition]=CreateNewDBOperator(strConnection);
}
return ConnectionPool[CurrentPosition];
}
}
private static DBOperator CreateNewDBOperator(string strConnection)
{
if(strConnection.IndexOf("provider=")<0) //SqlServer
{
return new SqlDBOperator(strConnection);
}
else //other database
{
return new OleDBOperator(strConnection);
}
}
這里使用的算法比較簡(jiǎn)單,只是為了能夠比較清楚地說(shuō)明問(wèn)題,讀者應(yīng)當(dāng)能夠在實(shí)際使用過(guò)程中,實(shí)現(xiàn)更好的算法。
以上,介紹了一種通用數(shù)據(jù)庫(kù)操作類的實(shí)現(xiàn)設(shè)計(jì)方法,希望能夠?qū)Υ蠹矣兴鶈l(fā)。筆者設(shè)計(jì)Websharp中間件的時(shí)候,在數(shù)據(jù)庫(kù)處理層,采用了上面的方法,取得了很好的效果。
------------------------------- · 相關(guān)文檔瀏覽 · --------------------------------------------------------------------- · 熱門文檔瀏覽 · -------------------------------------