數據庫三層模型


1,什么是三層?

2,為什么使用三層?

3,三層與以往使用的兩層相比有什么不同?它的優勢在哪里?

4,如何學好三層?如何應用三層?

先了解:

1、什么是三層

UI(表現層):主要是指用戶交互的界面。用於接收用戶輸入的數據和顯示處理后用戶需要的數據。

 

BLL:(業務邏輯層):UI層和DAL層之間的橋梁實現業務邏輯。業務邏輯具體包含:驗證、計算、業務規則等等。

 

DAL:(數據訪問層):與數據庫打交道。主要實現對數據的增、刪、改、查。將存儲在數據庫中的數據提交給業務層,同時將業務層處理的數據保存到數據庫。(當然這些操作都是基於UI層的。用戶的需求反映給界面(UI),UI反映給BLLBLL反映給DALDAL進行數據的操作,操作后再一一返回,直到將用戶所需數據反饋給用戶)

 

每一層都各負其責,那么該如何將三層聯系起來呢?

1>單項引用(見下圖)

2>這時候實體層(Entity)來了。(注:當然,實體層的作用不止這些)

 

Entity(實體層):它不屬於三層中的任何一層,但是它是必不可少的一層。

Entity在三層架構中的作用(也叫模型層model):

1,實現面向對象思想中的"封裝";

2,貫穿於三層,在三層之間傳遞數據;

注:確切的說實體層貫穿於三層之間,來連接三層)

3,對於初學者來說,可以這樣理解:每張數據表對應一個實體,即每個數據表中的字段對應實體中的屬性(注:當然,事實上不是這樣。為什么?1>,可能我們需要的實體在數據表對應的實體中並不存在;2>,我們完全可以將所有數據表中的所有字段都放在一個實體里)

4,每一層(UI>BLL>DAL)之間的數據傳遞(單向)是靠變量或實體作為參數來傳遞的,這樣就構造了三層之間的聯系,完成了功能的實現。

但是對於大量的數據來說,用變量做參數有些復雜,因為參數量太多,容易搞混。比如:我要把員工信息傳遞到下層,信息包括:員工號、姓名、年齡、性別、工資....用變量做參數的話,那么我們的方法中的參數就會很多,極有可能在使用時,將參數匹配搞混。這時候,如果用實體做參數,就會很方便,不用考慮參數匹配的問題,用到實體中哪個屬性拿來直接用就可以,很方便。這樣做也提高了效率。(字段轉成對象的屬性)

 

注:這里為什么說可以暫時理解為每個數據表對應一個實體??答:大家都知道,我們做系統的目的,是為用戶提供服務,用戶可不關心你的系統后台是怎么工作的,用戶只關心軟件是不是好用,界面是不是符合自己心意。用戶在界面上輕松的增、刪、改、查,那么數據庫中也要有相應的增、刪、改、查,而增刪改查具體操作對象就是數據庫中的數據,說白了就是表中的字段。所以,將每個數據表作為一個實體類,實體類封裝的屬性對應到表中的字段,這樣的話,實體在貫穿於三層之間時,就可以實現增刪改查數據了)

思想來源於生活:

服務員:只管接待客人;

廚師:只管做客人點的菜;

采購員:只管按客人點菜的要求采購食材;

他們各負其職,服務員不用了解廚師如何做菜,不用了解采購員如何采購食材;廚師不用知道服務員接待了哪位客人,不用知道采購員如何采購食材;同樣,采購員不用知道服務員接待了哪位客人,不用知道廚師如何做菜。

他們三者是如何聯系的?

比如:廚師會做:炒茄子、炒雞蛋、炒面——此時構建三個方法( cookEggplant()cookEgg()cookNoodle())

 

顧客直接和服務員打交道,顧客和服務員(UI層)說:我要一個炒茄子,而服務員不負責炒茄子,她就把請求往上遞交,傳遞給廚師(BLL層),廚師需要茄子,就把請求往上遞交,傳遞給采購員(DAL層),采購員從倉庫里取來茄子傳回給廚師,廚師響應cookEggplant()方法,做好炒茄子后,又傳回給服務員,服務員把茄子呈現給顧客。

這樣就完成了一個完整的操作。

 

在此過程中,茄子作為參數在三層中傳遞,如果顧客點炒雞蛋,則雞蛋作為參數(這是變量做參數)。如果,用戶增加需求,我們還得在方法中添加參數,一個方法添加一個,一個方法設計到三層;何況實際中並不止設計到一個方法的更改。所以,為了解決這個問題,我們可以把茄子、雞蛋、面條作為屬性定義到顧客實體中,一旦顧客增加了炒雞蛋需求,直接把雞蛋屬性拿出來用即可,不用再去考慮去每層的方法中添加參數了,更不用考慮參數的匹配問題。

 

這樣講,不知道大家是不是可以明白。(待會實例解釋吧)

2,為什么使用三層?

使用三層架構的目的:解耦!!!

同樣拿上面飯店的例子來講:

1)服務員(UI層)請假——另找服務員;廚師(BLL層)辭職——招聘另一個廚師;采購員(DAL)辭職——招聘另一個采購員;

2)顧客反映:1>你們店服務態度不好——服務員的問題。開除服務員;

2>你們店菜里有蟲子——廚師的問題。換廚師;

 

任何一層發生變化都不會影響到另外一層!!!

3,與兩層的區別??

兩層:

 

(當任何一個地方發生變化時,都需要重新開發整個系統。“多層”放在一層,分工不明確耦合度高——難以適應需求變化,可維護性低、可擴展性低)

 

三層:

 

 

(發生在哪一層的變化,只需更改該層,不需要更改整個系統。層次清晰,分工明確,每層之間耦合度低——提高了效率,適應需求變化,可維護性高,可擴展性高)

 

綜上:三層架構的

優勢:1,結構清晰、耦合度低,2,可維護性高,可擴展性高;3,利於開發任務同步進行;容易適應需求變化

 

劣勢:1、降低了系統的性能。這是不言而喻的。如果不采用分層式結構,很多業務可以直接造訪數據庫,以此獲取相應的數據,如今卻必須通過中間層來完成。

2、有時會導致級聯的修改。這種修改尤其體現在自上而下的方向。如果在表示層中需要增加一個功能,為保證其設計符合分層式結構,可能需要在相應的業務邏輯層和數據訪問層中都增加相應的代碼

3、增加了代碼量,增加了工作量

4,三層的具體表現形式??

models:定義用戶實體模型

 

namespace WeatherStationManager.Models
{
   public class User
    {
        int userId;

        public int UserId
        {
            get { return userId; }
            set { userId = value; }
        }
        string userName;

        public string UserName
        {
            get { return userName; }
            set { userName = value; }
        }
        string userPassWord;

        public string UserPassWord
        {
            get { return userPassWord; }
            set { userPassWord = value; }
        }
        bool isDel;

        public bool IsDel
        {
            get { return isDel; }
            set { isDel = value; }
        }
        DateTime addTime;

        public DateTime AddTime
        {
            get { return addTime; }
            set { addTime = value; }
        }
    }
}

 

DAL:唯一直接與數據庫交互的層

1創建數據庫訪問公共類,負責完成 數據庫訪問對象的建立,數據庫連接建立,sql命令的執行方法

namespace WeatherStationManager.DAL
{
    public class DBHelpSQL
    {

        private SqlConnection conn;//創建一個sql數據庫打開的連接,參數是連接字符串 /// <summary>
        /// 數據庫連接對象屬性
        /// </summary>
        /// 
       
        public SqlConnection Conn//封裝為屬性
        {
            get
            {
              // string connectionString = ConfigurationManager.ConnectionStrings["ConnString"].ToString();
               string connectionString = "server=.;database=WeatherStation;uid=sa;pwd=sasasa";
                if (conn == null)
                {
                    conn = new SqlConnection(connectionString);
                    conn.Open();
                }
                else if (conn.State == System.Data.ConnectionState.Closed)
                {
                    conn.Open();
                }
                else if (conn.State == System.Data.ConnectionState.Broken)
                {
                    conn.Close();
                    conn.Open();
                }
                return conn;
            }
        }
        /// <summary>
        /// 關閉數據庫連接
        /// </summary>
        public void CloseDB()
        {
            if (conn.State == System.Data.ConnectionState.Open || conn.State == System.Data.ConnectionState.Broken)
            {
                conn.Close();
            }
        }
// 創建sqlDataReader對象(每次向數據庫只讀一條)必須調用 SqlCommand 對象的 ExecuteReader 方法,read()方法讀取 ,每次讀一條數據 ,查詢 語句
public SqlDataReader ExecuteReader(string sql, params SqlParameter[] parmaeters) { // SqlConnection conn = new SqlConnection(connctionString); SqlCommand cmd = new SqlCommand(sql, Conn); if (parmaeters != null) { cmd.Parameters.AddRange(parmaeters); } // conn.Open(); return cmd.ExecuteReader(CommandBehavior.CloseConnection);//返回sqlDataReader對象 } /// <summary> /// 執行sql語句 返回數據表 ,查詢 /// </summary> /// <param name="safeSql">sql語句</param> /// <returns>數據表</returns> public DataTable GetDataTable(string safeSql) { //SqlConnection conn = new SqlConnection(connctionString); DataSet ds = new DataSet(); SqlCommand cmd = new SqlCommand(safeSql,Conn);
        //SqlDataAdapter是 DataSet和 SQL Server之間的橋接 SqlDataAdapter da
= new SqlDataAdapter(cmd);//SqlCommand是sql命令,執行后通過sqlDataAdapter返回填入DataSet, da.Fill(ds); return ds.Tables[0];//將dataset的第一張表返回也就是datatable ,因為dataset是數據表集合 ,相當於內存中的數據庫 } /// <summary> /// 執行sql語句 返回數據表 , 查詢語句 /// </summary> /// <param name="sql">sql語句</param> /// <param name="values">sql參數列表</param> /// <returns>數據表</returns> public DataTable GetDataTable(string sql, params SqlParameter[] values) { DataSet ds = new DataSet(); SqlCommand cmd = new SqlCommand(sql, Conn); cmd.Parameters.AddRange(values); SqlDataAdapter da = new SqlDataAdapter(cmd); da.Fill(ds); cmd.Parameters.Clear(); return ds.Tables[0]; } /// <summary> /// 執行Command , 執行增刪改 /// </summary> /// <param name="sql">sql語句</param> /// <param name="values">sql參數數組</param> /// <returns></returns> public int ExecuteCommand(string sql, params SqlParameter[] values) { SqlCommand cmd = new SqlCommand(sql, Conn); cmd.Parameters.AddRange(values); int result = cmd.ExecuteNonQuery(); //執行(增刪改)的方法,返回執行命令所影響的行數(return int類型) cmd.Parameters.Clear(); return result; } /// <summary> /// 執行帶sql參數的語句 /// </summary> /// <param name="sql">sql語句</param> /// <param name="values">sql參數列表</param> /// <returns></returns> public int GetScalar(string sql, params SqlParameter[] values) { object obj = null; try { SqlCommand cmd = new SqlCommand(sql, Conn); cmd.Parameters.AddRange(values); obj = cmd.ExecuteScalar();//獲得查詢到的結果集的第一個單元格的值,為obj類型 cmd.Parameters.Clear(); } catch (Exception ex) { throw ex; } finally { CloseDB(); } if (obj == null) return 0; else return Convert.ToInt32(obj); } } }

 2增刪改查

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using WeatherStationManager.Models;
using System.Data.SqlClient;
using System.Data;
using System.Reflection;

namespace WeatherStationManager.DAL
{
    public class UserServices
    {
        DBHelpSQL dbHelper = null;  //DAL層需要用到數據庫訪問公共類 ,這里建立dbhelp對象 public UserServices()
        {
            dbHelper = new DBHelpSQL();
        }
  
        #region 修改
        /// <summary>
        /// 修改學員信息
        /// </summary>
        /// <param name="model">學員實體</param>
        /// <returns></returns>
        public bool ModifyStudent(Models.User model)
        {
            StringBuilder sbSql = new StringBuilder("update [User] set ");// user 與系統關鍵字沖突 用[] 括起來
            Type modeType = model.GetType();//獲得對象的類型
            PropertyInfo[] pros = modeType.GetProperties(); // 得到類型的所有公共屬性
            List<SqlParameter> paras = new List<SqlParameter>();
            foreach (PropertyInfo pi in pros)   //反射獲得屬性和屬性的值
            {
                if (!pi.Name.Equals("UserId") && !pi.Name.Contains("AddTime"))//如果不是主鍵則追加sql字符串
                {
                    if (pi.GetValue(model, null) != null && !pi.GetValue(model, null).ToString().Equals(""))//判斷屬性值是否為空
                    {
                        sbSql.Append(pi.Name + "=@" + pi.Name + ",");//SID=@SID
                        paras.Add(new SqlParameter("@" + pi.Name, pi.GetValue(model, null)));
                    }
                }
            }
            string strSql = sbSql.ToString().Trim(','); //去掉兩邊的,
            strSql += " where UserId=@UserId";
            paras.Add(new SqlParameter("@UserId", model.UserId));
            return dbHelper.ExecuteCommand(strSql, paras.ToArray()) > 0;   //執行語句
        }
        #endregion
        #region 修改密碼
        /// <summary>
        /// 修改學員信息
        /// </summary>
        /// <param name="model">學員實體</param>
        /// <returns></returns>
        public bool ModifyPwd(Models.User model)
        {
            string Sql = "update [User] set UserPassWord=@userpwd where UserName=@username ";
            Type modeType = model.GetType();
            SqlParameter[] parms = {
                                       new SqlParameter("@username",model.UserName),
                                       new SqlParameter("@userpwd",model.UserPassWord) 
                                   };
            return dbHelper.ExecuteCommand(Sql, parms) > 0;
        }
        #endregion

        #region 根據查詢條件 返回 學生實體 列表  ,查詢 
        /// <summary>
        /// 根據查詢條件 返回 學生實體 列表 where SName=@SName and SPwd=@SPwd and SPwd=@SPwd
        /// </summary>
        /// <returns></returns>
        public List<Models.User> QueryListByCondition(SqlParameter[] paras)
        {
            Models.User model = null;
            StringBuilder sbSql = new StringBuilder("select * from [User]  where IsDel=0 ");
            if (paras != null)//如果參數數組不為空,則循環生成sql的條件語句,追加查詢條件
            {
                for (int i = 0; i < paras.Length; i++)//循環所有參數(如: and PWD=@SPWD)
                {
                    SqlParameter p = paras[i];
                    sbSql.Append(" and ");//第二個參數開始 在前面加 and
                    sbSql.Append(p.ParameterName.Substring(1));//獲得參數所對應的列名
                    sbSql.Append("=" + p.ParameterName);
                }
            }
            //讀取數據庫 返回查詢到的數據表(如果參數為null,則直接執行sql語句,否則帶參數執行sql語句)
            DataTable dt = (paras == null) ? dbHelper.GetDataTable(sbSql.ToString()) : dbHelper.GetDataTable(sbSql.ToString(), paras);
            //准備要返回的泛型集合
            List<Models.User> list = null;
            if (dt.Rows.Count > 0)//如果查詢到的行數大於0
            {
                list = new List<Models.User>();//實例化集合對象
                foreach (DataRow dr in dt.Rows)//循環臨時表的行記錄
                {
                    model = new Models.User();//每循環一行生成一個實體
                    SetDr2Model(dr, model);//將行數據填入實體對應的屬性
                    list.Add(model);//將實體對象加入集合
                }
            }
            return list;
        }
        #endregion
        #region 新增
        public int ADD(Models.User MOD)
        {
            string sql = "insert into [User] (UserName,UserPassWord) values (@UserName,@UserPassWord);select @@identity";
              //insert into 后獲得自動插入的id(select @@identity) SqlParameter[] pars
={ new SqlParameter("@UserName",MOD.UserName), new SqlParameter("@UserPassWord",MOD.UserPassWord) }; int res = dbHelper.GetScalar(sql, pars); //獲得插入id return res; } #endregion #region 根據id刪除指定行 public int DelById(string UserId) { int res = dbHelper.ExecuteCommand("delete [User] where UserId=@UserId", new SqlParameter("@UserId", UserId)); return res; } #endregion #region 返回單個學生實體 public User GetUserByCondition(string UserName, string UserPwd) { string sql = "select * from [User] where UserName=@username and UserPassWord=@userpwd"; SqlParameter[] parms = { new SqlParameter("@username",UserName), new SqlParameter("@userpwd",UserPwd) }; SqlDataReader dr = dbHelper.ExecuteReader(sql, parms); User oneUser = null; //轉換成實體對象 if (dr.Read()) { oneUser = new User(); oneUser.UserId = Convert.ToInt32(dr["UserId"]); oneUser.UserName = dr["UserName"].ToString(); oneUser.UserPassWord = dr["UserPassWord"].ToString(); oneUser.IsDel = Convert.ToBoolean(dr["IsDel"]); oneUser.AddTime = Convert.ToDateTime(dr["AddTime"]); } dr.Close();// dataReader 用完一定要關閉 return oneUser; } #endregion #region 將 數據行 轉換 成 實體對象 /// <summary> /// 將 數據行 轉換 成 實體對象,用於將dataset 的每一行轉成實體對象 /// </summary> /// <param name="dr">數據行</param> /// <param name="model">實體對象</param> public void SetDr2Model(DataRow dr, Models.User model) { if (dr["UserId"].ToString() != "") { model.UserId = int.Parse(dr["UserId"].ToString()); } if (dr["UserName"].ToString() != "") { model.UserName = dr["UserName"].ToString(); } model.UserPassWord = dr["UserPassWord"].ToString(); if (dr["IsDel"].ToString() != "") { model.IsDel = bool.Parse(dr["IsDel"].ToString()); } if (dr["AddTime"].ToString() != "") { model.AddTime = DateTime.Parse(dr["AddTime"].ToString()); } } #endregion } }

 BLL:UI層與DAL層交互的橋梁

 

namespace WeatherStationManager.BLL
{
    public class UserManager
    {
        UserServices userServices = new UserServices();  //BLL層只需要與DAL層交互 ,這里建立 DAL層的對象 /*    public int AddUser(User addUser)
        {
            if (!this.CheckExists(addUser.UserName,addUser.UserPassWord))
            {
                //返回false表示不存在,則新增
                return userServices.AddUser(addUser);
            }
            else
            {
                throw new Exception("用戶名已存在!");
            }
        }*/
        #region 修改密碼
        /// <summary>
        /// 修改學員信息
        /// </summary>
        /// <param name="model">學員實體</param>
        /// <returns></returns>
        public bool ModifyPwd(Models.User model)
        {
           return userServices.ModifyPwd(model);
        }
        #endregion
        #region del
        public bool DelById(string UserId)
        {
            int res = userServices.DelById(UserId);
            bool resBool = false;
            if (res > 0)
                resBool = true;
            else resBool = false;
            return resBool;
        }  
        #endregion
        #region add
        public bool ADD(Models.User mod)
        {
            int newId = userServices.ADD(mod);
            return newId > 0;
        } 
        #endregion
        #region updata
        /// <summary>
        /// 修改學員信息
        /// </summary>
        /// <param name="model">學員實體</param>
        /// <returns></returns>
        public bool ModifyStudent(Models.User model)
        {
            return userServices.ModifyStudent(model);
        }
        #endregion
        #region 獲得所有user
        /// <summary>
        /// 獲得所有列表
        /// </summary>
        /// <returns></returns>
        public List<Models.User> GetAllUsers()
        {
            return userServices.QueryListByCondition(null);
        }
        #endregion
        #region 是否存在

        /// <summary>
        /// 檢測userName在數據庫中是否存在,如果存在返回true,否則返回false
        /// </summary>
        /// <param name="typeName"></param>
        /// <returns></returns>
        public bool CheckExists(string userName, string userPwd)
        {
            User oneUser = userServices.GetUserByCondition(userName, userPwd);
            if (oneUser != null)
            {
                return true;
            }
            else
            {
                return false;
            }
        } 
        #endregion
    }
}

 

我這里由於業務比較簡單,其實必要的話 ,業務邏輯層是會加上一些復雜的邏輯判斷的。

這樣的分層  數據層 只是訪問 數據庫 , 業務層做一些邏輯判斷 , UI層負責顯示 與輸入 

UI層 : 用戶交互 

 1                 string uName = tb1.Text;  //獲取用戶輸入
 2                 string uPwd = tb2.Text;
 3                 string apwd = tb3.Text;
 4                 bool res = false;
 5                 UserManager userManager = new UserManager();  //由於UI層只需要與BLL層交互 ,這里建立BLL對象  6                 Models.User model=new Models.User();
 7                 if (uPwd!=apwd)
 8                 {
 9                     Response.Write("<script>alert('兩次輸入的密碼不一致!')</script>");
10                 }
11                 else
12                 {
13                     model.UserName = uName;
14                     model.UserPassWord = uPwd;
15                     res = userManager.ModifyPwd(model);
16                     Response.Write("<script>alert('修改成功!')</script>");
17                 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM