1. 三層架構(不是本文重點,簡單介紹一下)
1) 用戶界面層(UI層),主要職責是提供可用的功能給用戶。用戶界面層(一般是XXXView),主要職責是響應(識別)用戶的請求操作(包括UI層返回及用戶的輸入數據) ,由請求操作調用相應的XXXController(或XXXManager)完成相應的業務邏輯;在這一層也還要對一些錯誤信息進行判斷和處理(錯誤信息是和數據庫沒有關係的。
2) 業務邏輯模塊層(一般由XXXController或 XXXManager類模塊組成),主要職責執行業務邏輯的計算,業務邏輯可以很簡單,簡單到只是簡單的調用XXXDAO的一個save操作。也可以很複雜,複雜到要用到多個CXXXDAO(或者說是多個table表)才能完成。即使用到了多個table表,業務邏輯層也不應該直接用sql操作 table表,那樣會帶來維護的複雜度,這時應該新建一個能操作相關table的XXXDAO新類,讓這個新類來完成業務邏輯要求的複雜的sql(或是複雜的存儲過程)。
3) DAO層(即數據訪問層)。主要職責完成各種各樣的存儲、查詢操作。主要作用是向上提供不同的存儲接口及查詢。如saveUserAndXxx();getUserAdnXxx()等等;提供數據的簡化訪問的同時也屏蔽了數據庫的存在,業務邏輯層不需要知道DAO層到底在用那個數據庫,或者根本就沒用。
理論知識說破天也就那樣,關鍵還是實踐,能把三層通過幾個項目實踐出來,從項目中不斷認識三層架構,仔細分析架構的好處纔是硬道理。
2. 事務(不是重點,簡單介紹一下)
事務(Transaction)是訪問並可能更新數據庫中各種數據項的一個程序執行單元(unit)。事務有四個屬性(原子性、一致性、隔離性、持久性)。
原子性主要描述的是一個事務的不可分割性,要麼都做要麼都不做。
一致性主要描述的是數據庫從一個一致性的狀態變到了另外一個一致性的狀態
隔離性主要描述的是事務執行的不被幹擾性
持久性主要描述的是事務一旦提交那麼數據庫的改變是永久性的不會被其他操作影響。
3、事務在三層架構中的運用(這是重點)
網上也有許多人在議論,事務應該在三層架構中哪一層設計比較好。其實我個人認爲沒有好與不好之分,只有哪個在特定環境下比較合適。
我現在做着的這個項目中出現了這個問題,正好在這裏嘮叨嘮叨。
首先說,數據事務肯定是在數據層,因爲如果在數據層不寫事務的話,那麼涉及數據的回滾,就會出現問題。然而事務在邏輯層使用也是不可避免的,事務管理就是業務的範疇,所以事務處理應該在邏輯層中實現。(這是自己的理解,歡迎大家斧正)
通過這樣的分析,就很容易知道我們要在邏輯層中事務開啓、提交、異常回滾和關閉。而事務的實現其實是在數據層實現的。
我相信肯定會有人認爲事務只寫在數據層就行,在業務邏輯層用sqlConnection感覺很不正常,其實這個觀點網上也有很多人認同,也有很多人是這樣做的。那我我還是說沒有正確與否,只有合適不合適,這要根據項目來確定。首先說在數據層寫事務是沒有問題,一樣能夠實現。那麼兩者有什麼區別,這是我們要明白的,也是我們選擇在哪裏寫事務的一個主要因素。
就像我剛纔說的,我選擇在業務邏輯層這樣我只要在邏輯層的事務操作中只做一次連接即可,而在數據層那麼我們可能就不僅僅是一次了吧!如果我們在一個多發性的操作中,可能我們就會應爲這樣一個連接沒有處理好就會導致程序崩潰。
還是那句話,沒有唯一的正確標準,只有在某種情況下適合的方案。
如果我們在邏輯層使用事務的話,那麼我們勢必會在業務邏輯層中開啓數據庫連接,而我們的數據庫連接一般都會寫在一個數據庫助手類(sqlHelper)中,這樣我們就有可能面臨業務邏輯層中創建數據庫助手類對象了。這樣做耦合度會變得很高。那麼我們可以通過事務獨立成立來降低這個耦合度。具體的事務類中主要實現數據庫連接,開啓事務處理和關閉連接等操作。
具體事務類代碼實現:
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Collections;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using StuEduAdministrationModel;
using SqlHelp;
namespace StuEduAdministrationDAL
{
public class TransactionManager
{
/// <summary>
/// 定義連接
/// </summary>
SqlConnection sqlConnection = new SqlConnection();
/// <summary>
/// 定義事務
/// </summary>
SqlTransaction sqlTransaction;
/// <summary>
/// 得到連接
/// </summary>
/// <returns></returns>
public SqlConnection GetConnection()
{
sqlConnection.ConnectionString = ConfigurationManager.ConnectionStrings["StuEduAdministration"].ConnectionString;
sqlConnection.Open();
return sqlConnection;
}
/// <summary>
/// 開啓事務
/// </summary>
public SqlTransaction TranBegin()
{
sqlTransaction = sqlConnection.BeginTransaction();
return sqlTransaction;
}
/// <summary>
/// 提交事務
/// </summary>
public void TranCommit()
{
sqlTransaction.Commit();
}
/// <summary>
/// 回滾事務
/// </summary>
public void TranRollback()
{
sqlTransaction.Rollback();
}
/// <summary>
/// 關閉連接
/// </summary>
public void Close()
{
sqlConnection.Close();
}
}
}
我們在sqlHelper的代碼就要這樣實現事務處理即可:
Public Overloads Function ExecuteNonQuery(ByVal SqlString As String, ByVal SqlParameters As SqlParameterCollection, ByVal objTransaction As SqlTransaction) As Integer
Dim intTemp As Integer
Dim cmd As SqlCommand
Try
Me.InitializeConnection() '初始化連接
cmd = New SqlCommand(SqlString, m_objConnection)
cmd.Transaction = objTransaction '事物處理
'將參數集合中的參數添加到cmd中
Call Me.AddSqlParametersToCommand(cmd, SqlParameters)
intTemp = cmd.ExecuteNonQuery()
Me.FinalizeConnection() '關閉連接(與維護連接對象的方式有關)
Catch ex As Exception
Throw ex
End Try
Return intTemp
End Function
那麼我們在數據層只需實例化sqlHelper類並傳遞從邏輯層傳過來的事務類還有數據庫連接參數即可:
private SqlConnection sqlCon;
private SqlTransaction sqlTra;
/// <summary>
/// 數據庫助手類型
///
/// </summary>
private SqlHelper sqlHelper;
public SubCategoryScoreDAO(SqlConnection sqlCon,SqlTransaction sqlTra)
{
this.sqlCon = sqlCon;
this.sqlTra = sqlTra;
sqlHelper = new SqlHelper(sqlCon);
}
public SubCategoryScoreDAO()
{
sqlHelper = new SqlHelper("StuEduAdministration");
}
在邏輯層中進行事務開啓和關閉等操作:
<font xmlns="http://www.w3.org/1999/xhtml"></font><pre name="code" class="csharp"><font xmlns="http://www.w3.org/1999/xhtml"> /// <summary>
/// 根據學號學年刪除子類別參評得分信息
/// </summary>
/// <param name="strAcademicYear">學年</param>
/// <param name="strStuNo">學號</param>
public bool DeleteByStuNoAcademicYear(string strAcademicYear, string strStuNo)
{
SqlConnection sqlCon = transactionManager.GetConnection(); //獲得連接
SqlTransaction sqlTra = transactionManager.TranBegin(); //事務開啓
try
{
objCategoryScoreDAO = new CategoryScoreDAO(sqlCon, sqlTra);
objEvaluateInfoDAO = new EvaluateInfoDAO(sqlCon, sqlTra);
objSubCategoryScoreDAO = new SubCategoryScoreDAO(sqlCon, sqlTra);
objSubCategoryScoreDAO.DeleteByStuNoAcademicYear(strAcademicYear, strStuNo);
objCategoryScoreDAO.DeleteBystuNoAcademicYear(strAcademicYear, strStuNo);
objEvaluateInfoDAO.DeleteByStuNoAcademicYear(strAcademicYear, strStuNo);
transactionManager.TranCommit(); //事務提交
return true;
}
catch (Exception)
{
transactionManager.TranRollback(); //事務回滾
throw;
}
finally
{
transactionManager.Close(); //事務關閉
}
}</font>
通過這樣的設計,就可以在邏輯層實現事務管理,才數據層進行事務實現。
最後還是那句話,沒有最好的,只有最合適的。