在很多情況下,事務是個很有用的東西,可以把一系列的操作組合成一個原子粒度的操作,一旦組合中某個地方出錯,可以整個干凈的進行滾回,不會留下臟數據;除此之外,事務還能提高批量操作的效率,如在本地SQLite數據庫里面,批量插入1萬條數據,那么使用事務和沒有使用事務,速度上至少差別幾十到上百倍的差異。既然事務有完整性和速度性的差異,因此,基于上述原因,我們在很多情況下最好使用事務進行操作。本文主要介紹在開發框架中如何整合事務的操作,并介紹在各個分層中的事務使用案例。
由于我介紹的相關框架,主要是采用了微軟的EnterPRise Library的數據庫訪問模塊,因此它能夠很好抽象各種數據庫的事務,以適應各種不同數據庫的事務處理。使用微軟的Enterprise Library模塊,可以很好支持SQLSever、Oracle、MySQL、access、SQLite等數據庫。
1.1 數據訪問層的事務接口定義
由于使用事務操作,因此在底層的模塊里面,也就是這里的數據訪問層,一般需要一個事務對象的參數。如下代碼是一個數據訪問層的接口定義。
/// <summary> /// 數據訪問層的接口 /// </summary> public interface IBaseDAL<T> where T : BaseEntity { /// <summary> /// 插入指定對象到數據庫中 /// </summary> /// <param name="obj">指定的對象</param> /// <param name="trans">事務對象</param> /// <returns>執行成功返回True</returns> bool Insert(T obj, DbTransaction trans = null); /// <summary> /// 根據指定對象的ID,從數據庫中刪除指定對象 /// </summary> /// <param name="key">指定對象的ID</param> /// <param name="trans">事務對象</param> /// <returns>執行成功返回<c>true</c>,否則為<c>false</c>。</returns> bool Delete(object key, DbTransaction trans = null); /// <summary> /// 更新對象屬性到數據庫中 /// </summary> /// <param name="obj">指定的對象</param> /// <param name="primaryKeyValue">主鍵的值</param> /// <param name="trans">事務對象</param> /// <returns>執行成功返回<c>true</c>,否則為<c>false</c>。</returns> bool Update(T obj, object primaryKeyValue, DbTransaction trans = null); /// <summary> /// 查詢數據庫,檢查是否存在指定ID的對象 /// </summary> /// <param name="key">對象的ID值</param> /// <param name="trans">事務對象</param> /// <returns>存在則返回指定的對象,否則返回Null</returns> T FindByID(object key, DbTransaction trans = null); .....................//其他操作 }從上面的代碼上,我們可以看到,里面的增刪改查等操作,最后都帶一個trans的事務對象參數,這個參數默認為null,也就是可選參數的做法,這個方法就提供了兩個重載的方法供我們使用。
看到這里,可能有些人提出疑問,為什么查找方法也傳入事務對象,這個因為事務是一個排斥性操作,一旦啟動了事務,可能這個表的其他操作會被鎖定,但在這個事務內操作確實可以允許的,如果你使用SQLite這種單機版的數據庫,在一個地方采用事務操作一個表,在事務內部接著不使用事務進行表的任何操作將不會被允許,數據庫提示出錯信息的。
基于這個原因,所有表操作的接口,都應該提供事務性的操作接口,也就是提供一個事務性的對象參數。在接口的實現里面,判斷事務對象是否為空,然后進行相應的處理即可。
由于在IBaseDAL里面已經定義了很多事務性的接口,因此數據訪問層的基類里面也已經實現了很多相關的基礎操作。
因此數據訪問層DAL層里面,如自己定義的實現函數,調用這些基礎函數進行處理就可以了。自定義函數對事務的操作處理,代碼如下所示。
/// <summary> /// 調整客戶的組別 /// </summary> /// <param name="customerId">客戶ID</param> /// <param name="groupIdList">客戶分組Id集合</param> /// <returns></returns> public bool ModifyCustomerGroup(string customerId, List<string> groupIdList) { bool result = false; DbTransaction trans = base.CreateTransaction(); if (trans != null) { string sql = string.Format("Delete from T_CRM_CustomerGroup_Customer where Customer_ID='{0}' ", customerId); base.SqlExecute(sql, trans); foreach (string groupId in groupIdList) { sql = string.Format("Insert into T_CRM_CustomerGroup_Customer(Customer_ID,CustomerGroup_ID) values('{0}', '{1}') ", customerId, groupId); base.SqlExecute(sql, trans); } try { trans.Commit(); result = true; } catch { trans.Rollback(); throw; } } return result; }業務邏輯層BLL層是對數據訪問層的更高一層的封裝,它也相應提供相應的事務對象接口,以方便外部的調用。
它的業務類里面,自定義函數對事務的調用操作如下所示。
/// <summary> /// 把報價單轉換為銷售訂單 /// </summary> /// <param name="quotationNo">報價單編號</param> /// <returns></returns> public bool ConvertToOrder(string orderNo, int userId) { bool result = false; DbTransaction trans = baseDal.CreateTransaction(); if (trans != null) { SellInfo sellInfo = ConvertSellInfo(orderNo, userId, trans); List<OrderDetailInfo> detailList = new List<OrderDetailInfo>(); if (sellInfo != null) { detailList = ConvertOrderDetal(sellInfo, orderNo, trans); } bool success = BLLFactory<Sell>.Instance.Insert(sellInfo, trans); if (success) { foreach (OrderDetailInfo info in detailList) { BLLFactory<OrderDetail>.Instance.Insert(info, trans); } } try { trans.Commit(); result = true; } catch { trans.Rollback(); throw;//重新拋出異常 } } return result; }另一例子如下所示。
/// <summary> /// 刪除報價單及明細信息 /// </summary> /// <param name="id"></param> /// <returns></returns> public bool DeleteQuotationRelated(string id) { bool result = false; DbTransaction trans = CreateTransaction(); if (trans != null) { QuotationInfo info = baseDal.FindByID(id, trans); if (info != null) { List<QuotationDetailInfo> detailList = BLLFactory<QuotationDetail>.Instance.FindByOrderNo(info.HandNo, trans); foreach (QuotationDetailInfo detailInfo in detailList) { BLLFactory<QuotationDetail>.Instance.Delete(detailInfo.ID, trans); } //最后刪除主表訂單數據 baseDal.Delete(id, trans); try { trans.Commit(); result = true; } catch (Exception ex) { trans.Rollback(); LogTextHelper.Error(ex); throw; } } } return result; } }由于Winform界面層,直接調用BLL層的相應接口,進行數據的操作的,因此我們也可以在界面層創建相應的事務對象,然后在界面層操作事務。
界面層調用事務處理,操作代碼如下所示。
using (DbTransaction trans = BLLFactory<Function>.Instance.CreateTransaction()) { try { if (trans != null) { bool sucess = BLLFactory<Function>.Instance.Insert(mainInfo, trans); if (sucess) { FunctionInfo subInfo = null; int sortCodeIndex = 1; #region 子功能操作 if (chkAdd.Checked) { subInfo = CreateSubFunction(mainInfo); subInfo.SortCode = (sortCodeIndex++).ToString("D2"); subInfo.ControlID = string.Format("{0}/Add", mainInfo.ControlID); subInfo.Name = string.Format("添加{0}", mainInfo.Name); BLLFactory<Function>.Instance.Insert(subInfo, trans); } if (chkDelete.Checked) { subInfo = CreateSubFunction(mainInfo); subInfo.SortCode = (sortCodeIndex++).ToStr
新聞熱點
疑難解答