在 .NET 的学习过程中用过三种数据库:Sql Server、Access、SQLite。Sql Server 用得相对多一点点,但是,麻烦,每次用它都需要开服务,而且还费资源,更麻烦的是拷贝到一台没有装SqlServer的电脑上还不能用,所以一般练习中只要不涉及到什么存储过程这些也基本不用它。Access简单的文件型数据库,office的套件,基本的SQL语句也支持,在一般的小型应用系统中用起来还行,而且在Windows 7开始操作系统中就已经内置了Access的服务,随便拷贝。SQLite用过一次,需要安装服务,和Access一样,拷贝一下就可以用,就是管理工具的界面比较shit。

用过三种数据库后,发现其实在微软的ADO.NET中操作数据库的方式是一样的,链接对象都是继承自实现了IDbConnection接口的抽象类DbConnection,以及实现了IDbCommand的接口的抽象类DbCommand,其它的都一样。古就可以统一用接口或者抽象来来进行操作数据库了,这样就能够实现基本的跨数据库访问的功能了。唯一不同的就是实例化连接字符串的时候要用具体的子类来进行实例化,这时就可以用静态工厂来实现,从而做到不修改程序就实现夸数据库的功能,当然应用程序中的SQL语句是通用的才行,如果是某些数据库特有的,那就不行了。

静态工厂

也成为简单工厂,主要是用来因对对象的变化,根据不同的要求创建不同的实例,就可以进行一个不同的操作。在黑马的基础加强里面,有一个比较经典的例子能够说明他的作用:简单计算器。主程序中一直不变,变的是运算规则,然后把运算规则给抽出来,写到一个工厂中,然后根据不同的运算符来创建不同的计算实例,然后用一个同一的接口返回这个实例对象,就能够完成计算了。

         /// <summary>
/// 根据传递过来的运算符,来创造不同的子类对象
/// </summary>
/// <param name="op">运算符</param>
/// <returns>返回的是一个实现了接口ICalculatorable.ICalculable的子类对象</returns>
public static ICalculatorable.ICalculable CreatInstance (string op)
{
//创建一个接口变量,赋值为null
ICalculatorable.ICalculable cc = null;
////根据传递过来的运算符来创建对应的实例成员
switch (op)
{
//如果传递过来的是加法运算符,就创建一个计算加法的实例成员,并且赋值给上面的接口变量
case "+": cc = new AddLib_02.Add(); break;
case "-": cc = new SubLib_02.Sub(); break;
case "*": cc = new MultipLib_02.Multip(); break;
case "/": cc = new DivLib_02.Div(); break;
case "%": cc = new ModelLib_02.Model(); break;
//若上面的都不符合,就将接口变量赋值为null
default: cc = null;
break;
}
//返回指向了一个具体实例对象的接口变量
return cc;
}

简单计算器的静态工厂

其实在访问数据库的时候也是一样的,唯一变的就是链接实例,所以就可以把创建实例的东西给抽出来,交给一个工厂来实现,工厂可以通过读取配置文件来进行创建相应的实例对象,然后返回。代码如下:

     internal sealed class DbProvideFactory
{
private static string dbProvide = ConfigurationManager.AppSettings["provide"];
public static string DbProvide { get { return dbProvide; } } private static string connectionString = ConfigurationManager.ConnectionStrings["connStr"].ConnectionString;
/// <summary>
/// 连接字符串
/// </summary>
public static string ConnectionString
{
get { return connectionString; }
private set { connectionString = value; }
} #region 返回一个连接对象
public static DbConnection GetConnection ()
{
switch (dbProvide.ToLower())
{
case "sqlserver":
case "sql":
case "mssqlserver":
case "mssql": return new SqlConnection(connectionString);
case "access": return new OleDbConnection(connectionString);
case "sqlite": return new SQLiteConnection(connectionString);
case "oracle": return new OracleConnection(connectionString);
default: return new SqlConnection(connectionString);
}
}
#endregion #region 返回一个 Adapter
/// <summary>
/// 返回一个 Adapter
/// </summary>
/// <param name="cmd">Command 命令</param>
/// <returns></returns>
public static DataAdapter GetAdapter (IDbCommand cmd)
{
switch (dbProvide.ToLower())
{
case "sqlserver":
case "sql":
case "mssqlserver":
case "mssql": return new SqlDataAdapter(cmd as SqlCommand);
case "access": return new OleDbDataAdapter(cmd as OleDbCommand);
case "sqlite": return new SQLiteDataAdapter(cmd as SQLiteCommand);
case "oracle": return new OracleDataAdapter(cmd as OracleCommand);
default: return new SqlDataAdapter(cmd as SqlCommand);
}
}
#endregion #region 生成参数化查询时的参数对象
/// <summary>
/// 生成参数化查询时的参数对象
/// </summary>
/// <param name="name">参数名,@符号可有可无</param>
///
/// <returns></returns>
public static DbParameter GetParameter (string name, object value)
{
if (!name.StartsWith("@"))
{
name = "@" + name;
}
if (value == null)
{
value = DBNull.Value;
}
switch (dbProvide.ToLower())
{
case "sqlserver":
case "sql":
case "mssqlserver":
case "mssql": return new SqlParameter(name, value);
case "access": return new OleDbParameter(name, value);
case "sqlite": return new SQLiteParameter(name, value);
case "oracle": return new OracleParameter(name, value);
default: return new SqlParameter(name, value);
}
} /// <summary>
/// 生成参数化查询时的 输出参数
/// </summary>
/// <param name="name">参数名</param>
/// <param name="type">参数类型</param>
/// <returns></returns>
public static IDbDataParameter GetParameter (string name,DbType type)
{
if (!name.StartsWith("@"))
{
name = "@" + name;
}
IDbDataParameter p;
switch (dbProvide.ToLower())
{
case "sqlserver":
case "sql":
case "mssqlserver":
case "mssql": p = new SqlParameter(name, type); break;
case "access": p = new OleDbParameter(name, type); break;
case "sqlite": p = new SQLiteParameter(name, type); break;
case "oracle": p = new OracleParameter(name, type); break;
default: p = new SqlParameter(name, type); break;
}
p.Direction = ParameterDirection.Output;
return p;
}
#endregion
}

上面代码中首先读取了配置文件,以及连接字符串,这点可能封装的是太好,按理说,应该从DbHelper中传递过来,但是考虑到后面多处用到它,就把他放到这个地方了。
    根据配置文件中数据提供者的值不同然后创建不同的数据库连接对象。同时由于在参数化查询的时候,需要实例化参数对象,有封装了两个参数对象方法。在用SqlServer的存储过程中,考虑到有输出参数,古做了一个重载。

至于 DataAdapter,由于它里也是可以自己创建连接的,所以也就比较麻烦一点,手动创建了,其实可以利用微软已经提供好的一种方式,就是通过读取配置文件中的配置提供者,然后调用一些方法来进行创建,实现原理中应该也是利用的反射,由于这个值在配置文件中比较麻烦,需要写全称,也就放弃它了。

上面基本上就是利用简单工厂来实现的创建不同数据库实例和参数化对象的方式。

  

SQLite需要在官方下载ADO.NET的插件,然后进行引用,Oracle数据库没有尝试过,也需要应用组件,列在此处也是为了以后方便扩展

DbHelper

这里面就封装了几个常规的增删改查的方法,调用工厂的方法得到连接对象和参数,然后用接口去指向他们,由于使用的是接口去指向,故在添加参数的时候,只能够通过遍历来实现,可以改为抽象类来指向,这样就能够一次添加一个数组了。代码如下:

 public partial class DbHelper
{
//#region 得到一个连接对象,由具体的子类去重写而实现
///// <summary>
///// 得到一个连接对象,由具体的子类去重写而实现
///// </summary>
///// <returns></returns>
//public abstruct DbConnection GetConnection ();
//#endregion #region 执行增加、删除、更新三个非查询语句
public static int ExecuteNonQuery (string strSql, params IDataParameter[] pars)
{
using (IDbConnection conn = DbProvideFactory.GetConnection() )
{
using (IDbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = strSql; if (pars != null && pars.Length > )
{
foreach (var item in pars)
{
cmd.Parameters.Add(item);
}
}
conn.Open();
return cmd.ExecuteNonQuery();
}
}
}
#endregion #region 执行标量值查询
public static object ExecuteScalar (string strSql, params IDataParameter[] pars)
{
using (IDbConnection conn = DbProvideFactory.GetConnection())
{
using (IDbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = strSql;
if (pars != null && pars.Length > )
{
foreach (var item in pars)
{
cmd.Parameters.Add(item);
}
}
conn.Open();
return cmd.ExecuteScalar();
}
}
}
#endregion #region 返回一个只读的DbDataReader
public static IDataReader ExecuteReader (string strSql,CommandType type, params IDataParameter[] pars)
{
IDbConnection conn = DbProvideFactory.GetConnection(); using (IDbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = strSql;
if (pars != null && pars.Length > )
{
foreach (var item in pars)
{
cmd.Parameters.Add(item);
}
}
cmd.CommandType = type;
try
{
conn.Open();
//外部关闭当前连接
return cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
}
catch
{
conn.Close();
conn.Dispose();
throw;
}
}
}
#endregion #region 返回一张数据表格,如果没有查到数据就返回null
//由于用的是接口(或者抽象类)不能够直接创建Command来添加参数,所以调用了工厂的方法创建一个连接对象,然后创建Command来实现参数的添加
public static DataTable DataAdpater (string strSql, params IDataParameter[] pars)
{
using (IDbConnection conn = DbProvideFactory.GetConnection())
{
IDbCommand cmd = conn.CreateCommand();
cmd.CommandText = strSql;
if (pars != null && pars.Length > )
{
foreach (var item in pars)
{
cmd.Parameters.Add(item);
}
}
IDataAdapter adapter = DbProvideFactory.GetAdapter(cmd);
DataSet set = new DataSet();
int count = adapter.Fill(set);
return count > ? set.Tables[] : null;
}
}
#endregion #region 将一个SqlDataReader对象转换成一个实体类对象 +static TEntity MapEntity<TEntity>(SqlDataReader reader) where TEntity : class,new()
/// <summary>
/// 将一个DataReader对象转换成一个实体类对象
/// </summary>
/// <typeparam name="TEntity">实体类型</typeparam>
/// <param name="reader">当前指向的reader</param>
/// <returns>实体对象</returns>
public static TEntity MapEntity<TEntity>(IDataReader reader) where TEntity : class,new()
{
try
{
var props = typeof(TEntity).GetProperties();
var entity = new TEntity();
foreach (var prop in props)
{
if (prop.CanWrite)
{
try
{
var index = reader.GetOrdinal(prop.Name);
var data = reader.GetValue(index);
if (data != DBNull.Value)
{
prop.SetValue(entity, Convert.ChangeType(data, prop.PropertyType), null);
}
}
catch (IndexOutOfRangeException)
{
continue;
}
}
}
return entity;
}
catch
{
return null;
}
}
#endregion
}

在上面代码中,如果程序需要使用具体的连接对象,可以将DBHelper改成一个抽象类,然后把创建连接实例对象改成抽象方法,就可以了。

通过上面的简单工厂和用接口来指向操作数据库的各种对象就能够做到简单的跨数据库的功能了

静态工厂 + DbHelper的更多相关文章

  1. 静态工厂方法VS构造器

    我之前已经介绍过关于构建者模式(Builder Pattern)的一些内容,它是一种很有用的模式用于实例化包含几个属性(可选的)的类,带来的好处是更容易读.写及维护客户端代码.今天,我将继续介绍对象创 ...

  2. [原创]java WEB学习笔记102:Spring学习---Spring Bean配置:bean配置方式(工厂方法(静态工厂方法 & 实例工厂方法)、FactoryBean) 全类名

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  3. Spring 学习总结 使用静态工厂创建Bean

    创建Bean时,class属性必须指定,此时为静态工厂类. factory-method指定静态工厂方法名. 接口: public interface Being { public void test ...

  4. 【Effective Java】1、静态工厂的方式代替构造函数

    使用一个服提供者对进行服务的提供,服务的请求通过不同的提供者提供不同的服务,服务提供者首先必须在工厂中进行注册,然后才可以通过工厂实例化服务 Service.java package cn.xf.cp ...

  5. 读Effective Java笔记之one:static Factory methods instead of Constructors (静态工厂方与构造器)

    获取类的实例的方法有很多种,在这很多种方法中,它们各有优缺,各有特点.这里,只介绍2中方法 1.使用构造方法 public class Person { private String sex; /** ...

  6. <创建和销毁对象>经验法则——考虑用静态工厂方法代替公有构造方法

    一.引出静态工厂方法 对于java类而言,为了让使用者获取它自身的一个实例化对象,会有以下方法: 1.该类提供一个公有的构造方法.在这种情况下,程序可以通过多个“new 构造方法”语句来创建类的任意多 ...

  7. HttpSolrServer-采用静态工厂方法,创建HttpSolrServer单实例

    HttpSolrServer线程安全,如果使用下面构造器,必须对所有的请求重用相同的实例.如果实例在运行中创建的,它可能会导致连接泄漏.推荐的做法就是保持每个solr服务url的HttpSolrSer ...

  8. 创建对象_工厂方法(Factory Method)模式 与 静态工厂方法

      工厂方法模式:   定义:为创建对象定义一个接口,让子类决定实例化哪个类.工厂方法让一个类的实例化延迟至子类.   应用场景: 客户类不关心使用哪个具体类,只关心该接口所提供的功能: 创建过程比较 ...

  9. 设计模式系列 1——StaticFactory(静态工厂),AbstractFactory(抽象工厂)

    本文出自 代码大湿 代码大湿 本系列持续更新,敬请关注. 1 静态工厂 静态工厂介绍: 静态工厂模式可以实现接口封装隔离的原则.在客户端只知接口而不知实现的时候可以使用静态工厂模式. 源码请点击我 角 ...

随机推荐

  1. 我的MVP呢?

    Ladies and gentelmen, welcome the MVP of NBA 16-2017 Season:... 呃,等下,好像哪里不对.那是因为,我要说的MVP根本就不是Most Va ...

  2. Max Chunks To Make Sorted II LT768

    This question is the same as "Max Chunks to Make Sorted" except the integers of the given ...

  3. 提升HTML5的性能体验系列之一 避免切页白屏

    窗体切换白屏的现实问题 HTML5的性能比原生差很多,比如切页时白屏.列表滚动不流畅.下拉刷新和上拉翻页卡顿.在低端Android手机上,很多原生App常用的功能和体验效果都很难使用HTML5技术模拟 ...

  4. Tkinter添加图片

    Tkinter添加图片的方式,与Java相似都是利用label标签来完成的: 一.默认的是gif的格式,注意将png后缀名修改为gif还是无法使用,文件格式依然错误. photo = PhotoIma ...

  5. 2018.12.14 codeforces 922E. Birds(分组背包)

    传送门 蒟蒻净做些水题还请大佬见谅 没错这又是个一眼的分组背包. 题意简述:有n棵树,每只树上有aia_iai​只鸟,第iii棵树买一只鸟要花cic_ici​的钱,每买一只鸟可以奖励bbb块钱,从一棵 ...

  6. WebSocket 长连接 及超时问题解决

    <?phpset_time_limit(0); class SocketService { private $address = 'localhost'; private $port = 80; ...

  7. Keepalived+Nginx高可用架构配置

    1.yum install -y libnfnetlink-devel2.yum -y install libnl libnl-devel 3.yum -y install openssl-devel ...

  8. Linux 安装android

    ---恢复内容开始---http://pan.baidu.com/s/1rvPP8 1.下载eclipse http://pan.baidu.com/s/1kTvNjmv http://www.cr1 ...

  9. IntelliJ IDEA 2017版 spring-boot 2.0.3 邮件发送搭建,概念梳理 (二)

    第二部分 邮件发送历史   一.第一封邮件   1.1969年10月,世界上的第一封电子邮件    1969年10月世界上的第一封电子邮件是由计算机科学家Leonard K.教授发给他的同事的一条简短 ...

  10. 第20章:MongoDB-聚合操作--聚合管道--$unwind

    ①$unwind 在查询数据的时候经常会返回数组信息,但是数组并不方便信息的浏览,所以提供有“$unwind”可以将数组数据变为独立的字符串内容. 将文档中数组类型的字段拆分成多条,每条文档包含数组中 ...