6.数据访问层设计

  1. 概述

数据访问层负责与数据库存储设备打交道,为业务层提供数据服务。

一般来说,dal包含了对数据的增、删、改、查四种类型的操作,同时也包含着事务的管理、数据并发处理、查询解析机制等相关逻辑。

  1. 数据访问层设计策略

2.1仓储模式

 

Repsoitory协调领域和数据映射层,起到了类似内存中业务对象集合的作用。

业务层与底层数据存储机制进行了分离,业务层只是知道调用相应的respositoty就可以实现数据库操作,它并不关心采用的是什么数据访问技术和存储介质,以及底层数据模型的结构差异是怎样的。

2.2数据访问对象模式

 

数据访问对象(Data Access Object,DAO)模式是一种简单的将数据访问层与系统其它部分相隔离的设计方式。DAO和Respository很类似,就像我们初次学习Domain Model模式和ActiveRecord模式一样,但是Dao没有隐藏数据底层的相关信息,而且数据库中的每一个表都存在一个DAO。

  1. 数据访问层常用的模式与原则

3.1工作单元

 

维护受业务事务影响的对象列表,并且协调变化的写入和解决并发问题。

工作单元就是记录对象数据变化的对象,只要开始做一些可能对所要记录的对象的数据有影响的操作,就会创建一个工作单元去记录这些变化,所以每当创建、修改或者删除一个对象的时候,就会通知工作单元。

工作单元的关键就是在提交操作中由它决定要做什么:打开一个系统事务,做并发检查,最后向数据库写入所做的修改。要做到这一点,工作单元需要知道它应该记录那些对象,以及如何记录。有三种方式可以实现此功能:调用者注册、对象自身注册、respository注册。

3.1.1调用者注册

 

每当用户改变某个对象时,必须将改变的对象注册到工作单元中。任何没有注册的对象提交时都不会写入数据库。

3.1.2对象自身注册

 

如果采用对象自身注册的方式,调用者就不再负责注册对象了,而是将注册的方法置于对象本身的方法中。

一般做法如下:

  1. 将从数据库加载的对象注册为"干净"的对象。
  2. 在对象属性的set或其他修改对象值得方法中将对象注册为"脏"对象。
  3. 在提交工作单元的时候,将会检查所有提交的对象,并将"脏"的数据提交上去。

注意事项:

工作单元要么传递给对象,要么被放在一个总所周知、公共的地方。

对象的开发者必须记得在适当的地方加上注册调用代码。

3.1.3respository注册方式

 

Respositoty就充当工作单元的角色。业务对象在获取数据的时候总会获取调用respository的方法,例如add,此时repositoty不会立即将业务对象数据保存到介质中,而是将对象保存到一个内存的additemlist中;如果调用delete方法,那么对象就保存到deleteitemlist中,最后调用respositoty的commit方法,将additemlsit和deleteitemlist中的数据作为一个事务提交。

工作单元不仅能作用于数据库资源,还可以作用于任何事物资源,同时又可以用它来调整消息队列和事务监控。

3.2标识映射

 

标识映射(Identity Map)就是通过在映射中保存每个已经加载的对象,从而确保每个对象只被加载一次。当要访问对象时,可以通过映射来查找它们。

3.3延迟加载

 

延迟加载表示直到需要的时候才去加载所需要的资源。

实现延迟加载的方式有四种:延迟初始化、虚代理、值保持和重影。

 

虚代理看起来应该是我们需要的对象,但是实际上不包含任何东西,只有在调用它的方法时,它才会去加载恰当的对象。

代理的几种方式:

远程代理:为一个位于不同地址空间的对象提供一个局域代表对象。

虚代理:根据需要创建一个资源消耗较大的对象,也就是说此对象只有在需要的时候才会被真正创建出来。

Copy-on-write:虚拟代理的一种。把复制拖延到只有在客户端需要时才真正采取行动。

保护访问代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。保护代理的好处就是它可以在运行时对用户的有关权限进行检查,并在核实后决定是否将调用传递给被代理的对象。

Cache代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。

防火墙代理:保护目标,不让恶意用户接近。

同步化代理:使几个用户能够同时使用一个对象而没有冲突。

智能引用代理:当一个对象被引用时,提供一些额外的操作。

3.4数据并发控制

 

比较典型的解决方案有两种:隔离和识别不变性。

所谓的"隔离",就是划分数据。通过把数据划分成很多的小片数据,来让每一个分片都只能被一个执行单元访问。隔离室减少错误发生几率的一种很有效的技术,通过这种方法来安排资源,当程序进入隔离区时就无需考虑并发问题了。一个好的并发设计应该是:找到各种创建隔离区的办法,并且保证在每个隔离区里面完成尽可能多的任务。

只有在共享数据可以被修改的时候,才会产生并发问题。所以另一个避免并发冲突的办法就是标识出那些数据是不变的。当然,让所有的数据都不变是不可能的,因为许多系统本来就要对很多数据进行修改。但是通过定义某些数据为不变,或者至少几乎不变,或者变得频率很低,就可以不用总考虑这些数据的并发性问题了。这就是所谓的"识别不变性"。

当有些可变的数据无法隔离时,可以采用两种形式的并发控制策略:乐观并发控制和悲观并发控制。乐观并发控制允许多个用户同时对同一个数据进行修改,最后提交的修改被采纳。悲观并发控制每次只允许一个用户对数据进行修改,与此同时其它多个用户对数据享有"读"的权限。

这两种策略各有优缺点。悲观并发控制降低了系统并发操作发生的频繁程度。例如:当用户A对某数据进行加锁修改的时候,其它想修改该数据的用户就只能等待了。乐观并发控制则自有一些,多个用户可以同时修改数据,只是在提交数据的时候会出现一些阻碍,因为很可能你提交的修改已经被其他用户覆盖了。

使用那种控制的标准是:根据冲突的频率与严重性来决定。如果冲突很少,或者冲突的后果不严重,那么通常情况下应该选择乐观并发控制,因为它能够得到更好的并发性,而且实现起来也比较容易。

一般来说,乐观并发控制和悲观并发控制都是将数据的冲突检测建立在数据的某种版本标识上的,可能是时间戳,也可能是顺序计数器。对于悲观并发控制,在提交修改数据时,先比较此数据在数据源中的版本标识是否和提交的数据的标识一样,如果一样,那么将数据进行保存,否则,拒绝保存数据。

3.5查询对象

 

查询对象(query object)描述的是一次数据库的查询。查询对象是解释器模式在sql查询上的应用。它使得客户程序可以采用面向业务对象语言而不是数据库语言来描述查询。

只有使用领域模型和数据映射器时,才会真正的需要查询对象。

4.ORM对象关系映射

 

Entity framework是微软在.net平台下的一个重量级的ORM。EF不仅仅能够在关系模型存储介质和业务模型之间进行映射,也能在非关系数据存储介质和业务模型之间进行映射。

如下图所示:

posted @ 2016-06-09 22:13  常想一二,不思八九  阅读(3110)  评论(0编辑  收藏  举报