热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

数据库的ORM

如果我们要做一个需要能够支持各种数据库的ORM。可以用到AbstractFactory,Singleton等设计模式。我们先分析一下,要实现一个ORM&

如果我们要做一个需要能够支持各种数据库的ORM。可以用到AbstractFactory,Singleton等设计模式。
  
  我们先分析一下,要实现一个ORM,我们首先需要一个能够和各种不同数据库平台交互的一致的接口,我们把它叫做DBWR,意思就是“数据库读写”。它可以读取数据库中的数据,对数据库执行DDL操作。我们认为它有这些功能:
  
  1、读取数据,以DataTable的形式返回,其原型是:
  
  public DataTable GetData(string Sql);
  
  2、执行Sql语句,并返回受影响的行数,其原型是:
  
   public int ExecSql(string Sql);
  
   在实践中,我们经常以参数的形式作为某种条件来读取数据或者把数据写入数据库,这样一是方便,二是安全。因此还应该加上如下函数:
  
  3、public DataTable GetData(string Sql, IDataParameter[] Params);
  
  4、public int ExecSql(string Sql, IDataParameter[] Params);
  
  另外,我们经常只要从数据库中获取一个值,完全没有必要用DataTable的形式,就用一个object好了。所以我们再加上下面的函数:
  
  5、public object GetSingleValue(string Sql);
  
  6、public object GetSingleValue(string Sql, IDataParameter [] Params);
   下面是DBWR的简单类图:
  
  在上面的函数定义中,我们为什么用IDataParameter,而不用SqlParameter呢?请注意,我们的DBWR类是需要与各种支持.net的数据库平台进行交互的,如果用SqlParameter的话,就只能与Sql Server进行交互了。
  
  在.net 中,对数据库进行连接的通用接口是IDbConnection.,执行Sql语句的是IDbCommand,当然还有用来填充IDbCommand的 Parameters属性IDataParameter,当然,如果我们需要更加方面的操作,那就需要一个IDbDataAdapter的实例。但是各数 据平台对他们的实现是不同的,不要期望Sql Server能够与Oracle能够统一。我们的DBWR类需要获取这一系列操作数据库的、相关的接口的实例,但是又不知道这些实例的具体的类,怎么办 呢?这个时候,就可以用AbstractFactory设计模式来解决这个问题。它的意图就是“提供一个创建一系列相关或相互依赖对象的接口,而无需指定 它们具体的类”。
  
  我们把这个能够为我们提供这些能与不同数据库平台打交道的接口,但又不必指定具体类的工具叫做DbInterfaceGenerator(意思就是“数据库接口生成器”),它有这些功能:
  
  1、获取能够连接数据库的IDbConnection的实例:
  
  public abstract IDbConnection GetConnection();
  
  2、获取能够执行Sql语句的IDbCommand的实例:
  
  public abstract IDbCommand GetCommand();
  
  3、获取IDataParameter的实例:
  
   A、按参数名称,参数的值获取参数:
  
  public abstract IDataParameter GetParameter(string ParameterName, object ParameterValue);
  
  B、按参数名称,参数的长度,参数的精度,参数的数据库类型的名字,比如“varchar”获取参数:
  
   public abstract IDataParameter GetParameter(string ParameterName, int? Length, byte? Scale, string DbTypeName);
  
  C、按参数名称,参数的长度,参数的精度,参数的数据库类型的名字,比如“varchar”,参数的值获取参数:
  
   public abstract IDataParameter GetParameter(string ParameterName, int? Length, byte? Scale, string DbTypeName, object ParameterValue);
  
   为什么把参数的长度,参数的精度声明为int?呢?因为有时我们并不需要指定参数的长度和精度,这时,就用null代替好了。
  
  4、获取IDbDataAdapter
  
  public abstract IDbDataAdapter GetAdapter();
  
  
  
   下面是的DbInterfaceGenerator简单类图:
  
  那 么对应Sql Server数据库,那就是SqlDbInterface,它返回的就是分别是 SqlConnection,SqlCommand,SqlParameter,SqlDataAdapter。对应Oracel数据库,那就是 OracleDbInterface.它返回的就是 OracleConnection,OracleCommand,OracleParameter,OracleDataAdapter。
  
  对其他的数据库,就是XXXDbInterface,返回就是对应的Connneciton,Command,Paramter和DataAdapter。
  
  下面是它们的简单类图:
  
  
  不 同数据库进行交互的问题解决了,我们又碰到了一个问题,我们的DBWR类每次需要用到DbInterfaceGenerator类某个子类的实例的时候, 难道每次都new一个新的实例么?这样做,既不方便,也会影响性能。一般来说,只要我们决定了用哪种数据库,系统一旦启动,就不会再更改。所以我们用以与 数据交互的DbInterface只要有一个实例就可以了,它在第一次需要的的时候就生成。可以用Singleton模式达到我们的要求。它的目的就是“ 保证一个类仅有一个实例,并提供一个访问它的全局访问点”。
  
  我们给DbInterfaceGenerator添加一个能够让其他类可以访问它本身唯一实例的“全局访问点”,叫做DbInterface。
  
  改进后的DbInterfaceGenerator 的类图如下:
  
  
  对于DBWR类,我们也需要提供一个这样的访问点,叫做Instance。改进后的类图是:
  
  我们非常幸运,因为我们使用C#,实现这种Singleton模式,非常容易。两条简单的语句就可以实现DBWR的Instance:
  
  public static readonly DBWR Instance = new DBWR();
  
  protected DBWR() { }
  
  当然,我们也可以把我们的DBWR类的构造函数申明为private.
  
  不 过,对于DbInterfaceGenerator的DbInterface,就没有这么容易了。但也不要紧张,大家注意到public static readonly和 DBWR的构造函数没有?套用领导们常说的话:“只要抓住了public static readonly和protected/private 构造函数(){…} 这两个基本点,我们的属性就符合Singleton模式了”。其实现如下:
  
   public static readonly DbInterfaceGenerator DbInterface = GetDbInterface();
  
   private static DbInterfaceGenerator GetDbInterface()
  
   {
  
   string InterfaceType = System.Configuration.ConfigurationManager.AppSettings["DbInterfaceType"];
  
   string InterfaceAssembly = System.Configuration.ConfigurationManager.AppSettings["DbInterfaceAssembly"];
  
   if (!string.IsNullOrEmpty(InterfaceType) && !string.IsNullOrEmpty(InterfaceAssembly))
  
   {
  
   return (DbInterfaceGenerator)
   System.AppDomain.CurrentDomain.CreateInstance(InterfaceAssembly, InterfaceType).Unwrap();
  
   }
  
   return new SqlDbInterface();
  
   }
  
 在GetDbInterface函数中,我们用到了“反射”,这是.net中一个非常重要的特性,他对于实现工厂方法,提供了“无与伦比的方便”。相关函数还有“Activator.CreateInstance”等。
  
   如果我们的数据库平台不是Sql Server,那么需要在我们的App.config文件的AppSettings 节中,添加“DbInterfaceType”和“DbInterfaceAssembly”这两个元素。
  
  具体代码因为较长,不便在这里贴出,请参考DongLiORM中的DBWR.cs, DbInterface.cs,和SqlDbInterface.cs。
  
  参考文件:
  
  TerryLee的《.NET设计模式(2):单件模式(Singleton Pattern)》
  
  TerryLee的《.NET设计模式(3):抽象工厂模式(Abstract Factory)》


推荐阅读
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • Python SQLAlchemy库的使用方法详解
    本文详细介绍了Python中使用SQLAlchemy库的方法。首先对SQLAlchemy进行了简介,包括其定义、适用的数据库类型等。然后讨论了SQLAlchemy提供的两种主要使用模式,即SQL表达式语言和ORM。针对不同的需求,给出了选择哪种模式的建议。最后,介绍了连接数据库的方法,包括创建SQLAlchemy引擎和执行SQL语句的接口。 ... [详细]
  • Java自带的观察者模式及实现方法详解
    本文介绍了Java自带的观察者模式,包括Observer和Observable对象的定义和使用方法。通过添加观察者和设置内部标志位,当被观察者中的事件发生变化时,通知观察者对象并执行相应的操作。实现观察者模式非常简单,只需继承Observable类和实现Observer接口即可。详情请参考Java官方api文档。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • Oracle分析函数first_value()和last_value()的用法及原理
    本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
author-avatar
mobiledu2502882737
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有