PetShop数据访问层之数据库访问计划

《解剖PetShop》序列之二

二、PetShop数据访问层之数据库访问计划
在多样一中,我从总体上分析了PetShop的架构设计,并提及了分段的概念。从本有的起初,我将各种对各层开展代码级的解析,以求拿到越来越密切而尖锐的掌握。在PetShop
4.0中,由于引入了ASP.Net
2.0的有的新特色,所以数据层的情节也更为的科普和复杂性,包括:数据库访问、Messaging、MemberShip、Profile四部分。在浩如烟海二中,我将介绍有关数据库访问的筹划。

在PetShop中,系统需要处理的数据库对象分为两类:一是数据实体,对应数据库中相应的数据表。它们从不作为,仅用于表现对象的多寡。那个实体类都被内置Model程序集中,例如数据表Order对应的实业类OrderInfo,其类图如下: 

图片 1

这一个目的并不持有持久化的效用,简单地说,它们是作为数据的载体,便于工作逻辑针对相应数据表举行读/写操作。虽然这么些类的习性分别映射了数据表的列,而每一个对象实例也恰恰对应于数据表的每一行,但这些实体类却并不持有相应的数据库访问能力。

出于数量访问层和作业逻辑层都将对这个数量实体举行操作,因此先后集Model会被这两层的模块所引用。

第二类数据库对象则是数码的事情逻辑对象。那里所指的事体逻辑,并非业务逻辑层意义上的小圈子(domain)业务逻辑(从这一个意义上,我更倾向于将业务逻辑层称为“领域逻辑层”),一般意义上说,这一个工作逻辑即为基本的数据库操作,包括Select,Insert,Update和Delete。由于那么些事情逻辑对象,仅具有行为而与数量无关,由此它们均被架空为一个独立的接口模块IDAL,例如数据表Order对应的接口IOrder: 

图片 2

将数据实体与有关的数据库操作分离出来,符合面向对象的动感。首先,它反映了“职责分开”的口径。将数据实体与其行为分别,使得两者之间依赖收缩,当数码作为暴发改变时,并不影响Model模块中的数据实体对象,避免了因一个类职责过多、过大,从而致使该类的引用者暴发“灾难性”的震慑。其次,它呈现了“抽象”的神气,或者说是“面向接口编程”的最佳显示。抽象的接口模块IDAL,与具象的数据库访问实现完全割裂。这种与实现无关的规划,保证了系统的可扩充性,同时也确保了数据库的可移植性。在PetShop中,可以支撑SQL
Server和Oracle,那么它们具体的落实就分别放在多少个不等的模块SQLServerDAL、OracleDAL中。

以Order为例,在SQLServerDAL、OracleDAL六个模块中,有不同的实现,但它们同时又都实现了IOrder接口,如图: 

图片 3

从数据库的实现来看,PetShop展示出了未曾ORM框架的交汇与丑陋。由于要对数据表举行Insert和Select操作,以SQL
Server为例,就使用了SqlCommand,SqlParameter,SqlDataReader等目的,以完成这多少个操作。尤其复杂的是Parameter的传递,在PetShop中,使用了汪洋的字符串常量来保存参数的名目。此外,PetShop还专门为SQL
Server和Oracle提供了纸上谈兵的Helper类,包装了部分常用的操作,如ExecuteNonQuery、ExecuteReader等办法。

在尚未ORM的图景下,使用Helper类是一个相比好的政策,利用它来成功数据库基本操作的包装,可以减弱过多和数据库操作有关的代码,这反映了目标复用的标准化。PetShop将这些Helper类统一置于DBUtility模块中,不同数据库的Helper类显露的章程基本相同,只除了部分特其它要求,例如Oracle中处理bool类型的艺术就和SQL
Server不同,从而专门提供了OraBit和OraBool方法。其余,Helper类中的方法均为static方法,以利于调用。OracleHelper的类图如下: 

图片 4

对此数据访问层来说,最头疼的是SQL语句的拍卖。在早期的CS结构中,由于未使用三层式架构设计,数据访问层和事务逻辑层是紧紧糅合在一齐的,因而,SQL语句遍布与系统的每一个角落。这给程序的珍惜带来巨大的紧巴巴。另外,由于Oracle使用的是PL-SQL,而SQL
Server和Sybase等应用的是T-SQL,两者尽管都按照了业内SQL的语法,但在不少细节上仍有分别,倘若将SQL语句大量的利用到程序中,无疑为可能的数据库移植也带动了窘迫。

最好的法门是使用储存过程。这种措施使得程序更加整洁,其它,由于存储过程可以以数据库脚本的款型存在,也有利移植和修改。但这种办法仍然有弱点。一是储存过程的测试相对困难。尽管有相应的调节工具,但比起对代码的调试而言,如故相比复杂且不便利。二是对系统的翻新带来阻力。假使数据库访问是由程序完成,在.Net平台下,咱们仅需要在改动程序后,将再也编译的次序集xcopy到布置的服务器上即可。假设应用了仓储过程,出于安全的设想,必须有特意的DBA重新运行存储过程的本子,部署的模式受到了限制。

自身已经在一个门类中,利用一个特地的表来存放SQL语句。如要使用相关的SQL语句,就利用重大字搜索得到对应语句。这种做法近似于存储过程的调用,但却防止了安排上的问题。可是那种方法却在性质上不可能得到保险。它仅符合于SQL语句较少的场地。但是,利用得天独厚的统筹,咱们得以为各个业务提供不同的表来存放SQL语句。同样的道理,这个SQL语句也足以存放到XML文件中,更有益系统的扩展或改动。然则前提是,我们需要为它提供特其它SQL语句管理工具。

SQL语句的采取无法制止,怎么着更好的使用SQL语句也无定论,但有一个标准化值得大家坚守,就是“应该尽可能让SQL语句尽存在于数量访问层的具体落实中”。

理所当然,倘使采纳ORM,那么任何就变得不同了。因为ORM框架已经为数据访问提供了大旨的Select,Insert,Update和Delete操作了。例如在NHibernate中,大家得以直接调用ISession对象的Save方法,来Insert(或者说是Create)一个多少实体对象:
public void Insert(OrderInfo order)
{
    ISession s = Sessions.GetSession();
    ITransaction trans = null;
    try
    {
    trans = s.BeginTransaction();
      s.Save( order);
      trans.Commit();
    }
    finally
    {
      s.Close();
    }
}

未曾SQL语句,也没有那多少个烦人的Parameters,甚至不需要特别去考虑工作。此外,这样的筹划,也是与数据库无关的,NHibernate可以透过Dialect(方言)的编制匡助不同的数据库。唯一要做的是,大家需要为OrderInfo定义hbm文件。

当然,ORM框架并非是文武双全的,面对纷繁复杂的工作逻辑,它并不可以一心扑灭SQL语句,以及替代复杂的数据库访问逻辑,但它却很好的反映了“80/20(或90/10)法则”(也被号称“帕累托法则”),也就是说:花相比较少(10%-20%)的力气就可以解决大部分(80%-90%)的题目,而要解决剩下的少部分问题则需要多得多的大力。至少,这一个在多少访问层中占据了多方面的CRUD操作,通过运用ORM框架,我们就仅需要付出极少数日子和精力来解决它们了。这无疑裁减了整套项目支付的周期。

或者回到对PetShop的钻探上来。现在大家早已有了数据实体,数据对象的空洞接口和实现,可以说关于数据库访问的焦点就曾经形成了。留待我们的还有两个问题需要解决:
1、数据对象创设的军事管制
2、利于数据库的移植

在PetShop中,要创立的数据对象包括Order,Product,Category,Inventory,Item。在后面的筹划中,这些目标已经被架空为对应的接口,而其实现则遵照数据库的不比而有所不同。也就是说,创设的对象有多种品类,而每体系型又有两样的落实,这是独立的纸上谈兵工厂情势的施用场景。而地方所述的七个问题,也都得以经过架空工厂形式来缓解。标准的架空工厂格局类图如下: 

图片 5

譬如说,成立SQL Server的Order对象如下:
PetShopFactory factory = new SQLServerFactory();
IOrder = factory.CreateOrder();

要考虑到数据库的可移植性,则factory必须作为一个全局变量,并在主程序运行时被实例化。但这么的计划性即便曾经达标了“封装变化”的目的,但在开创PetShopFactory对象时,仍不可防止的产出了切实的类SQLServerFactory,也即是说,程序在这些局面上发出了与SQLServerFactory的强看重。一旦整个系统要求协助Oracle,那么还索要修改这行代码为:
PetShopFactory factory = new OracleFactory();

修改代码的这种作为明确是不足接受的。解决的点子是“倚重注入”。“依赖注入”的意义平时是用特另外IoC容器提供的,在Java平台下,这样的容器包括Spring,PicoContainer等。而在.Net平台下,最广泛的则是Spring.Net。不过,在PetShop系统中,并不需要专门的器皿来促成“看重注入”,简单的做法依旧使用配置文件和反光效用来贯彻。也就是说,我们得以在web.config文件中,配置好实际的Factory对象的完全的类名。可是,当我们利用配置文件和反光效用时,具体工厂的缔作育显得有点“画蛇添足”了,我们一齐可以在部署文件中,直接针对具体的数据库对象实现类,例如PetShop.SQLServerDAL.IOrder。那么,抽象工厂情势中的相关工厂就足以简化为一个工厂类了,所以自己将这种情势称之为“具有简易工厂特质的抽象工厂格局”,其类图如下: 

图片 6

DataAccess类完全代替了前头成立的工厂类体系,它是一个sealed类,其中创立各样数码对象的方法,均为静态方法。之所以能用这一个类达到抽象工厂的目标,是因为安排文件和反光的施用,如下的代码片断所示:
public sealed class DataAccess
{
 // Look up the DAL implementation we should be using
    private static readonly string path =
ConfigurationManager.AppSettings[“WebDAL”];
    private static readonly string orderPath =
ConfigurationManager.AppSettings[“OrdersDAL”];

 public static PetShop.IDAL.IOrder CreateOrder()
 {
         string className = orderPath + “.Order”;
         return
(PetShop.IDAL.IOrder)Assembly.Load(orderPath).CreateInstance(className);
    }
}

在PetShop中,这种倚重配置文件和反光成立对象的不二法门最好普遍,包括IBLLStategy、CacheDependencyFactory等等。这一个实现逻辑散布于所有PetShop系统中,在我看来,是足以在此基础上拓展重构的。也就是说,大家可以为全方位连串提供类似于“ServiceLocator”的落实:
public static class ServiceLocator
{
 private static readonly string dalPath =
ConfigurationManager.AppSettings[“WebDAL”];
    private static readonly string orderPath =
ConfigurationManager.AppSettings[“OrdersDAL”];
 //……
 private static readonly string orderStategyPath =
ConfigurationManager.AppSettings[“OrderStrategyAssembly”];

 public static object LocateDALObject(string className)
 {
  string fullPath = dalPath + “.” + className;
  return Assembly.Load(dalPath).CreateInstance(fullPath);
 }
public static object LocateDALOrderObject(string className)
 {
  string fullPath = orderPath + “.” + className;
  return Assembly.Load(orderPath).CreateInstance(fullPath);
 }
public static object LocateOrderStrategyObject(string className)
 {
  string fullPath = orderStategyPath + “.” + className;
  return Assembly.Load(orderStategyPath).CreateInstance(fullPath);
 }
 //……
}

这就是说和所谓“依赖注入”相关的代码都足以运用Service(Service)Locator来形成。例如类DataAccess就足以简化为:
public sealed class DataAccess
{
 public static PetShop.IDAL.IOrder CreateOrder()
 {
         return (PetShop.IDAL.IOrder)ServiceLocator.
LocateDALOrderObject(“Order”);
    }
}

通过瑟维斯(Service)(Service)Locator,将拥有与部署文件有关的namespace值统一管理起来,这有利于各类动态创造对象的管制和前程的护卫。

相关文章