系统设计 — 数据源层构建

抽取出数据源层的作用是能够将领域层需要的不同的基础设施通信的细节进行屏蔽。
现在最成功的持久化方式就是关系数据库,它成功的原因在于SQL,一个标准化的数据通信语言,尽管不同的厂商有细节上的改进,但是总体还是相同的。

分离
对于领域逻辑访问数据的方式,早期的时候我们使用JDBC,是直接将SQL写在了我们的设计代码里面,这样可能在开发过程中效率会更高,但是DBA希望也能够访问到SQL,从DB的角度来更好的调整和组织索引。
所以对于此,就很有必要将SQL和我们的领域逻辑进行剥离,放到独立的类中,每个表对应一个类,这个类就是我们访问数据库的入口。对于单条记录的获取,我们称之为行数据入口,就是一条数据就是一个对象。这样的类就是单条的获取数据!但是,我们实际开发过程中还会碰到的一种情况就是一次获得一个记录集,这种持有记录集的入口类,我们称之为表数据库入口,不再是以一种对象的方式来理解数据。
实际的开发过程中,很难见到单纯的行数据入口和或是表数据入口,一般这个入口类包含以上两种情况:既有批量的获取数据,亦有获取单条的数据。

在简单的应用中,领域模型和数据库结构是相当的一致的,一个领域对象对应一个数据库的话,那么完全可以通过领域对象直接与数据库通信,也就是说一个领域类既有领域逻辑,也有同数据库通信的逻辑。这种做法的优点是能避免代码的副本,但是随着领域逻辑的复杂性提高,领域对象已经不能单一的对应一张表的情况下就会出现问题,同时对于采用继承,多态来实现的设计模式就很难实现。

对于这个问题的解决是在领域模型和数据库入口类中间添加一个数据映射器。双方通过数据库映射器来通信,两者完全独立。对应到我们现在开发中就是DAO层。

数据源的其他功能处理
随着业务的复杂,会出现在一个领域逻辑中对应了多个不同的数据源层,可能是同一个应用的,也可能是外部应用的,对于此,就需要保证保证多个数据源层间的提交进行统一的管理,在完成所有的业务逻辑后,再持久化数据,我们将负责此类工作的类称之为工作单元,在多事务中就是一个事务管理器,事务管理的具体实现有二阶段提交,以及它的改进版,三阶段提交,具体这里就不展开了。
同理,在单个数据源中保证一个数据的并发访问的数据一致性的处理,也可以称之为一个工作单元。它是领域模型之下,数据源层之上的中间处理层。

数据结构的映射
通过领域建模,那么必然在多个领域对象之间有依赖,组合,聚合等关系,对于单项或是双向的一对一依赖,在映射到数据库的设计上就是一个外键来解决,若是一对多的话也是外键,不过一个表中多条数据对应另一个表中数据。若是多对多的话,那么一般采用的方案是在另个表之间添加中间表。
现在ibaits和hibernation帮我们解决的就是这种领域对象和数据库表之间的映射,不过它们都是通过元数据的方式来实现,如此对于通用性的代码完全可以通过反射等方式来实现。看个ibaits中的具体例子就是:
<resultMap id=”domain” class=”xxx.xxx.java”>
<result property=”id” column=”ID” />
<result property=”name” column=”Name” />
<result property=”age” column=”Age” />
</resultMap>
将数据库字段和对象中的字段通过元数据映射,然后对于具体数据库记录通过反射生成对象。

继承的映射
之前说的都是组合方式的数据库映射,例如一个对象持有单个对象,或是持有一个集合的对象,或是双向的持有等,对于继承这种类关系一般有三种方式处理。
加入A类是一个接口,B、C是A类的具体实现,D是C的继承。
1. 单表继承:就整个继承体系中的类映射到一张表张。
2. 具表继承:就是为整个体系中的所有实现类映射到一张表中,也就是说现在会有B、C、D三张表存在
3. 类表继承:为整个体系中的所有类各自映射到一张表中,也就是说会有A、B、C、D四张表存在。

以上三种方式中,类表继承是类和表之间最简单的关系,但是代价需要通过多张表的join才能获取一个对象或是多次的查询来获得一个对象,对于性能损耗太大。因为不同表存了一个对象的不同属性。
具表继承避免了join,允许从一个表中获取一个对象,但是改变起来很难,若是对于超类的任何改变都不得不改变它对应的子表数据,同时若类继承结构改变的话,也会是很大的困扰的。
单表解决了以上两种的问题,但是带来的就是空间的浪费,越高层次的类,空间浪费越严重。

作者: inter12

在这苦短的人生中,追求点自己的简单快乐

发表评论

电子邮件地址不会被公开。 必填项已用*标注