选择数据访问模式,合理规划数据访问层[转]
级别: 初级
陈隽伟 (chenjunw@cn.ibm.com), 软件工程师, IBM 中国软件实验室
2005 年 2 月 01 日
本文介绍了主流的数据访问技术和典型的数据访问模式,并举例说明了如何在典型场景中选择合适的模式与技术。
在一个典型的电子商务应用中,三层架构(或是它的变形 - N 层架构)常常是架构师们的首选,这当中包括表现层,商业逻辑层和数据访问层。本文的关注点放在数据访问层,它负责和应用中的各种数据源打交道,诸如 DB2、Oracle 等关系型数据源、XML 数据以及其它种类的非关系型数据、Web 服务、各种特别的遗留系统等等,将它们整合起来,为商业逻辑层提供统一的数据服务。
在本文中,你可以了解到下面这些内容:
- 主流的数据访问技术
- 典型的数据访问模式
- 举例说明如何在典型场景中选择合适的模式与技术
这不是一篇专门介绍数据访问技术的文章,也不是一篇有关数据访问话题的完整的索引;而是抛砖引玉,超越数据访问技术这个层次,希望通过一些归纳和讨论使得读者能够更多地关注于数据访问模式,并在自己的实践中有意识地选择合适的模式,这才是本文的目的。
从数据库技术诞生到现在,已经有了数十年的历史;相应的,从最早的读写磁盘文件开始,数据访问技术也不断的推陈出新,变得越来越丰富。
在最初的日子里,不同的数据库厂商会提供不同的访问接口,开发人员需要针对每种数据库学习不同的访问技术,比如 DB2 系统的 CLI Library 或是 Oracle 的 OCI Library。再到后来,数据访问中间件的思想得到了发展,出现了最初的 ODBC(Open Database Connectivity:开放数据库连接)以及其它一些类似的访问技术。在 C 和 C++ 流行的年代里,这项技术在微软的推动下不断发展,后来又有了诸如 OLE DB(Object Link and Embedding Database)以及 ADO(ActiveX Data Object)等数据访问技术,它们直到今天还运行在世界各地成千上万的系统中。
这里,我们更为专注的是现在的主流数据访问技术,它们是:
- JDBC(Java Database Connectivity:Java 数据库连接)
依照 JDBC 规范,有四种类型的 JDBC 驱动程序体系结构:
- Type 1:这类驱动程序将 JDBC API 作为到另一个数据访问 API 的映射来实现,如开放式数据库连通性(Open Database Connectivity,ODBC)。这类驱动程序通常依赖本机库,这限制了其可移植性。JDBC-ODBC 桥驱动程序就是 Type 1 驱动程序的最常见的例子。
- Type 2:这类驱动程序部分用 JAVA 编程语言编写,部分用本机代码编写。这些驱动程序使用特定于所连接数据源的本机客户端库。同样,由于使用本机代码,所以其可移植性受到限制。
- Type 3:这类驱动程序使用纯 JAVA 客户机,并使用独立于数据库的协议与中间件服务器通信,然后中间件服务器将客户机请求传给数据源。
- Type 4:这类驱动程序是纯 JAVA,实现针对特定数据源的网络协议。客户机直接连接至数据源。 目前,有关 JDBC 最新的工业规范是 JDBC 3.0, http://www.jcp.org/en/jsr/detail?id=54 JSR54,这是在 JCP 上的规范。
在下面的链接中,你可以获取到有关 JDBC 3.0 的最新资料。
/developerworks/cn/java/j-jdbcnew/index.html JDBC 3.0 的新特性
这是 Sun 的官方网站上,这是有关 JDBC 相关技术的信息中心http://java.sun.com/products/jdbc/index.jsp
-
在主流的数据访问技术中,微软相关的数据访问技术自成一套体系。
下面简单的回顾一下微软系列的数据访问技术所走过的几个阶段:
- ODBC(Open Database Connectivity):是第一个使用SQL访问不同关系数据库的数据访问技术。使用 ODBC 应用程序能够通过同样的命令操纵不同类型的数据库,而开发人员需要做的仅仅只是针对不同的应用加入相应的 ODBC 驱动。
- DAO(Data Access Objects):不像 ODBC 那样是面向 C/C++ 程序员的,它是微软提供给 Visual Basic 开发人员的一种简单的数据访问方法,用于操纵 Access 数据库。
- RDO(Remote Data Object):在使用 DAO 访问不同的关系型数据库的时候,Jet 引擎不得不在 DAO 和 ODBC 之间进行命令的转化,导致了性能的下降,而 RDO(Remote Data Objects)的出现就顺理成章了。
- OLE DB:随着越来越多的数据以非关系型格式存储,需要一种新的架构来提供这种应用和数据源之间的无缝连接,基于COM(Component Object Model)的OLE DB应运而生了。
- ADO:基于 OLE DB 之上的 ADO 更简单、更高级、更适 合Visual Basic 程序员,同时消除了 OLE DB 的多种弊端,取而代之是微软技术发展的趋势。
- ADO.NET:微软在 .NET 框架中提出的全新的数据访问模型。
在开始设计 .NET体系架构时,微软就决定重新设计数据访问模型,以便能够完全地基于 XML 和离线计算模型。两者的区别主要有:
ADO 以 Recordset 存储,而 ADO.NET 则以 DataSet 表示。Recordset看起来更像单张数据表,如果让Recordset以多表的方式表示就必须在SQL中进行多表连接; 而DataSet可以是多个表的集合。ADO 的运作是一种在线方式,这意味着不论是浏览或更新数据都必须是实时的;而 ADO.NET 则使用离线方式,在访问数据的时候 ADO.NET 会导入并以 XML 格式维护数据的一份副本,ADO.NET 的数据库连接也只有在这段时间需要在线。
此外,由于 ADO 使用 COM 技术,这就要求所使用的数据类型必须符合 COM 规范,而 ADO.NET 基于XML 格式,数据类型更为丰富并且不需要再做 COM 编排导致的数据类型转换,从而提高了整体性能。
图 1 即描绘了 ADO 技术的体系结构:
图 1: ADO 架构
而图 2 则描绘了 ADO.NET 技术的体系结构:
图 2:ADO.NET 架构
你还可以在下面的链接中得到更多有关 ADO 和 ADO.NET 方面相关的参考资料。
你可以在这个页面上找到很多有关 ODBC 技术的链接
如果说数据访问技术是一些得心应手的兵器的话,数据访问模式就应该是各种武功秘笈了,它们才是致胜的法宝。 架构师在不同的应用场合下可能会选择不同的数据访问模式,并且还会不断地推陈出新,这里不会也不可能穷尽所有的数据访问模式,而只是列举了其中最为典型的几个。
在线访问是最基本的数据访问模式,也是在实际开发过程中最常采用的。
如图 3 所示,这种数据访问模式会占用一个数据库连接,读取数据,每个数据库操作都会通过这个连接不断地与后台的数据源进行交互。
如图4所示,DAO 模式是标准 J2EE 设计模式之一,开发人员常常用这种模式将底层数据访问操作与高层业务逻辑分离开。
一个典型的 DAO 实现通常有以下组件:
- 一个 DAO 工厂类
- 一个 DAO 接口
- 一个实现了 DAO 接口的具体类
- 数据传输对象(有时称为值对象)
这当中具体的 DAO 类包含访问特定数据源的数据的逻辑。
下面的这两篇文章给出了一个通过 DAO 模式访问 JDBC 数据源的完整的例子:
Data Access Object Implementation Write once, persist anywhere
Implementation of JDBC DAO Provider
如图 5 所示,Data Transfer Object 是经典 EJB 设计模式之一。 DTO 本身是这样一组对象或是数据的容器,它需要跨不同的进程或是网络的边界来传输数据。这类对象本身应该不包含具体的业务逻辑,并且通常这些对象内部只能进行一些诸如内部一致性检查和基本验证之类的方法,而且这些方法最好不要再调用其它的对象行为。
在具体设计这类对象( DTO )的时候,通常可以有两种选择:
1.使用编程语言内置的集合对象,它通常只需要一个类,就可以在整个应用程序中满足任何数据传输目的;而且因为几乎所有的编程语言都内置了集合类型,我们不需要再另外编写这个类的实现代码。同时,使用内置的集合对象来实现 DTO 对象的时候,客户端必须按位置序号(在简单数组的情况下)或元素名称(在键控集合的情况下)访问集合内的字段。此外,集合存储的是同一类型(通常是最基本的 Object 类型)的对象,这有时会导致在编译时碰到一些无法检测到编码错误,这是采用内置集合对象来实现 DTO 对象的缺点。
2.通过创建自定义类来实现 DTO 对象,通过定义显式的 get 或是 set 方法来访问数据。这种方式的主要优点在于能够提供与任何其他对象完全一样的、客户端应用程序可访问的强类型对象,这种对象可以提供编译时的类型检查;但是主要缺点在于增加了编码的工作量,因为如果应用程序发出许多远程调用的话,我们需要自己编写大量的调用代码。
具体实现中有许多方法试图将上述这两种方法的优点结合在一起。第一种方法是代码生成技术,该技术可以生成脱离现有元数据(如可扩展标记语言 ( XML ) 架构)的自定义 DTO 类的源代码;第二种方法是提供更强大的集合,尽管它也是平台内置的一般的集合,但它将关系和数据类型信息与原始数据存储在一起,比如 IBM 提出的 SDO 技术或是微软 ADO.NET 中的 DataSet 就支持这类方法。
无论采用上述的哪种方法,当有了 DTO 对象之后,就需要用数据来填充它。在大多数情况下,DTO 内部填充的数据往往来自于多个其它种类的对象;由于 DTO 对象中很少有具体的数据操作方法,因此它很难从其它对象中直接提取数据。这种设计是有道理的,因为如果不让 DTO 对象知道如何调用其它对象的方法,我们就可以在不同的场合直接重用 DTO 对象,这样一旦其它对象发生更改,我们无需修改 DTO 对象的设计。
这个 BLOG 上有对 DTO 模式更详细的一些讨论。
这篇文章给出了 .NET 框架下对 DTO 模式的实现的例子
离线数据模式是这样一种数据访问模式:
- 以数据为中心:数据从数据源获取之后,将按照某种预定义的结构(这种结构可以是 SDO 中的 Data 图表结构,也同样可以是 ADO.NET 中的关系结构)存放在系统中,成为应用的中心。
- 离线:对数据的各种操作独立于各种与后台数据源之间的连接或是事务。
- 与 XML 集成:离线数据集所维护的数据可以方便地与 XML 格式的文档之间互相转换。
- 独立于数据源:离线数据模式的不同实现定义了数据的各异的存放结构和规则,这些都是独立于具体的某种数据源的。
现在,离线数据访问已经在不少产品中有了实现。
-
WebSphere 平台中的实现 - SDO 从 WebSphere Application Server V5.1 和 WSAD V5.1.1 开始,WebSphere 平台就对离线数据模式有了初步的支持,只是那个时候是称作 WDO,现在在 WebSphere Application Server V6 中有了更充分的支持,而且它不光能够支持访问关系型数据源,还对 XML 以及其他类型的数据源的访问提供支持。
JSR235 for Service Data Object 这里可以找到 SDO对应的业界规范的描述。
这篇文章还是介绍 WDO(SDO 的 beta 版) 的文章,不过其基本原理并没有太大的变化。
-
在 JDBC v3.0 规范中支持的 CachedRowSet 已经部分地提供了离线数据模式的支持,下面这些链接给出了一些应用的例子:
Using CachedRowSet to transfer JDBC Query result between classes
- 除此之外,微软 .NET 框架下的 ADO/ADO.NET 技术也对离线数据模式提供了很好地实现: http://www.microsoft.com/china/MSDN/library/data/dataAccess/USdnhcvb04vb04h4.mspx
数据访问模式5: 对象/关系映射(O/R Mapping: Object/Relation Mapping)
这是另外一个非常热门的话题,在最近几年,采用OR 映射的指导思想来进行数据持久层的设计似乎已经成了一种潮流,也因此出现了各种不同的潜在的工业规范和使用工具,这当中属 JDO、Hibernate 等最为有名。
对象/关系映射的基本思想是来源于这样一种现实:大多数应用中的数据都是依据关系模型存储在关系型数据库中;而很多应用程序中的数据在开发或是运行时则是以对象的形式组织起来的。那么对象/关系映射就提供了这样一种工具或是平台,能够帮助将应用程序中的数据转换成关系型数据库中的记录;或是将关系数据库中的记录转换成应用程序中代码便于操作的对象。
你可以在下面的一些链接中找到进一步的资料,相关的工具和开源项目很多,但在设计上却都有异曲同工之处,因此我们更需要关注地是它们在数据访问层的设计思想。
http://blog.aspcool.com/tim/archive/2004/09/16/1097.html 飞鹰的 BLOG 中有不少文章对 O/R Mapping 作了很好地分析和总结。
http://www.microsoft.com/china/msdn/archives/library/dnbda/html/BOAGag.asp 设计数据层组件并在层间传递数据,尽管这篇文章采用了微软系列的数据访问技术,但是对于 Java 应用同样有着不少借鉴作用。
http://www.hibernate.org/ 这是 Hibernate 的官方网站。
这里,我们选取了一个常见的电子商务应用 - 网上书城,并且从中分解出一些典型的应用场景,来分析,体会这些数据访问模式的不同。
如下图(图 6),这个网上书城的主要功能是提供在线的各种图书信息的查询,浏览,并且能够订购这些图书。
这当中,我们分解出四种典型的情况,来看看如何选择合适的数据访问模式。
用户根据查询条件查询相关的书目,返回符合条件的书目列表,这可能内容非常多,而且可能每次查询的内容都不一样。
针对用户书目查询的应用,我们要区别不同的情况。如果查询返回的数据量并不是很大,同时并不频繁,可以考虑采用在线访问的模式;而如果返回的数据量较大(比如返回众多符合条件的书目并且分页显示)而且较为频繁,则可以考虑在线访问模式和离线模式相结合,通过离线数据的缓存来提高查询的性能。
可以参考上一部分数据访问模式中在线访问和 DAO 方式访问部分的相关链接。
管理员可能需要批量对这些书目信息进行修改,并且需要将更新返回数据库。
这类数据应用的特点往往是与数据库交互的次数并不频繁,但是每次的数据量相对较大;同时,也希望能够使得本地操作有较好的交互体验。 针对这种情况,往往适合采用离线数据访问的模式,DTO 模式也是不错的选择。映射到具体的数据访问技术,如果之前采用的是 IBM WebSphere 平台,可以使用 SDO 技术,或者使用 Java 中的 CachedRowSet 技术;如果应用是基于微软的平台,则可以采用 ADO.NET。
可以参考上一部分数据访问模式中 DTO 方式和离线数据方式部分的相关链接。
网上书城的用户访问量越来越大,如何合理地规划有限的资源。
如何让我们的应用能够处理大规模的并发用户访问是一个很复杂的问题,涉及到应用的架构、采用的软件、相应的配置、应用程序的调优等方方面面的问题。一个常见的方法是使用连接池的模式以便能够处理更多的用户请求。
对于比较简单的应用程序,我们可以来实现自己的连接池程序;如果在你的应用中使用了应用服务器,比如 IBM WebSphere Application Server, BEA WebLogic 或是 Tomcat 等等,这些应用服务器都提供了连接池的机制。
下面的链接提供了一些实现简单的连接池的指导,关于具体应用服务器中连接池的配置,请参考相关的产品文档。
Using Data Sources the Right Way
网上书城并购了其它同类的书城,需要将后台系统进行整合。因为有了新的不同类型的数据库系统的加入,需要和旧的数据库系统进行整合。
后台数据系统种类繁多,配置复杂的情况是随处可见的。可能这个书城在刚刚起步的时候只是用了 MS SQL Server 2000 来作为后台数据源;后来随着业务的增加,又有了新的 DB2 或是 Oracle 系统;除此之外,可能还有很多诸如 XM L 文档等等需要和已有的系统集成起来,这些都是令人头痛的问题。 在很多实际项目中,经常需要自己花很大的力气开发一些导入导出工具来集成数据,而现在已经有了专门的产品来做这件事情。
IBM WebSphere Information Integrator(以前的 DB2 Information Integrator )就可以用来支持各种关系型数据源以及非关系型数据源的数据整合,本文不是专门来讨论这个产品,读者可以从下面的链接中得到与该产品以及相关解决方案的资料:
WebSphere Information Integration 专题
数据访问模式并不都是灵丹妙药,明确你需要什么,选择合适的模式,辅以适当的数据访问技术,才是最优的选择。