传智播客培训班学员交流专区,本群只讨论技术问题和针对培训课程内容进行答疑,也欢迎大家贡献自己每天的培训日记和学习心得。
核心J2EE模式 —— 数据访问对象(DAO)
(原文翻译)
一、背景
数据的访问因数据源的不同而异。访问持久化的数据的方式因存储类型(关系型数据库、面向对象数据库、普通文件等)和开发商的实现方式不同也会有很大差异。
二、问题
许多真实的J2EE应用程序都或多或少的需要将数据持久化。对很多应用而言,他们所使用的持久化存储(例如数据库)有着不同的实现机制,因此其所使用的用于访问持久化数据的API就有着显著的差异。还有些应用程序可能需要访问存储于不同系统中的数据。比如,数据可能存储于大型机系统或者LDAP
repositories中。另外一个例子是,数据可能是来自外部系统提供的服务,比如B2B集成系统、信用卡发卡行等。
通常,应用程序通过像实体bean这样的“共享分布式组件”来表示持久化数据。当实体bean显式的访问持久化数据,也就是说在实体bean中写入了直接访问持久数据的代码的时候,我们就认为这个应用程序使用了“由bean管理的”持久层。而一个需求更简单一点的程序可能会跳过实体bean,转而使用会话bean或servlet去直接访问持久化存储来读取、修改数据。或者,应用程序可能会结合使用实体bean和由容器管理的持久层,从而让容器去处理事务和持久化的细节。
应用程序可以使用JDBC API来访问存储于RDBMS(关系型数据库管理系统)中的数据。JDBC
API支持以标准方式访问和操纵持久层(例如关系型数据库)中的数据,也支持J2EE应用程序通过SQL语句这种标准方式访问RDBMS数据表。然而,即使是在一个RDBMS内部,SQL语句的实际语法和格式也根据不同的数据库产品而有所不同。
不同类型的持久化存储之间的差异就更大了。在RDBMS、OODB、普通文件等不同类型的持久化数据解决方案之间,他们的访问机制、所支持的API、功能特性等都有所不同。需要从legacy
system 或 disparate
system中访问数据的应用程序还经常会用到不开放源码的API。这些截然不同的API为应用程序带来挑战,并有在程序代码和数据访问代码之间造成直接依赖的潜在可能。当业务组件——实体bean、会话bean,或者表示组件——servlet、JSP页面的helper对象等需要访问数据源的时候,他们可以使用合适的API来建立连接并操纵数据源。但是,把负责连接和操纵数据的代码写入这些组件,就造成这些组件和数据源之间的紧耦合。像这种存在于组件之间的数据依赖关系,使得将应用程序从一类数据源迁移到另一类数据源的工作既困难又乏味。当数据源改变了的时候,组件就需要做相应的变化,来处理新的数据源类型。
三、改变的动力
1、由bean管理的实体bean、会话bean、servlet以及其他对象(比如JSP页面的helpers)需要从持久化存储中或其他数据源中(如legacy
systems, B2B, LDAP)访问、存储数据。
2、持久化存储的API依其开发商的不同而不同。其他类型的数据源可能有非标准的或是有版权限制的API。这些API及其兼容性也受到RDBMS、OODBMS、XML文档、普通文件等不同存储方式的制约。我们缺乏一种通用的API来满足访问各种完全不同的数据存储系统的需求。
3、通常,组件使用受版权限制(未开放源码)的API来访问外部数据和 / 或legacy systems,以获得和存储数据。
4、在组件中包含特定的访问机制和API,使得组件的可移植性受到直接影响。
5、对实际的持久化存储和具体的数据源实现而言,组件应当是透明的,以提供一种简易的、在不同厂商的产品之间、不同的存储类型之间、以及不同的数据源之间迁移的方式。
四、解决方案
使用数据访问对象(DAO)来抽象、封装所有对数据源的访问。DAO负责管理与数据源的连接,以及获取和存储数据。
DAO实现了读写数据源所需的访问机制。数据源可以是RDBMS、B2B交换、LDAP或CORBA互联网IIOP协议等等。DAO为用户提供一组简单化的接口,而依赖于DAO的业务组件就可以去使用它。DAO完全对用户隐藏了数据源的细节。
因为即使底层的数据源变化了,DAO为用户提供的接口也不会变化,所以这种模式允许DAO在不影响用户程序的前提下,去适应不同的存储方案。本质上说,DAO扮演的,是一个介于组件和数据源之间的适配器(adapter)。
结构
图9.1是表示DAO模式中类之间的关系的类图。
图9.1 – 数据访问对象
参与者及其职责
图9.2是表示此模式中不同参与者之间的交互关系的时序图。
图9.2 – 数据访问对象时序图
业务对象(Business Object)
业务对象代表客户端。它就是那个有数据访问需求的对象。一个业务对象可能以会话bean、实体bean或其他java对象的形式实现。
数据访问对象(DataAccessObject)
数据访问对象DAO是这个模式中的主要对象。对上面提到的业务对象而言,DAO将底层的数据访问细节抽象出来,以实现对数据访问的透明性。同时,业务对象也把数据装载和存储的操作委托给了DAO。
数据源(DataSource)
这代表具体的数据源实现。一个数据源可以是RDBMS、OODBMS、XML文档、普通文件等类型的数据库。也可以是其他系统或服务,比如legacy/mainframe、B2B服务或信用卡数据库、轻量级目录访问协议。
传递对象(Transfer Object)
它代表用于承载数据的传递对象(有点像java bean)。DAO可以使用传递对象向客户程序返回数据,也可以从客户端取得数据,以更新数据源中的数据。
策略
一、自动产生DAO代码的策略
既然每个业务对象对应一个特定的DAO,那就有可能在业务对象、DAO和底层数据库之间建立一种联系。一旦这种联系建立好了,就可以写一个简单的、面向特定的应用程序的代码生成工具,以产生该程序的所有DAO的代码。用于自动产生DAO的元数据可以来自于一个描述文件,这个文件由开发人员定义。或者,代码生成器可以通过自省机制,自动获得数据库的细节,并产生访问该数据库所需的DAO。如果对DAO的需求非常复杂,那么可以考虑使用提供对象-关系映射功能的第三方工具。这类工具通常包含可视化界面,以帮助用户将业务对象映射到持久存储对象,从而定义了DAO。一旦映射的操作完成,工具就可以自动生成代码,并可能提供其他附加的特性,例如结果缓存、查询缓存、与应用程序服务器集成、与其他第三方工具集成等等。
二、DAO工厂策略
通过采用“抽象工厂”以及“工厂方法模式”,DAO模式可以具有高度灵活性。
当底层存储不会从一种实现迁移到另一种实现(例如从MySQL到Oracle)时,这种策略可以通过“工厂方法模式”实现,以产生一系列程序所需的DAO。图9.3是这种情况的类图。
图9.3 使用“工厂方法”的DAO工厂
当底层存储可能会从一个实现迁移到另一个实现的时候,可以通过“抽象工厂模式”来实现这一策略。正如《设计模式:可复用面向对象软件的元素》一书中所述,“抽象工厂”可以建立并使用“工厂方法”。这种情况下,此策略提供一个抽象的DAO工厂对象,它可以构造出不同类型的实体DAO工厂,每种工厂支持一种不同类型的持久化存储实现方案。一旦你获得了一个面向具体实现(例如MySQL)的实体DAO工厂,你就可以使用它产生出支持该实现的DAO。
图9.4是这种策略的类图。
此类图展现了一个抽象的DAO基准工厂,不同的实体DAO工厂都去继承并实现它。客户程序获得一个具体的实体DAO工厂(比如RdbDAOFactory)之后,就可以用它产生出可以操作具体的数据库的实体DAO。例如,客户程序可以获得一个RdbDAOFactory,然后用它得到类似RbdCustomerDAO、RdbAccountDAO等等的DAO。这些DAO继承并实现了一个通用的基类(图中的DAO1和DAO2),而基类专门描述了特定业务对象所需的DAO功能(例如insert、update、delete)。每个实体DAO为其所支持业务对象负责连接到数据源、取得数据、操纵数据等等这些事情。
DAO模式及其策略的实现示例请见本章的“代码示例”一节。
图9.4 使用抽象工厂的DAO工厂策略
描述此策略的交互关系的时序图,请看图9.5。
图9.5 使用抽象工厂的DAO工厂的时序图
结果
1、实现了透明性
业务对象可以使用数据源,而无需知道关于数据源的具体实现细节。访问数据的过程是透明的,因为其实现细节隐藏在DAO内部了。
2、迁移更加简单
DAO层的存在,使得应用程序在不同的数据库之间的迁移更加容易。业务对象并不知道底层数据实现的细节,从而使得迁移的过程只是改一下DAO层。更进一步,如果使用工厂模式,就有可能为每一种底层存储实现提供一个实体工厂,在这种情况下,迁移只是意味着为应用程序生成一个新的工厂实例。
3、降低业务对象的代码复杂度
因为DAO管理了所有的数据访问细节,所以它简化了业务对象和其他使用DAO的数据客户程序。所有和具体实现相关的代码(例如SQL
Statement)都包含在DAO中,而非业务对象中。这样做,改进了代码的可读性,提高了开发效率。
4、将全部的数据访问集中在一个单独的层中
因为所有数据访问操作都委托给了DAO,所以,我们可以把这个独立出来的数据访问层视为一个用来分离程序中的数据访问操作和其他操作的分隔层。这种集中化使得应用程序更容易管理和维护。
5、对容器管理的持久层没有用处
因为EJB容器以CMP(容器管理的持久层)管理实体bean,所以容器自动为所有持久化存储的访问提供服务。既然应用程序服务器能以一种透明的方式提供这样的功能,那么,使用容器管理的实体bean的应用程序就不需要DAO层了。然而,当需要将CMP和BMP结合起来的时候,DAO仍然可以派上用场。
6、增加了一层
DAO在数据客户程序和数据源之间增加了一层。设计和实现这一层所付出的额外精力,会抵消一些它本身所带来的好处,但考虑到其所带来的优点,这样做仍是值得的。
7、需要设计类的层次结构
当使用工厂策略的时候,实体工厂的层次结构,以及这些工厂所生产的产品的层次结构都需要设计和实现。当我们非常需要考虑程序的灵活性的时候,这些额外的设计工作就必须仔细考虑。这样做会增加设计的复杂度。然而,你可以选择先从工厂方法模式入手,再在必要的时候转移到抽象工厂。
五、示例代码
数据访问对象(DAO)模式的实现
例9.4是一段DAO的代码,它用于持久化顾客信息。当CloudscapeCustomerDAO的findcustomer()方法被调用的时候,它就会创建一个顾客信息传递对象。
例9.6是一个使用DAO的示例代码。图9.6是这个例子的类图。
图9.6 DAO模式的实现
使用“工厂方法”模式实现DAO工厂的策略
想象一下,我们需要在一个数据库(比如Oracle)上,通过DAO工厂生产出很多很多个DAO。这个工厂的DAO产品是CustomerDAO、AccountDAO、OrderDAO等等。
这个例子的类图如图9.7所示。
图9.7 使用工厂方法实现DAO工厂的实现方式
DAO工厂的示例代码请见例9.2。
使用抽象工厂模式
设想一下,现在,我们要在三种不同的数据库上实现这种工厂模式。这时我们就可以采用抽象工厂模式。
此例的类图请见图9.8。这个抽象的DAO工厂类的代码请见例9.1。
此工厂可以产生CustomerDAO、AccountDAO、OrderDAO等等。这个策略使用由抽象工厂产生的实体工厂中的工厂方法。
图9.8 使用抽象工厂实现DAO工厂的策略
例9.1 抽象DAO工厂类
例9.2是CloudscapeDAOFactory的示例代码。OracleDAOFactory和SybaseDAOFactory的实现方式是类似的,只是一些细节有所不同,比如JDBC驱动、数据库连接URL、以及SQL语法的不同。 例9.2 Cloudscape的实体DAO工厂的实现
例9.3所示的CustomerDAO接口定义了用于Customer数据表的DAO中的方法,所有实体DAO都要实现这些方法,例如CloudscapeCustomerDAO、OracleCustomerDAO、SybaseCustomerDAO。类似的,AccountDAO和OrderDAO接口分别为Account和Order表的业务对象定义了方法。 例9.3 Customer表的DAO的接口
例9.4是实现了 CustomerDAO接口的CloudscapeCustomerDAO。其它DAO的实现是类似的。 例9.4 CloudscapeDAO在Customer表上的实现
例9.5是存有客户信息的数据传递对象类。DAO使用它来与客户程序之间收发信息。关于传递对象的细节,请参考“传递对象模式”。 例9.5 客户传递对象
例9.6展示了DAO工厂和DAO的使用方法。如果我们把数据库从Cloudscape该成其它的,则只需修改getDAOFactory()方法调用,已得到一个不同的方法。
例9.6 DAO和DAO工厂的使用——客户程序代码
|
【相关链接】: http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html (本网站不保证链接的安全性)
【标签】: j2ee,DAO,Data Access Object,数据访问模型
这么长,你看了么
楼上哥们这么牛,欺负我英语不好啊。。郁闷哦,,,
太长了,没看,对不起让你失望了。。。。-_-!
我更失望。。。
