很多的JEE应用程序需要使用持久性数据(数据库文件等)不同的程序持久性存储是各不相同的并且用来访问这些不同的持久性存储机制的API也有很大的不同如果应用程序要在不同的持久性存储间迁移这些访问特定持久存储层的代码将面临重写 如何解决这个问题?且看DAO模式 数据访问对象(Data Acess Object) 模式 一环境 根据数据源不同数据访问也不同根据存储的类型(关系数据库面向对象数据库文件等等)和供应商实现不同持久性存储(比如数据库)的访问差别也很大 二问题 许多真是的JEE应用程序需要在一定程度上使用持久性数据对于许多应用程序持久性存储是使用不同的机制实现的并且用来访问这些不同的持久性存储机制的API也有很大的不同 比如应用程序使用实体bean(这里应该是指BMP的beanCMP的bean已大大降低了与RDBMS的耦合)的分布式组件来表示持久性数据或者使用JDBC API来访问驻留在某关系数据库管理系统(RDBMS)中的数据这些组件中包含连接性性和数据访问代码会引入这些组件与数据源实现之间的紧密耦合组件中这类代码依赖性使应用程序从某种数据源迁移到其他种类的数据源将变得非常麻烦和困难当数据源变化时组件也需要改变以便于能够处理新类型的数据源 (举个例子来说我们UPTEL系统是使用JDBC API对 ORACLE数据库进行连接和数据访问的这些JDBC API与SQL语句散布在系统中当我们需要将UPTEL迁移到其他RDBMS时比如曾经迁移到INFORMIX就面临重写数据库连接和访问数据的模块) 三作用力 诸如bean管理的实体bean会话beanservlet等组件往往需要从持久性存储数据源中检索数据以及进行数据存储等操作 根据产品供应商的不同持久性存储API差别也很大这些API和其能力同样根据存储的类型不同也有差别这样存在以下缺点即访问这些独立系统的API很不统一 组件需要透明于实际的持久性存储或者数据源实现以便于提供到不同供应商产品不同存储类型和不同数据源类型的更容易的移植性 四解决方案 使用数据访问对象(DAO)模式来抽象和封装所有对数据源的访问DAO管理着与数据源的连接以便检索和存储数据 DAO实现了用来操作数据源的访问机制数据源可以时RDBMSLDAPFile等依赖于DAO的业务组件为其客户端使用DAO提供更简单的接口DAO完全向客户端隐藏了数据源实现细节由于当低层数据源实现变化时DAO向客户端提供的接口不会变化所有该模式允许DAO调整到不同的存储模式而不会影响其客户端或者业务组件重要的是DAO充当组件和数据源之间的适配器 (按照这个理论如果我们UPTEL系统使用了DAO模式就可以无缝的从ORACLE迁移到任何一个RDBMS了梦想总是很完美的且看看DAO模式如何实现) 结构图是表示DAO模式中各种关系的类图 此主题相关图片如下 参与者和职责 )BusinessObject(业务对象) 代表数据客户端正是该对象需要访问数据源以获取和存储数据 )DataAccessObject(数据访问对象) 是该模式的主要对象DataAccessObject抽取该BusinessObject的低层数据访问实现以保证对数据源的透明访问BusinessObject也可以把数据加载和存储操作委托给DataAccessObject )DataSource(数据源) 代表数据源实现数据源可以是各RDBMSR数据库OODBMSXML文件等等 )valueObject(值对象) 代表用做数据携带着的值对象DataAccessObject可以使用值对象来把数据返回给客户端 DataAccessObject也许会接受来自于客户端的数据其中这些用于更新数据源的数据存放于值对象中来传递 策略 )自动DAO代码产生策略 因为每个BusinessObject对应于一个特殊的DAO因此有可能建立BusinessObjectDAO和低层实现(比如RDBMS中的表)之间的关系(映射)一点这些关系(映射)已经建立我们就可以编写与应用程序有馆的代码生成的简单工具了(什么?自己写GP程序?用ORM的附带工具自动生成不就完了最多自己写几个Adapter牛人就是不同啥都要自己写)其中的工具可以产生该应用程序需要的所有DAO代码 如果DAO需求很复杂我们可以采用第三方工具其中这些工具提供对象到RDBMS数据库的关系映射(这里指的是前面提到的ORM工具全称是Object Relation Mapping目前成熟的ORM工具有很多:HibernateOJBTorqueTopLink等等) 这些工具通常包含GUI工具来把业务对象映射到持久性存储对象并且因而定义中间DAO一旦这些映射完成这些工具会自动地生成代码并且也许会提供其他增值功能比如结果缓沖查询缓沖与应用程序集成以及与其他第三方产品(比如分布式缓沖)地继承等等 (增值服务Torque提供了结果缓沖Hibernate提供了对Oracle数据库SQL指令的优化OJB提供JDO APIOMDB API) )数据访问对象的工厂策略 通过调整抽象工厂和工厂方法模式DAO模式可以达到很高的灵活度 当低层存储不会随着实现变化而变化时该策略可以使用工厂方法模式来实现该策略以产生应用程序需要的大量DAO图是这种情况下的类图 此主题相关图片如下 当低层存储随着实现变化而变化时该策略可以使用抽象工厂方法模式而实现 图是这种情况下的类图 此主题相关图片如下 结果 )启用透明性 业务对象可以是使用数据源而无须了解该数据源实现的具体细节访问是透明的原因是实现被隐藏在DAO的内部 )启用更容易的迁移 DAO层使应用程序更加容易地迁移到一个不同的数据库实现业务对象不了解低层数据实现因而该迁移只涉及对DAO层的变化更进一步说如果使用工厂策略则有可能为每一个低层存储实现提供一个具体工厂实现在这种情况下迁移到不同的迁移实现意味着给应用程序提供一个新的工厂实现 )减少业务对象中代码复杂度 由于DAO管理所有的数据访问复杂性它可以简化业务对象和其他使用DAO的客户端中的代码所有与实现有关的代码(比如sql语句)都被包含在DAO中而不是包含在业务对象中这样做提高了代码的可读性已经代码生产效率 )把所有的数据访问集中到一个独立的层 因为所有的数据访问操作现在被委托给DAO所有单独的数据访问层可以被看作把数据访问实现与应用程序中的其他代码相隔离的这种集中化使应用程序更容易地维护和管理 )不适用于容器管理的持久性 由于EJB容器用容器管理的持久性(CMP)来管理实体bean该容器会自动地服务所有的持久性存储访问使用容器管理的实体bean的应用程序不需要DAO层因为该应用程序服务器透明地提供该功能然而当需要组合使用CMP和BMP时DAO仍旧有用处 )添加其他层 DAO会在数据客户端和数据源之间创建其他的对象层其中该数据源需要被设计和实现以便于权衡该模式的好处但是选择本方法也会带来额外的开销 )需要类层次设计 在使用工厂策略时我们需要设计和实现具体工厂的层次以及这些工厂产生的具体产品层次如果能够确保这种灵活性则有必要考虑这种额外的工作这样做会增加设计的复杂性然而在实现该工厂策略时你可以首先考虑工厂方法模式然后再根据需要过渡到抽象工厂 六范例代码 实现数据访问对象模式 范例时表示Customer信息的持久性对象的DAO范例代码当findCustomer()被调用时CloudscapeCustomerDAO创建一个Customer值对象 范例是使用DAO的范例代码 实现数据访问对象的工厂策略 )使用工厂方法模式 )使用抽象工厂模式 范例代码是CloudscapeDAOFactory的范例代码 范例代码中的CustomerDAO接口为Customer持久性对象定义了DAO方法这些接口是被所有具体DAO实现来实现的比如CloudscapeCustomerDAOOracleCustomerDAO已经SybaseCustomerDAOAccount和OrederDAO接口也与此类似 Example Abstract DAOFactory Class // Abstract class DAO Factory public abstract class DAOFactory { // List of DAO types supported by the factory public static final int CLOUDSCAPE = ; public static final int ORACLE = ; public static final int SYBASE = ;
// There will be a method for each DAO that can be // created The concrete factories will have to // implement these methods public abstract CustomerDAO getCustomerDAO(); public abstract AccountDAO getAccountDAO(); public abstract OrderDAO getOrderDAO();
public static DAOFactory getDAOFactory( int whichFactory) { switch (whichFactory) { case CLOUDSCAPE: return new CloudscapeDAOFactory(); case ORACLE : return new OracleDAOFactory(); case SYBASE : return new SybaseDAOFactory();
default : |