[故纸堆四]面向对象实现无差别数据库访问
面向对象实现无差别数据库访问
一:引言:
ADO.NET为应用程序开发人员提供了一种全新的数据库访问机制,它使得数据库编程变得相当容易。然而,在运用ADO.NET进行数据库编程时,开发人员往往会因为不注意某些细节问题而使得应用程序的可扩展性很差,也即某个数据库应用程序只能应用于某个特定类型的数据库,而不能和更多的其他类型的数据库进行交互或是移植到其它数据库平台下。本文将深入研究如何运用面向对象的接口技术和工厂模式来实现通用数据库编程技术并构建通用数据库访问层。
ADO.net对于不同的数据源提供了不同的数据提供者,三个通用的数据提供者分别是OLE DB, SQL, and ODBC。使用不同数据提供者的用意就在于可以针对不同的数据源提供最强大且稳定的数据访问技术。例如,当你访问Access数据库时采用OLE DB data provider这是最有效的方法,但是如果你采用ODBC data provider时,它是建在OLE DB data provider基础上的,所以效率就会打折扣。
实际上,所有的数据提供者类(data provider classes),比如连接(connection),命令(command),数据适配器(data adapter)和数据读取者(data reader)都是从某一特定接口继承的。我希望写篇文章深入的讨论这些,但这要花费我很多天的时间。
总之,我的这篇文章的主要问题是如何写一个通用的类能够在运行时根据用户的选择而分别采用OLE DB, SQL, and ODBC data providers访问数据源。
二:ADO.NET体系结构:
ADO.NET是由一系列的数据库相关类和接口组成的,它的基石是XML技术,所以通过运用ADO.NET技术应用程序不仅能访问关系型数据库中的数据,而且还能访问层次化的XML数据。ADO.NET为应用程序提供了两种数据访问的模式:连接模式(Connected Mode)和非连接模式(Disconnected Mode)。运用过ADO技术的开发人员对前一种模式应该是非常熟悉的,而后一种模式则是ADO.NET才具有的。相比于传统的数据库访问模式,非连接的模式为应用程序提供了更大的可升级性和灵活性。在该模式下,一旦应用程序从数据源中获得所需的数据,它就断开与原数据源的连接,并将获得的数据以XML的形式存放在主存中。在应用程序处理完数据后,它再取得与原数据源的连接并完成数据的更新工作。
ADO.NET中的DataSet类是非连接模式的核心,数据集对象(DataSet)是以XML的形式存放数据。应用程序既可以从一个数据库中获取一个数据集对象,也可以从一个XML数据流中获取一个数据集对象。而从用户的角度来看,数据源在哪里并不重要,也是无需关心的。这样一个统一的编程模型就可被运用于任何使用了数据集对象的应用程序。
ADO.NET体系结构中还有一个非常重要的部分就是数据提供者对象(Data Provider),它是访问数据库的必备条件。通过它应用程序可以产生相应的数据集对象;同时它还提供了连接模式下的数据库访问支持。图1描述了ADO.NET总体的体系结构。
图1 ADO.NET的体系结构
三:数据提供者对象:
本文研究的是通用数据库编程并如何运用该技术实现通用数据库访问层,因此要从ADO.NET体系结构中的数据提供者对象入手,使得一个应用程序具有访问多个不同类型数据库的能力。ADO.NET中的数据提供者对象包括数据库连接接口(IDbConnection)、数据库命令接口(IDbCommand)、数据读取器接口(IDataReader)和数据适配器接口(IDbDataAdapter)等不同种类的接口。通过这些接口,应用程序可以访问数据库、执行相关的命令操作并获取相应结果,获取的结果可以是以XML数据的形式存放在数据集对象中,也可以是直接被应用程序所使用。目前,微软的.NET Framework已经是1.1版本了,所以其中的ADO.NET支持了更广泛的数据提供者对象:一种为SQL Server数据提供者对象,它是专门应用于MS SQL Server数据库的,所以性能得到了优化;一种为OleDb数据提供者对象,它可以通过COM层和OLE DB进行交互;一种为ODBC数据提供者对象,它可以直接跟ODBC数据源进行交互;最后一种Oracle数据提供者对象,它是专门针对Oracle数据库的,所以性能上也得到了不少优化。与这四种数据提供者对象相关联的类的前缀分别为:Sql、OleDb、Odbc以及Oracle,而与其相关联的命名空间则分别为:System.Data.SqlClient、System.Data.OleDb、System.Data.Odbc以及System.Data.OracleClient。在实际的开发中,开发人员可以根据需要选择相应类型的数据提供者对象。不过为了使应用程序具有通用性,开发人员应通过使用数据提供者对象的接口而并非某个特定类型的数据提供者对象来实现数据库的访问操作,这样就可以实现通用数据库编程并构建通用数据库访问层了。
下面的表格列举了各种接口的名称以及相应的描述:
表1 各种接口名称以及其相应描述
下面的表格列举了四种不同类型的数据提供者对象中的各个相互并行的类:
表2 四种类型的数据提供者对象中的类
四.通用数据库访问层的构建
数据提供者对象构成了ADO.NET的基础,通过实现其中的各种不同的接口,构建通用数据库访问层并非难事。一般来说,运用数据提供者对象访问并更新数据的操作会包含以下几个步骤:
1.运用数据库连接对象建立和数据源的连接。
2.根据上面建立的数据库连接对象创建一个数据库命令对象以执行特定的操作。
3.执行数据库命令对象或创建并执行数据适配器对象,以返回数据读取器对象(连接模式)或填充数据集对象(非连接模式)或取得其他相应结果。
4.数据处理完毕后,通过数据库命令对象或数据适配器对象将处理结果更新到数据源。
5.最后释放各种数据提供者对象资源。
基于以上考虑,构建通用数据库访问层主要得实现对数据源访问的底层操作的封装,而仅仅暴露出经过进一步抽象的数据读取器对象或是数据集对象等以供商业逻辑层调用,使用者不再考虑使用什么数据库实现。
图1 数据库连接、适配器操作抽象图
我们首先定义接口IDbRoot用来制定所有要实现的功能契约(执行SQL命令、执行查询命令并返回到DataSet、执行查询并返回到DataReader、打开、关闭连接等等),然后在使用到具体数据库时创建继承此接口的实现方法类如OracleRoot、SqlRoot,使用DbFactory的GetDb来根据条件产生实例。而使用者就不需要考虑具体的数据库类型。
图2 数据库读取器操作抽象图
然后我们对DataReader进行抽象,使用IDataReader接口,通过在EIIRead中封装IDataReader的方法,实现操作的统一。
五:示例对比
1, 在不使用通用对象时,当使用的数据库从Oracle转到SQL server下时,所有的程序都要将使用OracleConnection 的地方变为SqlConnection、OracleDataAdapter改为用SqlDataAdapter:
public bool AddNew()
{
string sql = " INSERT INTO T_TEST (USER_ID,USER_NAME) values (?,?) ";
DbSqlText cmd = new DbSqlText(sql);
cmd.SetValue(userID);
cmd.SetValue(userName);
OracleConnection conn= new OracleConnection (CONN_STRING);
conn.Open();
OracleCommand comm = new OracleCommand(cmd.SqlCmd,conn);
comm.CommandType = CommandType.Text;
bool ok = (comm.ExecuteNonQuery()>0);
conn.Close();
return ok;
}
需要修改为
public bool AddNew()
{
string sql = " INSERT INTO T_TEST (USER_ID,USER_NAME) values (?,?) ";
DbSqlText cmd = new DbSqlText(sql);
cmd.SetValue(userID);
cmd.SetValue(userName);
SqlConnection conn = new SqlConnection(CONN_STRING);
conn.Open();
SqlCommand comm = new SqlCommand(cmd.SqlCmd,conn);
comm.CommandType = CommandType.Text;
bool ok = (comm.ExecuteNonQuery()>0);
conn.Close();
return ok;
}
2, 如果使用通用对象,我们只需要针对SQL Server实现IDbRoot,其他上层代码就无需修改,如下:
public bool AddNew()
{
string sql = " INSERT INTO T_TEST (USER_ID,USER_NAME) values (?,?) ";
DbSqlText cmd = new DbSqlText(sql);
cmd.SetValue(userID);
cmd.SetValue(userName);
//使用工厂根据当前配置生成所需的数据操作实例
IDbRoot db = DbFactory.GetDb();
//使用接口定义的方法
return db.Exec(cmd.SqlCmd );
}
六:总结
本文研究了运用ADO.NET中数据提供者对象的接口技术实现通用数据库访问层的方法,并给出了一个具体的测试项目以显示通用数据库访问层给实际项目带来的潜在益处和效率。ADO.NET中数据提供者对象的接口技术是非常有用的,而平常开发人员却很容易将其忽视掉,那样的结果就是实际项目的可扩展性、灵活性和通用性大打折扣。而通过运用这项技术,实际项目的通用性,灵活性和可扩展性都获得了大大的提高,这为项目以后可能的升级或移植作好了前期的准备,从而可以减小项目的相关风险系数。