程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> vc教程 >> 使用設計模式構建通用數據庫訪問類

使用設計模式構建通用數據庫訪問類

編輯:vc教程

  在應用程序的設計中,數據庫的訪問是非常重要的,我們通常需要將對數據庫的訪問集中起來,以保證良好的封裝性和可維護性。在.Net中,數據庫的訪問,對於微軟自家的SqlServer和其他數據庫(支持OleDb),采用不同的訪問方法,這些類分別分布於System.Data.SqlClIEnt和System.Data.OleDb名稱空間中。微軟後來又推出了專門用於訪問Oracle數據庫的類庫。我們希望在編寫應用系統的時候,不因這麼多類的不同而受到影響,能夠盡量做到數據庫無關,當後台數據庫發生變更的時候,不需要更改客戶端的代碼。

  有的時候,為了性能和其他原因,我們也希望提供對數據庫訪問的緩存,特別是數據庫連接的緩存。雖然微軟給我們內置了數據庫緩存,但是,自己控制緩存,無疑可以提供更大的靈活性和效率。

  這就需要我們在實際開發過程中將這些數據庫訪問類再作一次封裝。這裡,介紹一種在實際應用中得到了非常好的效果的實作策略。Factory和Silgleton設計模式是使用的主要方法。

  我們先來看看Factory的含義:定義一個用於創建對象的接口,讓子類決定實例化哪一個類。Factory Method使一個類的實例化延遲到其子類。我們這裡可能會處理對多種數據庫的操作,因此,需要首先定義一個操縱數據庫的接口,然後,根據數據庫的不同,由類工廠決定實例化哪個類。

  下面,我們首先來定義這個訪問接口。為了方便說明問題,我們為這個類定義了比較少的方法,其他的方法是很容易參照添加的。同時注意,我這裡使用了abstract class來定義這個訪問接口,而不是interface,理由在後面可以看到。

public abstract class DBOperator
{
public abstract IDbConnection Connection{get;} //得到數據庫連接
public abstract void Open(); //打開數據庫連接
public abstract void Close(); //關閉數據庫連接
public abstract void BeginTrans(); //開始一個事務
public abstract void CommitTrans(); //提交一個事務
public abstract void RollbackTrans(); //回滾一個事務
public abstract void exeSql(string strSql,string[] strParams,object[] objValues);
//執行Sql語句,沒有返回值
public abstract DataSet exeSqlForDataSet(string QueryString);//執行Sql,返回DataSet
}

  然後,我們分別為SQL Server和OleDb數據庫編寫兩個數據訪問的具體實現類:

  SQL Server的數據庫訪問類:

internal class SqlDBOperator: DBOperator
{
private SqlConnection conn; //數據庫連接
private SqlTransaction trans; //事務處理類
private bool inTransaction=false; //指示當前是否正處於事務中
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("查詢參數和值不對應!");
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數據庫操作的類同SQL Server數據庫操作的類非常相似,只是把相應的Sql類替換成OleDb類。需要注意的是,因為OleDb和Sql Server的參數傳遞方式不一致,所以,這裡需要做一點小小的轉換,將"@參數名"類型的參數轉換成"?",這個細節希望讀者能夠注意到。代碼如下:

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("查詢參數和值不對應!");
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;
  }
}
  現在我們已經完成了所要的功能,下面,我們需要創建一個Factory類,來實現自動數據庫切換的管理。這個類很簡單,主要的功能就是根據數據庫連接字符串,判斷使用什麼數據庫,然後,返回適當的數據庫操縱類。在這裡,判斷的方法很簡單,只是根據兩種數據庫連接字符串的不同來判斷。在實際中,隨著數據庫類的增加,判斷的方法可能會有所變化,讀者應當根據自己的實際情況來做相應的調整。

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);
}
}
}

  好了,現在,一切都完成了,客戶端在代碼調用的時候,可能就是采用如下形式:

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();

  當數據庫發生變化的時候,DBOperatorFactory會根據數據庫連接字符串自動調用相應的類,客戶端不會感覺到變化,也不用去關心。這樣,實現了良好的封裝性。當然,前提是,你在編寫程序的時候,沒有用到特定數據庫的特性,例如,SQL Server的專用函數。

  實際上,Factory模式也可以不使用Factory類來實現,而是讓接口抽象類自己來管理,這可以稱作自管理的Factory,是Factory模式的一種變形。這麼做的好處,是可以免去一個Factory類,使代碼更加簡練。這麼做,我們需要對DBOperator類做一些改變,增加一個Instance方法。這也是對DBOperator采用抽象類而不是接口的原因(接口的方法不能有實現),代碼如下:

public static DBOperator Instance(string strConnetion)
{
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();


  下面來看看連接池的做法,方法就是Singleton。

  先看Singleton模式的經典含義:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。推而廣之,當我們需要精確控制類在系統中的實例的個數的時候,就可以使用Singleton模式。現在,我們需要構建一個緩沖池,保存數據庫類的實例,正好是Singleton模式發揮作用的時候。

  我們仍然讓DBOperator類自己來管理這個緩沖池,為了實現這個目的,我們需要對DBOperator類做一些變化:

  首先,增加兩個變量:

 static DBOperator[] ConnectionPool=new
DBOperator[int.Parse(ConfigurationSettings.APPSettings["PoolCount"])];
 static int CurrentPosition=-1;

  然後,對Instance方法做一些改變:

public static DBOperator Instance(string strConnection)
{
if(ApplicationConfiguration.PooledConnectionCount<1) //沒有緩沖
{
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);
}
}

  這裡使用的算法比較簡單,只是為了能夠比較清楚地說明問題,讀者應當能夠在實際使用過程中,實現更好的算法。

  以上,介紹了一種通用數據庫操作類的實現設計方法,希望能夠對大家有所啟發。筆者設計Websharp中間件的時候,在數據庫處理層,采用了上面的方法,取得了很好的效果。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved