读《企业应用架构模式》第三章 映射到关系数据库

映射到关系数据库:

   数据源层的作用是与应用需要的基础设施的不同部分进行通信,问题主要是和数据库的会话。换句话说,是和现今系统中广泛使用的关系数据库进行会话。

架构模式:

   架构模式,它要解决的问题是驱动领域逻辑访问数据库的方式。此时的选择对于设计影响深远而且难以重构。许多应用程序开发者对SQL还是缺乏充分理解,特别是刚出道的研发者,因此,不能很好地构造有效的查询语句和命令。例外,DBA也希望能得到访问数据表的SQL语句,使得能最好的调整和组织索引。

   基于上述原因,把SQL访问从领域逻辑中分离出来,把它放到独立的类中,让它们以数据库中的表结构为基础,每个数据库表对应一个类。这些类为数据表建立了一个入口,一般我们叫做“表数据入口。应用程序的其他部分根本不需要了解任何与SQL有关的事情,而且很容易就能找到所有访问数据库的SQL。

   除了上面的“表数据入口”。例外,通过查询语句而返回的每一行也会产生一个它的实例。该实例入口,一般我们叫做“行数据入口,这种行数据入口就像是用面向对象的方式来看待数据。

   记录集,是表和数据行的一种通用数据结构,用来模拟数据库的表格式属性。因为记录集是一种通用数据结构,环境可以在应用程序的很多地方使用它。如果使用记录集,对数据库中的每个表,仅仅需求一个对象来管理。查询数据库的方法,返回一个记录集。(个人观点,在我看来记录集就是查询数据的返回结果集。)

    表数据入口与记录集非常匹配,这使得它们成为使用表模块的当然选择。它也是一个组织存储过程的模式。很多设计者都喜欢通过存储过程来完成所有的数据库访问,而不是直接使用SQL语句。在这种情况下,可以考虑把存储过程的集合看出是为一个表定义的表数据入口。可能还有一个内存中的表数据入口来包装对存储过程的调用,这样就可以把存储过程的调用机制封装起来。

    在简单应用中,领域模型是一种和数据库结构相当一致的简单结构,对于每个数据库表都有一个领域类。这种领域对象的业务逻辑复杂度通常适中。这种情况下,每个领域对象都自己负责数据库的存储过程,这就是活动记录

    从另一个角度来考虑活动记录,就是从行数据入口开始,然后把领域逻辑加入到类中,特别是从多个事务脚本中发现了重复代码的时候。在这种情况下,入口增加的间接性提供的价值不大。随着领域逻辑变得更加复杂,它就慢慢转变成一个大的领域模型,简单的活动记录开始不能满足要求,领域类与表的一对一匹配也开始随着把领域逻辑放入更小的类而失效。关系数据库无法处理继承,因此使用策略模式和其他轻巧的面向对象模式非常困难。

    所有这些都迫使你随着领域模型的增大而采用间接的方式。入口虽然可以解决一些问题,但它仍然将数据库方案和领域模型耦合在一起。结果就会有一些从入口域到领域对象域的转换,这会使领域对象变得复杂。

    将领域模型和数据库完全独立,可以让间接层完成领域对象和数据库表之间的映射。这个数据映射器处理数据库和领域模型之间的所有的存储操作,并且允许双方都能独立变化。这是数据库映射架构中最复杂的架构,但它的好处是把两个层完全独立了。如果领域逻辑非常简单并且类和表十分一致,使用活动记录就可以了,如果领域逻辑比较复杂,数据映射器才是需要的。

行为对象:

   在说到O/R映射时,人们通常会关注结构方面如何把表和对象联系起来。所谓行为问题,就是如何让各个对象从数据库中读取出来以及存到数据库中。粗略看起来,活动记录可以解决这个问题,如果加载了一些对象到内存并且进行了修改,就必须跟踪每个修改过的对象,并保证把他们写回到数据库中。如果数据量少,很容易。一旦数据量多了,这就不再容易了。尤其是在创建了某些行的同时,还修改了其他行的时候,这是由于在修改引用它们的行之前要获得这些新建行的主键。

    因为要读取对象并修改它们,所以就必须保证正在使用的数据库状态的一致性。如果读取了某些对象,重要的是要保证读取必须是独占的,也就是说,没有其他进程在读取的同时修改这些对象。否则,就可能在对象中得到不一致或无效的数据,这就是同步问题。

    有一种专门解决上述问题的模式就是工作单元。工作单元会跟踪所有从数据库中读取的对象以及以任何形式修改过的对象。它同样负责将更新提交到数据库。应用程序的编程人员将工作交托给工作单元,而不是直接调用明确的保存方法。工作单元排列好对数据库的操作顺序,把所有复杂的提交处理放在一起。当与数据库的交互动作比较复杂的时候,工作单元是一个必要的模式。

   可以这样理解工作单元,它是一个对象,充当数据库映射的控制器。在没有工作单元的情况下,一般都是有领域层充当控制器,决定何时读写数据库。工作单元就是来源于把数据库映射控制器的行为分解到它自己的对象中。

   但是在加载对象时,需要注意的是必须小心避免把同一个对象加载两次,否则,在内存中就有两个对象和同一个数据库行对应。对他们都进行更新就会乱套了。为了解决这个问题,可以在标识映射里记录读取的每一行。每次读入数据时,必须到标识映射里去检查一下是否已经存在了。如果该数据已经加载,可以返回一个对它的引用。这样所有更新操作就可以正确的组织好了。还可以得到一些好处,比如可能避免一些数据库调用,因为标识映射就像一个数据库高速缓存。标识映射的主要目的是保存一致性,而不是提高性能。

    如果使用了领域模型,就必须要合理安排,使得关联的对象一起加载,例如:在读取一个对象的时候,是否需要读取出所有与之关联的对象,关联读取的对象过多,会导致效率低下,为了避免这种低效,必须设法减少带出来的东西,当然,还需要保存接口以便在以后需要的时候再来取。延迟加载的主要思想是拥有一个对象引用的占位符,可以采用几种方法,但他们的共同点都是拥有被修改对象的对象引用,它指向是一个占位符而不是实际的对象。当且仅当想到通过链接访问的时候,才会真的去数据库中读取实际的对象。适当使用延迟加载能使每次数据库调用取得刚好够用的数据。

读取数据:

   在读取数据时,可以把读取数据的方法看作一个查找器,它通过一个方法结构的接口来隐藏SQL查询语句。在什么地方放置查找器是由使用的接口模式决定的,如果数据库交互类是基于表的(表数据入口),也就是说对于数据库中的每个表都有一个类的实例与之对应,那么就把插入和更新操作也捆绑在查找器方法中。如果交互类是基于数据行的(行数据入口),也就是说对于数据库的每一行都有一个类的实例与之对应,这种情况就不行了。

    用基于行的类操作数据库时,最好是创建独立的查找器对象。每一个查找器类都有很多封装了SQL语句的方法。当执行查询操作时,查找器对象返回一个适当的基于行的对象集合。

    使用查找器方法时,需要注意的是:这些查找器方法工作在数据库状态下面而不是对象状态下。换句话说:就是你的查询只针对数据库,而不是针对内存中的对象。

读取数据的时候,为了不影响性能,通常会遵循以下几条经验:

1、尽量一次读回多行,最好不要为了得到多行而在同一个表上重复查询。

2、另一个避免多次进入数据库的方式是使用联接(Join),可以通过一次查询返回多个表。加快查询速度,建立一个入口来得到相互联接的表数据,或者通过一个数据映射器用一次调用加载多个领域对象。

3、如果正在使用联接操作,记住数据库必须优化到在一次查询中处理3-4个联接。一旦超过这个范围,将会带来性能损失,尽快可以通过缓存视图来重新装入大部分数据。




























你可能感兴趣的:(读《企业应用架构模式》第三章 映射到关系数据库)