zoukankan      html  css  js  c++  java
  • 我也来写:数据库访问类DBHelper(转)

    一、前言

      相信许多人都百度过:“.net 数据库访问类”。然后就出来一大堆SqlHelper。我也用过这些SqlHelper,也自己写过,一堆静态方法,开始使用起来感觉很不错,它们也确实在很多时候可以很好的工作。ADO.NET已经封装很好了,我们很容易就可以实现自己的数据库访问类。

      很久前,忘记在哪里看到过了,有一个朋友写了一篇【如何做一个好用的数据库访问类】(有兴趣的朋友仍然可以搜索到),这篇文章确实写得很好,作者很详细的讲解了如何设计一个好的数据库访问类;所谓“好“是指:轻量、易用、通用、高效。

      其实代码是很久前就实现了,只是现在才总结记录,希望可以分享一下学习的过程。ok,在开始前先来看几个ADO.NET常见的面试题:

    1. ADO.NET 5个核心对象是哪5个?

    2. 与ADO.NET 相关对象中,哪些可以用于数据绑定?

    3. DataSet与DataReader有什么区别?分别适用在什么情况?

    二、需求

      这是一个简单的、基于ADO.NET的数据库访问类,它最起码要具备以下特点:  

    1. 支持多种数据库

      搞.net的视乎有一个固定的思维:数据库就是用sql server。额,只是很多用sql server,但不是全部,也有很多用 my sql 等的。我们并不能限制一定用什么数据库。

    2. 支持多个数据库

      有时候我们的应用程序会用到多个数据库,并且这些数据库还不是部署在同一台服务器上的。

    3. 简单

      满足常见的操作。

    4. 可扩展

      可以随时增加新的方法;对于具体的数据源,也可以有特有的操作。

    三、主要说明

    3.1 使用DbProviderFactory

      既然要支持多种数据库,那么我们之前常写的SqlConnection、SqlCommand 就都不能用了,因为它们是针对sql server 数据源的。如果换成 my sql 就是 MySqlConnection, Oracle 就是 OracleConnection 了。

      既然有那么多种Connection,很多朋友可能会想到通过设计模式来处理,例如定义一个父类(或接口),然后各种类型的数据库继承它,然后再通过一个工厂,来创建所需要的对象;以后要增加哪种类型的数据库就再增加一个对应的类就可以了。大概是像下面这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public abstract class DBHelper
    {
        public abstract void Open(string key){}
        public abstract int ExecuteNonQuery() { }
        public abstract object ExecuteScalar() { }
        public abstract DataSet GetDataSet() { }
    }
     
    public class SqlHelper : DBHelper
    {
        const char _prefix = '@';
        //实现抽象方法...
    }
     
    public class MySqlHelper : DBHelper
    {
        const char _prefix = "@";
        //实现抽象方法...
    }
     
    public class OracleHelper : DBHelper
    {
        const char _prefix = ":";
        //实现抽象方法...
    }
     
    public class DBFactory
    {
        public static DBHelper GetDBHelper()
        {
            //根据条件返回DBHelper
        }
    }

      这样实现已经比用SqlXXX好很多了,这也是我之前写过的一种方式。但它仍然不够灵活,并且实现起来就会发现很多代码都是类似的,这就与我们上面的简单的需求相违背了。

      通过上面的分析,我们知道用工厂模式可以解决我们的问题,但这不用我们自己实现,.net 早就提供这样的工厂:DbProviderFactory。由名称可以指定DbProviderFactory就是数据源提供程序工厂,负责创建具体的数据源提供程序。它根据 ProviderName就可以创建对应数据源的访问对象了。这样我们的实现也由具体变成抽象了,具体的SqlConection变成了抽象的DbConnection。

      什么是 ProviderName? 在配置 web.config 的connectionStrings 时,就会有一个 providerNmae 属性,例如sql server就是 ”System.Data.SqlClient“,这个名称空间就是对应的数据源提供程序。

    3.2 参数问题

      不同数据库参数查询的格式可能不一样,例如 sql server/my sql 支持“@变量” 形式,而 oracle 支持“:变量”的形式。像上面的父类的写法,子类就必须定义自己的参数前缀。但这些用了DbProviderFactory后也不是问题了。

    3.3 using 问题

      我们都知道using是c#的语法糖,其实编译后就是 try-catch-finaly;uisng写起来比较优雅,而且在有异常的时候会自动调用对象的Disponse方法,避免有些人忘记调用。所以嵌套的 using,编译后就是嵌套的try-catch-finaly,但其实只要我们注意在抛异常的时候释放资源,一个try-catch-finaly即可。

    3.4 DbDataReader 问题

      实际项目中,我们更多的是使用DbDataReader而非DataSet/DataTable,而 DbDataReader需要自己逐行读取,这在每个调用的地方都这样写是很麻烦的,怎么解决?委托,又是它!

      说到委托还有一个小小的建议,有些人喜欢自己去定义委托,但其实.net已经内置了3种委托:Func、Action、Predicate,并且提供了多个重载版本,应该优先考虑使用这些委托,在不满足的情况下,再去自定义。

    3.5 在分层架构里的角色

      为 DAL 层提供数据访问服务,由 DAL 直接调用;不涉及sql语句拼接、日志记录等。

    四、例子

      假设要调用一个 P_GetFriends存储过程,接收一个id参数,返回一个好友列表。如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public List<Friend> GetFriends(int id)
    {
        try
        {               
            DBHelper helper = new DBHelper("dbConnectionKey");
            DbParameter[] parameters = new DbParameter[]
            {
                helper.CreateDbParameter("id",id)
            };
            return helper.ExecuteReader(CommandType.StoredProcedure, "P_GetFriends", parameters,
            reader =>
            {
                return new Friend()
                {
                    ID = reader.GetInt32(reader.GetOrdinal("ID")),
                    Name = reader.GetString(reader.GetOrdinal("Name"))
                };
            });
        }
        catch
        {
            throw;
        }
    }  

    附源代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    public class DBHelper
    {
        #region 属性
     
        /// <summary>
        /// 链接字符串
        /// </summary>
        private string conStr;
     
        /// <summary>
        /// DB工厂
        /// </summary>
        private DbProviderFactory provider;
     
        #endregion
     
        #region 构造函数
     
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="key">链接字符串键</param>
        public DBHelper(string key)
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException("key");
            }
            ConnectionStringSettings css = WebConfigurationManager.ConnectionStrings[key];
            if (css == null)
            {
                throw new InvalidOperationException("未找到指定的链接字符串!");
            }
            this.conStr = css.ConnectionString;
            this.provider = DbProviderFactories.GetFactory(css.ProviderName);
        }
     
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="conStr">链接字符串</param>
        /// <param name="providerStr">数据源提供程序</param>
        public DBHelper(string conStr, string providerStr)
        {
            if (string.IsNullOrEmpty(conStr))
            {
                throw new ArgumentNullException("conStr");
            }
            if (string.IsNullOrEmpty(providerStr))
            {
                throw new ArgumentNullException("providerStr");
            }
            this.provider = DbProviderFactories.GetFactory(providerStr);
            this.conStr = conStr;
        }
     
        #endregion
     
        #region 外部方法
     
        /// <summary>
        /// 执行命令,返回受影响行数
        /// </summary>
        /// <param name="commandType">命令类型</param>
        /// <param name="sql">sql语句或存储过程名称</param>
        /// <param name="parameters">参数数组</param>
        /// <returns>受影响行数,失败返回-1</returns>
        public virtual int ExecuteNonQuery(CommandType commandType, string sqlOrProcName, IEnumerable<DbParameter> parameters)
        {
            DbConnection con = CreateConnection();
            DbCommand cmd = CreateCommand(con, commandType, sqlOrProcName, parameters);
            try
            {
                con.Open();
                int row = cmd.ExecuteNonQuery();
                return row;
            }
            catch
            {
                throw;
            }
            finally
            {
                cmd.Dispose();
                con.Close();
            }
        }
     
        /// <summary>
        /// 执行命令,返回第一行第一列对象
        /// </summary>
        /// <param name="commandType">命令类型</param>
        /// <param name="sql">sql语句或存储过程名称</param>
        /// <param name="parameters">参数数组</param>
        /// <returns>执行结果</returns>
        public virtual object ExecuteScalar(CommandType commandType, string sqlOrProcName, IEnumerable<DbParameter> parameters)
        {
            DbConnection con = CreateConnection();
            DbCommand cmd = CreateCommand(con, commandType, sqlOrProcName, parameters);
            try
            {
                con.Open();
                object obj = cmd.ExecuteScalar();
                return obj;
            }
            catch
            {
                throw;
            }
            finally
            {
                cmd.Dispose();
                con.Close();
            }
        }
     
        /// <summary>
        /// 执行命令返回DataSet
        /// </summary>
        /// <param name="commandType">命令类型</param>
        /// <param name="sql">sql语句或存储过程名称</param>
        /// <param name="parameters">参数数组</param>
        /// <returns>DataSet</returns>
        public virtual DataSet GetDataSet(CommandType commandType, string sqlOrProcName, IEnumerable<DbParameter> parameters)
        {
            DbConnection con = CreateConnection();
            DbCommand cmd = CreateCommand(con, commandType, sqlOrProcName, parameters);
            DataSet set new DataSet();
            DbDataAdapter adapter = this.provider.CreateDataAdapter();
            try
            {
                con.Open();
                adapter.SelectCommand = cmd;
                adapter.Fill(set);
                return set;
            }
            catch
            {
                throw;
            }
            finally
            {
                adapter.Dispose();
                cmd.Dispose();
                con.Close();
            }
        }
     
        /// <summary>
        /// 执行命令返回DbDataReader
        /// </summary>
        /// <param name="commandType">命令类型</param>
        /// <param name="sql">sql语句或存储过程名称</param>
        /// <param name="parameters">参数数组</param>
        /// <param name="action">委托</param>
        /// <returns>对象列表</returns>
        public virtual List<T> ExecuteReader<T>(CommandType commandType, string sqlOrProcName, IEnumerable<DbParameter> parameters,
            Func<DbDataReader, T> action)
        {
            DbConnection con = CreateConnection();
            DbCommand cmd = CreateCommand(con, commandType, sqlOrProcName, parameters);
            List<T> result = new List<T>();
            try
            {
                con.Open();
                DbDataReader reader = cmd.ExecuteReader();
                try
                {
                    while (reader.Read())
                    {
                        var item = action(reader);
                        result.Add(item);
                    }
                    return result;
                }
                catch
                {
                    throw;
                }
                finally
                {
                    reader.Dispose();
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                cmd.Dispose();
                con.Close();
            }
        }
     
        /// <summary>
        /// 批量执行sql语句
        /// </summary>
        /// <param name="sqlList">sql语句集合</param>
        /// <param name="paramList">参数数组集合</param>
        /// <returns>执行成功或失败</returns>
        public virtual bool ExecuteSqlBatchByTrans(IEnumerable<string> sqlList, IEnumerable<List<DbParameter>> paramList)
        {
            DbConnection con = CreateConnection();
            DbCommand cmd = CreateCommand(con, CommandType.Text);
            try
            {
                con.Open();
                DbTransaction trans = con.BeginTransaction();
                cmd.Transaction = trans;
                try
                {
                    int length = sqlList.Count();
                    IEnumerable<DbParameter> parameters = null;
                    for (int i = 0; i < length; i++)
                    {
                        cmd.CommandText = sqlList.ElementAt<string>(i);
                        cmd.Parameters.Clear();
                        parameters = paramList.ElementAt<List<DbParameter>>(i);
                        foreach (DbParameter pm in parameters)
                        {
                            cmd.Parameters.Add(pm);
                        }
                        cmd.ExecuteNonQuery();
                    }
                    trans.Commit();
                    return true;
                }
                catch
                {
                    trans.Rollback();
                    throw;
                }
                finally
                {
                    trans.Dispose();
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                cmd.Dispose();
                con.Close();
            }
        }
     
        #endregion
     
        #region CreateDbParameter
     
        public DbParameter CreateDbParameter(string name, object value)
        {
            DbParameter parameter = this.provider.CreateParameter();
            parameter.ParameterName = name;
            parameter.Value = value;
            return parameter;
        }
     
        public DbParameter CreateDbParameter(string name, object value, ParameterDirection direction)
        {
            DbParameter parameter = this.provider.CreateParameter();
            parameter.ParameterName = name;
            parameter.Value = value;
            parameter.Direction = direction;
            return parameter;
        }
     
        public DbParameter CreateDbParameter(string name, object value, int size)
        {
            DbParameter parameter = this.provider.CreateParameter();
            parameter.ParameterName = name;
            parameter.Value = value;
            parameter.Size = size;
            return parameter;
        }
     
        public DbParameter CreateDbParameter(string name, object value, int size, DbType type)
        {
            DbParameter parameter = this.provider.CreateParameter();
            parameter.ParameterName = name;
            parameter.Value = value;
            parameter.Size = size;
            parameter.DbType = type;
            return parameter;
        }
     
        public DbParameter CreateDbParameter(string name, object value, int size, DbType type, ParameterDirection direction)
        {
            DbParameter parameter = this.provider.CreateParameter();
            parameter.ParameterName = name;
            parameter.Value = value;
            parameter.Size = size;
            parameter.DbType = type;
            parameter.Direction = direction;
            return parameter;
        }
     
        #endregion
     
        #region 私有方法
     
        /// <summary>
        /// 获取链接实例
        /// </summary>
        /// <returns>链接实例</returns>
        private DbConnection CreateConnection()
        {
            DbConnection con = this.provider.CreateConnection();
            con.ConnectionString = this.conStr;
            return con;
        }
     
        /// <summary>
        /// 获取命令实例
        /// </summary>
        /// <param name="con">链接实例</param>
        /// <param name="commandType">命令类型</param>
        /// <param name="sqlOrProcName">sql语句或存储过程名称</param>
        /// <returns>命令实例</returns>
        private DbCommand CreateCommand(DbConnection con, CommandType commandType, string sqlOrProcName, IEnumerable<DbParameter> parameters)
        {
            DbCommand cmd = InitCommand(con, commandType, parameters);
            cmd.CommandText = sqlOrProcName;
            return cmd;
        }
     
        /// <summary>
        /// 获取命令实例
        /// </summary>
        /// <param name="con">链接实例</param>
        /// <param name="commandType">命令类型</param>
        /// <returns>命令实例</returns>
        private DbCommand CreateCommand(DbConnection con, CommandType commandType)
        {
            return InitCommand(con, commandType, null);
        }
     
        /// <summary>
        /// 初始化命令
        /// </summary>
        /// <param name="commandType">命令类型</param>
        /// <param name="parameters">参数集合</param>
        /// <returns></returns>
        private DbCommand InitCommand(DbConnection con, CommandType commandType, IEnumerable<DbParameter> parameters)
        {
            DbCommand cmd = con.CreateCommand();
            cmd.CommandType = commandType;
            if (parameters != null)
            {
                foreach (DbParameter pm in parameters)
                {
                    cmd.Parameters.Add(pm);
                }
            }
            return cmd;
        }
     
        #endregion
    }
  • 相关阅读:
    前后端分类状态下SpringSecurity的玩法
    拓展 centos 7
    linux 日志管理
    Linux 内存监控
    Linux 周期任务
    Linux 文件系统
    linux 磁盘管理
    图论 最短路总结
    进阶线段树之乘法操作
    暑假集训Day 10 小烈送菜
  • 原文地址:https://www.cnblogs.com/nele/p/4800546.html
Copyright © 2011-2022 走看看