本章介紹框架中封裝的數據庫操作的一些功能,在實現的過程中費了不少心思,針對不同數據庫的操作(SQLServer、Oracle、DB2)這方面還是比較簡單的,用工廠模式就能很好解決,反而是在多數據庫同時操作方面走了不少彎路;現在從以下幾個方面進行說明:
本文要點:
1.不同數據庫操作
2.多數據庫同時操作
3.數據庫事務處理
4.數據分頁處理
5.關於oleDb對象創建與銷毀的生命周期
一、不同數據庫操作
此處用到了工廠模式來實現不同數據庫操作,看下圖
AbstractDatabase是一個抽象類,定義了所有對數據庫的操作抽象方法,包括執行一個SQL語句、執行存儲過程、事務操作等

[Serializable] public abstract class AbstractDatabase { #region 屬性 /// <summary> /// 數據庫事務 /// </summary> protected DbTransaction transaction = null; //數據庫事務 protected string _connString; /// <summary> /// 返回數據庫連接字符串 /// </summary> public string ConnectionString { get { return _connString; } } protected bool isInTransaction = false; //是否在事務中 /// <summary> /// 返回是否處於事務中 /// </summary> protected bool IsInTransaction { get { return this.isInTransaction; } } public int WorkId { get; set; } public string DbKey { get; set; } public DatabaseType DbType { get; set; } public AbstractDatabase newOleDb(string dbkey) { return this; } public AbstractDatabase defaultOleDb() { return this; } public abstract void TestDbConnection(); #endregion /// <summary> /// 啟動一個事務 /// </summary> public abstract void BeginTransaction(); /// <summary> /// 提交一個事務 /// </summary> public abstract void CommitTransaction(); /// <summary> /// 回滾一個事務 /// </summary> public abstract void RollbackTransaction(); public abstract DbCommand GetDbCommand(); #region 執行插入一條記錄 適用於有 自動生成標識的列 public abstract int InsertRecord(IDbCommand cmd); public abstract int InsertRecord(string commandtext); #endregion #region 返回一個DataTable public abstract DataTable GetDataTable(IDbCommand cmd); public abstract DataTable GetDataTable(string commandtext); public abstract DataTable GetDataTable(string storeProcedureName, params object[] parameters); #endregion #region 返回一個DataReader public abstract IDataReader GetDataReader(IDbCommand cmd); public abstract IDataReader GetDataReader(string commandtext); #endregion #region 執行一個語句,返回執行情況 public abstract int DoCommand(IDbCommand cmd); public abstract int DoCommand(string commandtext); public abstract int DoCommand(string storeProcedureName, params object[] parameters); #endregion #region 執行一個命令返回一個數據結果 public abstract object GetDataResult(IDbCommand cmd); public abstract object GetDataResult(string commandtext); public abstract object GetDataResult(string storeProcedureName, params object[] parameters); #endregion public abstract DataSet GetDataSet(string storeProcedureName, params object[] parameters); }
SqlServerDb類繼承AbstractDatabase抽象類並實現

public class SqlServerDb : AbstractDatabase { /// <summary> /// 數據庫連接 /// </summary> protected DbConnection connection = null; //數據庫連接 /// <summary> /// 數據庫對象執行命令 /// </summary> protected DbCommand command = null; /// <summary> /// 企業庫數據庫訪問對象 /// </summary> protected Database database = null; public SqlServerDb() : base() { database = ZhyContainer.CreateDataBase(); _connString = database.ConnectionString; } public SqlServerDb(string key) : base() { database = ZhyContainer.CreateDataBase(key); _connString = database.ConnectionString; } public override DbCommand GetDbCommand() { SqlCommand cmd = new SqlCommand(); connection = database.CreateConnection(); connection.Open(); cmd.Connection = (SqlConnection)connection; return cmd; } public override void TestDbConnection() { database.CreateConnection().Open(); } public override void BeginTransaction() { try { if (isInTransaction == false) { connection = database.CreateConnection(); connection.Open(); transaction = connection.BeginTransaction(); isInTransaction = true; } else { throw new Exception("事務正在進行,一個對象不能同時開啟多個事務!"); } } catch (Exception e) { connection.Close(); isInTransaction = false; throw new Exception("事務啟動失敗,請再試一次!\n" + e.Message); } } public override void CommitTransaction() { if (transaction != null) { transaction.Commit(); isInTransaction = false; connection.Close(); }else throw new Exception("無可用事務!"); } public override void RollbackTransaction() { if (transaction != null) { transaction.Rollback(); isInTransaction = false; connection.Close(); }else throw new Exception("無可用事務!"); } public override int InsertRecord(string commandtext) { //string strsql = "SELECT Test_SQL.nextval FROM dual";SELECT @@IDENTITY if (isInTransaction) { command = database.GetSqlStringCommand(commandtext); command.Connection = connection; command.Transaction = transaction; command.CommandType = CommandType.Text; command.CommandText = command.CommandText + ";SELECT @@IDENTITY"; return Convert.ToInt32(database.ExecuteScalar(command, transaction)); } else { command = database.GetSqlStringCommand(commandtext); command.CommandText = command.CommandText + ";SELECT @@IDENTITY"; return Convert.ToInt32(database.ExecuteScalar(command)); } //command.CommandText = "SELECT @@IDENTITY"; //return Convert.ToInt32(database.ExecuteScalar(command)); } public override int InsertRecord(System.Data.IDbCommand cmd) { command = (System.Data.Common.DbCommand)cmd; command.CommandText = command.CommandText + ";SELECT @@IDENTITY"; object ret; if (isInTransaction) { command.Connection = connection; command.Transaction = transaction; ret = database.ExecuteScalar(command, transaction); return Convert.ToInt32(ret == DBNull.Value ? 1 : ret); } ret = database.ExecuteScalar(command); return Convert.ToInt32(ret == DBNull.Value ? 1 : ret); //command.CommandText = "SELECT @@IDENTITY"; //return Convert.ToInt32(database.ExecuteScalar(command));//? } public override DataTable GetDataTable(string commandtext) { DataSet ds = null; if (isInTransaction) { command = new SqlCommand(commandtext); command.Connection = connection; command.Transaction = transaction; command.CommandType = CommandType.Text; ds = database.ExecuteDataSet(command,transaction); } else { ds = database.ExecuteDataSet(CommandType.Text, commandtext); } if (ds != null && ds.Tables.Count > 0) { return ds.Tables[0]; } throw new Exception("沒有數據"); } public override DataTable GetDataTable(System.Data.IDbCommand cmd) { command = (System.Data.Common.DbCommand)cmd; DataSet ds = null; if (isInTransaction) { command.Connection = connection; command.Transaction = transaction; ds = database.ExecuteDataSet(command, transaction); } else ds = database.ExecuteDataSet(command); if (ds != null && ds.Tables.Count > 0) { return ds.Tables[0]; } throw new Exception("沒有數據"); } public override DataTable GetDataTable(string storeProcedureName, params object[] parameters) { DataSet ds = null; //List<object> param = new List<object>(); //foreach (IDbDataParameter val in parameters) //{ // param.Add(val.Value); //} if (isInTransaction) { command = database.GetStoredProcCommand(storeProcedureName, parameters); command.Connection = connection; command.Transaction = transaction; command.CommandType = CommandType.Text; ds = database.ExecuteDataSet(command, transaction); } else { ds = database.ExecuteDataSet(storeProcedureName, parameters); } if (ds != null && ds.Tables.Count > 0) { return ds.Tables[0]; } throw new Exception("沒有數據"); } public override global::System.Data.IDataReader GetDataReader(string commandtext) { if (isInTransaction) { command = database.GetSqlStringCommand(commandtext); command.Connection = connection; command.Transaction = transaction; command.CommandType = CommandType.Text; return database.ExecuteReader(command,transaction); } else { return database.ExecuteReader(CommandType.Text, commandtext); } } public override System.Data.IDataReader GetDataReader(System.Data.IDbCommand cmd) { command = (System.Data.Common.DbCommand)cmd; if (isInTransaction) { command.Connection = connection; command.Transaction = transaction; return database.ExecuteReader(command, transaction); } return database.ExecuteReader(command); } public override int DoCommand(string commandtext) { if (isInTransaction) { command = database.GetSqlStringCommand(commandtext); command.Connection = connection; command.Transaction = transaction; command.CommandType = CommandType.Text; return database.ExecuteNonQuery(command,transaction); } else { return database.ExecuteNonQuery(CommandType.Text, commandtext); } } public override int DoCommand(System.Data.IDbCommand cmd) { command = (System.Data.Common.DbCommand)cmd; if (isInTransaction) { command.Connection = connection; command.Transaction = transaction; return database.ExecuteNonQuery(command,transaction); } return database.ExecuteNonQuery(command); } public override int DoCommand(string storeProcedureName, params object[] parameters) { if (isInTransaction) { command = database.GetStoredProcCommand(storeProcedureName, parameters); command.Connection = connection; command.Transaction = transaction; command.CommandType = CommandType.Text; return database.ExecuteNonQuery(command, transaction); } else { return database.ExecuteNonQuery(storeProcedureName, parameters); } } public override object GetDataResult(string commandtext) { if (isInTransaction) { command = database.GetSqlStringCommand(commandtext); command.Connection = connection; command.Transaction = transaction; command.CommandType = CommandType.Text; return database.ExecuteScalar(command, transaction); } else { return database.ExecuteScalar(CommandType.Text, commandtext); } } public override object GetDataResult(System.Data.IDbCommand cmd) { command = (System.Data.Common.DbCommand)cmd; if (isInTransaction) { command.Connection = connection; command.Transaction = transaction; return database.ExecuteScalar(command, transaction); } return database.ExecuteScalar(command); } public override object GetDataResult(string storeProcedureName, params object[] parameters) { if (isInTransaction) { command = database.GetStoredProcCommand(storeProcedureName, parameters); command.Connection = connection; command.Transaction = transaction; command.CommandType = CommandType.Text; return database.ExecuteScalar(command, transaction); } else { return database.ExecuteScalar(storeProcedureName, parameters); } } public override DataSet GetDataSet(string storeProcedureName, params object[] parameters) { DataSet ds = null; List<object> param = new List<object>(); foreach (IDbDataParameter val in parameters) { param.Add(val.Value); } if (isInTransaction) { command = database.GetStoredProcCommand(storeProcedureName, param.ToArray()); command.Connection = connection; command.Transaction = transaction; command.CommandType = CommandType.Text; ds = database.ExecuteDataSet(command, transaction); } else { ds = database.ExecuteDataSet(storeProcedureName, param.ToArray()); } if (ds != null && ds.Tables.Count > 0) { return ds; } throw new Exception("沒有數據"); } }
OracleDb類同上,創建數據庫操作對象實例FactoryDatabase類的實現

public static AbstractDatabase GetDatabase(string dbkey) { if (string.IsNullOrEmpty(dbkey)) throw new Exception("沒有數據庫Key!"); AbstractDatabase _oleDb = null; string dbtype = System.Configuration.ConfigurationManager.AppSettings["DbType"].ToString();//獲取默認數據庫連接 switch (dbtype) { case "SqlServer": _oleDb = new SqlServerDb(dbkey); _oleDb.DbType = DatabaseType.SqlServer2005; break; case "Oracle": _oleDb = new OracleDb(dbkey); _oleDb.DbType = DatabaseType.Oracle; break; } return _oleDb; }
上面代碼需要讀取系統配置文件Web.Config或App.Config的配置的數據庫類型
二、多數據庫同時操作
接下來詳細講解一下多數據庫同時操作,什么時候我們系統會涉及到多數據庫,比如:跟第三方系統對接需要提取數據,而對方並沒有提供程序方面的接口而是把數據庫結構讓你直接訪問;還有業務數據比較多,需要按業務進行分庫才能滿足性能上的需求;還有可能需要把某個子系統產品化,需要獨立設計數據庫;在項目中難免會遇到以上情況,所以框架必須支持對多數據庫同時操作;
為什么說我在此處走了不少彎路,就是為了找到在編碼的時候,怎樣使用起來最簡單、最直觀的方法;一般的實現方法都是定義多個數據庫操作對象,oleDb1、oleDb2。。。這中方法在使用的針對數據庫1就必須用oleDb1,針對數據庫2就必須用oleDb2,編碼人員必須記住每個oleDb是操作的哪個數據庫,一不小心就有可能出錯;我就是想一個oleDb對象搞定多個數據庫,在這個地方想了很久,直到有一天在SQL Server Management Studio中寫sql語句發現它對多數據庫的這種處理方式值得借鑒,看下圖;
SQL Server Management Studio工具在一個頁面操作多個數據庫,只要先用USE命令指定數據庫名,那么下面sql語句都是在指定的數據庫中執行,直到碰到下一個USE;
下面來看框架中是怎么實現的:
先用OpenDBKeys指定哪兩個數據庫別名SQL2005和SQL20052,執行方法Text(),oleDb默認是操作SQL2005數據庫,執行SQL語句“select * from basemenu”,接着通過“UseDb("SQL20052")”切換oleDb為可以操作SQL20052數據庫了;Test()方法執行完后oleDb又恢復到默認數據庫;
不知道大家還有更好的方法來實現多數據庫同時操作,可以一起討論下;
三、數據庫事務處理
關於數據庫事務在上章講EntLib中就有提到過,利用框架的AOP功能來實現;
如上圖,使用起來很簡單,在方法前面加上自定義標簽AOP並綁定AopTransaction對象,這樣InitFields方法中數據庫操作都是在一個事務中進行的;我們在看一下AopTransaction類實現的一些細節;
BeginTransaction()開啟事務
CommitTransaction()、RollbackTransaction()提交事務或異常回滾事務
我們看畫紅線的部分是關於多數據庫事務處理的代碼,先循環BeginTransaction,但是在CommitTransaction或RollbackTransaction的時候必須先對oledb就行反序;意思就是先開始事務的oledb必須最后提交事務;
四、數據分頁處理
數據分頁功能在Web項目中經常用到,分頁一般分為前端分頁和后端分頁,前端分頁就是把數據全部從數據庫取出,在界面控件中進行分頁顯示;而后端分頁是每次只從數據庫庫取指定條數數據;這里當然是講得后端分頁,前端分頁JqueryEasyUI控件就可以幫我們搞定;
PageInfo分頁對象

/// <summary> /// 分頁信息 /// </summary> public class PageInfo { private int _pageSize = 20; private int _pageNo = 1; private int _totalRecord = 0; private string _keyName; private int _columnLength = 1; /// <summary> /// 頁面大小 /// </summary> public int pageSize { get { return _pageSize; } set { _pageSize = value; } } /// <summary> /// 要取的頁面,默認為0頁 /// </summary> public int pageNo { get { return _pageNo; } set { _pageNo = value; } } /// <summary> /// 總頁數 /// </summary> public int totalPage { get { return totalRecord % pageSize == 0 ? totalRecord / pageSize : totalRecord / pageSize + 1; } } /// <summary> /// 總記錄數 /// </summary> public int totalRecord { get { return _totalRecord; } set { _totalRecord = value; } } public int startNum { get { return (pageNo - 1) * pageSize + 1; } } public int endNum { get { return startNum + pageSize - 1; } } public string KeyName { get { return _keyName; } set { _keyName = value; } } /// <summary> /// 列的長度 /// </summary> public int ColumnLength { get { return _columnLength; } set { _columnLength = value; } } public PageInfo(int _pagesize, int _currpagenum) { _pageSize = _pagesize; _pageNo = _currpagenum; } public PageInfo(int _pagesize, int _currpagenum, int _columnlength) { _pageSize = _pagesize; _pageNo = _currpagenum; _columnLength = _columnlength; } }
SqlPage對sql語句進行分頁處理后生成新的sql語句

/// <summary> /// SQL語句進行分頁包裝 /// </summary> public class SqlPage { /// <summary> /// 格式化SQL語句 /// </summary> /// <param name="strsql"></param> /// <param name="pageInfo"></param> /// <param name="oleDb"></param> /// <returns></returns> public static string FormatSql(string strsql, PageInfo pageInfo, AbstractDatabase oleDb) { switch (oleDb.DbType) { case DatabaseType.IbmDb2: return Db2FormatSql(strsql, pageInfo, oleDb); case DatabaseType.MsAccess: return MsAccessFormatSql(strsql, pageInfo, oleDb); case DatabaseType.MySQL: return MySQLFormatSql(strsql, pageInfo, oleDb); case DatabaseType.Oracle: return OracleFormatSql(strsql, pageInfo, oleDb); case DatabaseType.SqlServer2000: return Sql2000FormatSql(strsql, pageInfo, oleDb); case DatabaseType.SqlServer2005: return Sql2005FormatSql(strsql, pageInfo, oleDb); } return null; } private static string Db2FormatSql(string strsql, PageInfo pageInfo, AbstractDatabase oleDb) { if (pageInfo.KeyName == null || pageInfo.KeyName == "") throw new Exception("分頁KeyName屬性不能為空,如:pageInfo.KeyName==\"Id\" 或 pageInfo.KeyName==\"Id|Desc\""); int starRecordNum = pageInfo.startNum; int endRecordNum = pageInfo.endNum; //int index = strsql.ToLower().LastIndexOf("order by"); //string _strsql = null; //if (index != -1) // _strsql = strsql.Remove(index); //else // _strsql = strsql; string _strsql = strsql; string sql_totalRecord = "select count(*) from (" + _strsql + ") A"; Object obj = oleDb.GetDataResult(sql_totalRecord); pageInfo.totalRecord = Convert.ToInt32(obj == DBNull.Value ? 0 : obj); string _sql = _strsql; string[] orderbys = pageInfo.KeyName.Split(new char[] { '|' }); string orderbyname, orderby; if (orderbys.Length != 2) { orderbyname = orderbys[0]; orderby = "desc"; } else { orderbyname = orderbys[0]; orderby = orderbys[1]; } strsql = @"select * from ( select rownumber() over(order by {3} {4}) as rowid, t.* from ({0}) t )as a where a.rowid >= {1} AND a.rowid < {2}"; strsql = String.Format(strsql, _sql, starRecordNum, endRecordNum, orderbyname, orderby); return strsql; } private static string Sql2000FormatSql(string strsql, PageInfo pageInfo, AbstractDatabase oleDb) { return null; } private static string Sql2005FormatSql(string strsql, PageInfo pageInfo, AbstractDatabase oleDb) { if (pageInfo.KeyName == null || pageInfo.KeyName == "") throw new Exception("分頁KeyName屬性不能為空,如:pageInfo.KeyName==\"Id\" 或 pageInfo.KeyName==\"Id|Desc\""); int starRecordNum = pageInfo.startNum; int endRecordNum = pageInfo.endNum; int index = strsql.ToLower().LastIndexOf("order by"); string _strsql = null; if (index != -1) _strsql = strsql.Remove(index); else _strsql = strsql; string sql_totalRecord = "select TOP 1 count(*) from (" + _strsql + ") A"; Object obj = oleDb.GetDataResult(sql_totalRecord); pageInfo.totalRecord = Convert.ToInt32(obj == DBNull.Value ? 0 : obj); string _sql = _strsql; string[] orderbys = pageInfo.KeyName.Split(new char[] { '|' }); string orderbyname, orderby; if (orderbys.Length != 2) { orderbyname = orderbys[0]; orderby = "desc"; } else { orderbyname = orderbys[0]; orderby = orderbys[1]; } strsql = @"select * from ( select row_number() over(order by {3} {4}) as rownum,t.* from ({0}) t ) as a where rownum between {1} and {2}"; strsql = String.Format(strsql, _sql, starRecordNum, endRecordNum, orderbyname, orderby); return strsql; } private static string MsAccessFormatSql(string strsql, PageInfo pageInfo, AbstractDatabase oleDb) { return null; } private static string MySQLFormatSql(string strsql, PageInfo pageInfo, AbstractDatabase oleDb) { if (pageInfo.KeyName == null || pageInfo.KeyName == "") throw new Exception("分頁KeyName屬性不能為空,如:pageInfo.KeyName==\"Id\" 或 pageInfo.KeyName==\"Id|Desc\""); int starRecordNum = pageInfo.startNum; int endRecordNum = pageInfo.endNum; //int index = strsql.ToLower().LastIndexOf("order by"); //string _strsql = null; //if (index != -1) // _strsql = strsql.Remove(index); //else // _strsql = strsql; string _strsql = strsql; string sql_totalRecord = "select count(*) from (" + _strsql + ") A"; Object obj = oleDb.GetDataResult(sql_totalRecord); pageInfo.totalRecord = Convert.ToInt32(obj == DBNull.Value ? 0 : obj); string _sql = _strsql; string[] orderbys = pageInfo.KeyName.Split(new char[] { '|' }); string orderbyname, orderby; if (orderbys.Length != 2) { orderbyname = orderbys[0]; orderby = "desc"; } else { orderbyname = orderbys[0]; orderby = orderbys[1]; } strsql = @"select * from ( select rownumber() over(order by {3} {4}) as rowid, t.* from ({0}) t )as a where a.rowid >= {1} AND a.rowid < {2}"; strsql = String.Format(strsql, _sql, starRecordNum, endRecordNum, orderbyname, orderby); return strsql; } private static string OracleFormatSql(string strsql, PageInfo pageInfo, AbstractDatabase oleDb) { int starRecordNum = pageInfo.startNum; int endRecordNum = pageInfo.endNum; string sql_totalRecord = "select count(*) from (" + strsql + ") A"; Object obj = oleDb.GetDataResult(sql_totalRecord); pageInfo.totalRecord = Convert.ToInt32(obj == DBNull.Value ? 0 : obj); strsql = " select * from( select a.*,rownum rn from ( " + strsql + " ) a ) where rn between " + starRecordNum.ToString() + " and " + endRecordNum.ToString(); return strsql; } }
五、關於oleDb對象創建與銷毀的生命周期
關於oleDb對象的生命周期是什么意思,為什么要合理的控制?比如:我們知道Controller是可以調用ObjectModel對象的,也可以調用Dao對象,如果對Controller中的方法加上事務,那ObjectModel和Dao的數據庫操作一定要在此事務中;所以oleDb對象創建實例后,是會傳遞到后面的ObjectModel和Dao的;
如何實現oledb的傳遞了,就是在創建對象的NewObject()和NewDao()方法中處理的;所以為什么說在框架中對象的創建一定得用這兩個方法,絕對不能用new關鍵字創建;