OraclePetShop数据访问层之数据库访问安顿

《解剖PetShop》体系之二

二、PetShop数据访问层之数据库访问安顿
在浩如烟海一中,我从总体上分析了PetShop的架构设计,并提及了分支的定义。从本有的发轫,我将次第对各层开展代码级的辨析,以求得到进一步缜密而长远的驾驭。在PetShop
4.0中,由于引入了ASP.Net
2.0的局地新特色,所以数据层的情节也进一步的周边和错综复杂,包蕴:数据库访问、Messaging、MemberShip、Profile四部分。在一体系二中,我将介绍有关数据库访问的布置。

在PetShop中,系统要求处理的数据库对象分为两类:一是数额实体,对应数据库中相应的数据表。它们并未作为,仅用于表现对象的数量。这个实体类都被放置Model程序集中,例如数据表Order对应的实体类OrderInfo,其类图如下: 

Oracle 1

这一个目标并不持有持久化的功能,简单地说,它们是作为数据的载体,便于工作逻辑针对相应数据表进行读/写操作。固然这几个类的质量分别映射了数据表的列,而每一个对象实例也恰好对应于数据表的每一行,但那个实体类却并不富有相应的数据库访问能力。

是因为数量访问层和工作逻辑层都将对那几个数据实体举办操作,由此先后集Model会被这两层的模块所引用。

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

Oracle 2

将数据实体与有关的数据库操作分离出来,符合面向对象的动感。首先,它反映了“职分分开”的尺码。将数据实体与其一举一动分别,使得两者之间器重缩小,当数码表现爆发变动时,并不影响Model模块中的数据实体对象,防止了因一个类任务过多、过大,从而致使该类的引用者暴发“魔难性”的熏陶。其次,它反映了“抽象”的动感,或者说是“面向接口编程”的顶级彰显。抽象的接口模块IDAL,与具象的数据库访问已毕完全割裂。那种与落到实处毫无干系的宏图,有限协助了系统的可扩张性,同时也保险了数据库的可移植性。在PetShop中,可以接济SQL
Server和Oracle,那么它们具体的落到实处就各自位居三个例外的模块SQLServerDAL、OracleDAL中。

以Order为例,在SQLServerDAL、OracleDAL四个模块中,有例外的贯彻,但它们同时又都完毕了IOrder接口,如图: 

Oracle 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的类图如下: 

Oracle 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。在后边的陈设中,那几个目的已经被架空为对应的接口,而其达成则按照数据库的例外而有所差距。也就是说,创设的目的有三体系别,而每连串型又有例外的贯彻,这是百里挑一的抽象工厂情势的采纳场景。而地点所述的五个难题,也都得以经过架空工厂情势来缓解。标准的空洞工厂方式类图如下: 

Oracle 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。那么,抽象工厂形式中的相关工厂就足以简化为一个厂子类了,所以自己将那种形式称之为“具有简易工厂特质的架空工厂方式”,其类图如下: 

Oracle 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);
 }
 //……
}

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

经过ServiceLocator,将所有与配置文件有关的namespace值统一保管起来,那有利于各样动态制造对象的田间管理和前途的护卫。

相关文章