WEB开发网
开发学院软件开发Java Apache Geronimo 和 Spring 框架,第 3 部分: 集成... 阅读

Apache Geronimo 和 Spring 框架,第 3 部分: 集成 DAO 与 ORM

 2010-04-16 00:00:00 来源:WEB开发网   
核心提示:本教程将扩展在本系列教程(共 6 个部分)的 第 2 部分 中创建的 Apache Geronimo 应用程序,在 第 1 部分 中向您介绍了 Geronimo 应用服务器、Spring 框架和控制反转(Inversion of Control,Apache Geronimo 和 Spring 框架,第 3 部分: 集

本教程将扩展在本系列教程(共 6 个部分)的 第 2 部分 中创建的 Apache Geronimo 应用程序。在 第 1 部分 中向您介绍了 Geronimo 应用服务器、Spring 框架和控制反转(Inversion of Control,IoC)。然后又详细阐述了如何开发、配置和部署第一个基于 Spring 框架的应用程序。在本部分中,您将了解如何让集成的技术 —— 如 Spring Java 数据库连接(Java Database Connectivity,JDBC)和 Spring 数据访问对象(Data Access Object,DAO)API —— 通过从 Apache Derby 数据库中动态读取应用程序数据而发挥其作用。您还将了解如何使用 iBATIS 将对象关系映射(Object Relational Mapping,ORM)集成到应用程序中,并享受在不触及任何代码的前提下通过修改应用程序中的数据源实现依赖性注入的妙处。

开始之前

本系列教程适合于需要了解 Spring 框架的更多信息以及如何在 Apache Geronimo 应用服务器上使用 Spring 框架的强大功能的 Java™ Platform, Enterprise Edition (Java EE) 开发人员。

关于本系列教程

本系列教程共分为 6 个部分,向您介绍了 Spring 框架及 Spring 框架怎样与 Geronimo 结合使用。我们将从检验各种 Spring 框架方法及其怎样与 Geronimo 服务器结合使用入手。在整个系列教程中,您将开发和部署个人电话本应用程序。该应用程序包括以下功能:

显示电话本

显示每个条目的细节

向电话本中添加新条目

编辑、修改和删除条目

向条目中添加更多细节,例如主电子邮件地址

第 1 部分 介绍了 Spring 框架的各个模块,并介绍了每个模块与在 Geronimo 应用服务器上开发 Java EE 应用程序的关系。该部分还说明了 Spring 框架所基于的方法。

第 2 部分 介绍了如何使用 Spring 框架在 Geronimo 上构建第一个骨架系统应用程序。

在第 3 部分中,您将采用通过 Derby 数据库添加 JDBC 支持来扩展在第 2 部分中获得的 Geronimo 应用程序。您还将了解如何将 ORM 集成到使用 iBatis 的应用程序中。

在第 4 部分中,您将面对 Spring 面向方面编程 (AOP) 和 Spring Web 框架。使用 Spring AOP,任何受 Spring 框架管理的对象都可变为面向方面的,并且本教程利用了通过 Spring AOP 提供的声明式事务管理服务。

第 5 部分介绍了 Spring Model-View-Controller (MVC)。该教程向您介绍了 Spring MVC 框架及 Web 视图,使您可以了解 Spring MVC 的入门知识。

在本教程结束的第 6 部分,介绍了如何通过 Spring 框架使用 JavaServer Page™(JSP™)、Velocity、Tile 和 PDF 导出功能。您将使用和体验 Spring MVC 内置的各种 Web 视图。

关于本教程

在 第 2 部分 中,开始使用 Spring 框架中的一些模块来部署电话本应用程序。您还了解了如何在 Geronimo 上部署该电话本应用程序。该应用程序中的数据被静态地硬编码到 JSP 页面中。在本教程中,您将了解如何从 Derby 数据库中动态读取数据,以及如何使用 Geronimo 的 Web 控制台创建表并通过使用 Spring JDBC 和 Spring DAO API 从中访问数据。

此外,您还将了解如何使用 iBATIS ORM 框架将数据对象映射为关系数据库对象,并使用 ORM SQL 映射在 Apache Commons connection API 的帮助下让应用程序能够透明地读取数据。

最后,将把所有技术集成到应用程序中,该应用程序将使用 Spring DAO 及用 iBATIS ORM 定义的 SQL 映射,并使用 Spring JDBC 连接至数据库。构建完应用程序后,您将看到依赖性注入的妙处;您只需在配置文件中更改几行即可改变应用程序使用的数据源,而整个过程都无需触及任何代码。

本教程展示了如何将这些技术融合在一起为您提供设计应用程序模型实现的另一种方式。

先决条件

本教程假定您熟悉面向对象的编程 (OOP) 并且熟知 J2EE 和 Java EE 术语。虽然本教程不会涵盖这些内容,但是您必须熟悉基本的 SQL 语句,了解基本的 XML 语义,并且了解对象关系映射的基本概念。了解面向方面编程更佳,但不作硬性要求。

系统要求

您的系统需要至少满足以下要求才能继续学习本系列教程:

The Spring Framework, Version 1.2.8 —— 具有所有依赖性的压缩文件。

Apache Geronimo 1.1 —— Geronimo 是 Apache 的 J2EE 认证应用服务器。

Apache Derby 数据库 —— 本教程使用 Derby,该数据库是开源的轻量级数据库。Derby 是嵌入到 Geronimo 1.1 里的,因此不需要再单独安装。

iBATIS 框架 —— iBATIS 的最新发行版是 2.1.7。

Apache commons dbcp 软件包 —— 需要使用此软件包进行 ORM 模块开发。

Apache commons pool 软件包 —— 这是 ORM 模块所需的软件包。

Apache Ant —— 确保正确配置 Ant 并且其 /bin 目录位于 Path 系统变量中。

Java 1.4.2 —— 确保 Java 安装并运行在系统中。

以下是安装和配置开发、部署和运行示例应用程序所需的软件的指导信息。

Spring 框架和 Geronimo 安装 —— 为了使样例代码能够运行,需要安装运行 Geronimo 和 Spring 框架。有关安装指南,请参阅 第 2 部分。

iBATIS 安装 —— 需要使用 iBATIS SQL 映射作为 ORM 示例的对象关系映射工具。下载后,将压缩文件解压缩到应用程序所在的驱动器中。解压缩文件应当会创建一个名为 iBatis_2.1.7 的目录(在我的驱动器中,iBATIS 的安装目录是 K:\iBatis_2.1.7)。此时,不需要为 SWF 做任何配置。

Derby 数据库 —— Geronimo 1.1 预打包附带的 Derby 数据库安装不需要任何特殊配置。

Apache Commons dbcp 和 Commons pool 软件包 —— 需要这些软件包中的 JAR 与 ORM 示例相配合。其目的是展示不触及任何源代码而进行数据源和连接程序的更改是多么简单。这是通过使用配置文件进行依赖性注入实现的。您将了解如何使用这些 API;目前先下载这些 API 并将它们解压缩到硬盘上。请记住,只需这两个软件包中的 JAR。

iBATIS 和 Spring

由于 iBATIS 在本教程中具有特殊性,本节将介绍什么是 iBATIS 以及必须对它感兴趣的原因。在投入到应用程序的实际开发之前,还必须大致了解一下 Spring 的两种关键技术:DAO 模块和 JDBC API。

什么是 iBATIS?

iBATIS 是一个开放源码对象关系映射程序,它比其他映射程序更轻、更简单,其功能是使用名为 SQL 映射的简单概念将对象映射到 SQL 语句。SQL 映射是一种对象关系映射框架 —— 如 Java 数据对象(Java Data Object,JDO)、Hibernate 等 —— 它将 Java 对象映射到 SQL 语句。它抽象出数据库事务(例如加载数据库驱动程序和获取并管理连接)中所涉及的低级别的细节,也提供高级别的 ORM 性能。

Spring DAO 和 iBATIS API

iBATIS API 提供了将对象映射到 SQL 语句的基本框架,而 Spring ORM API 提供了一种可以轻松地使用依赖性注入在运行时应用这些关系的方法。使用 DAO 设计模式,可以抽象和封装对数据源的所有访问。iBATIS DAO 框架提供了管理连接的功能。Spring DAO 可以提供同样的功能,但是它比 iBATIS 实现更有优势,因为所有配置细节都是在上下文文件中定义的。

为什么使用 iBATIS?

那么为什么选择 iBATIS 用于应用程序?

将 SQL 语句映射到对象十分简单

能够从应用程序中抽象出高级别的细节

简单的应用程序数据库不要求使用诸如 Hibernate 之类的更复杂的解决方案

iBATIS 数据库层附带了两个库:SQL 映射和 Data Access Object 框架,接下来将详细查看这两个库。

iBATIS SQL 映射

又称为 Data Mapper 框架。iBATIS SQL 映射框架可以减少访问关系数据库通常所需的代码量。此框架的特别之处在于它有核心函数库。Data Mapper 框架允许为 Java 应用程序设计和实现更优秀的持久层,方法是使用简单的 XML 描述符将对象与 SQL 语句或存储过程耦合在一起。它还有助于分离应用程序开发中的职能:允许开发人员灵活地将纯 SQL 映射到 Java 对象,同时允许 DBA 优化实际的 SQL 语句以提高性能。

在本教程的稍后部分中,将看到在 ORM 示例中使用 SQL 映射是多么轻松。

iBATIS Data Access Object 框架

它是 iBATIS 框架提供的另一个抽象层 API。它隔离持久性解决方案的具体细节,并提供通用 API 透明地访问应用程序中的数据。

iBATIS DAO 是一个精心打造、设计健壮的框架;本文的主要目标是展示 Spring Framework 所能提供的功能,这也是为什么要使用 Spring 的 DAO 层来抽象与持久层的通信。使用 Spring 的动态配置功能,可以很容易切换到任何受支持的持久性框架,例如 Hibernate、JDO 或 Apache ObJectRelationalBridge (OJB)。

Spring 中的持久性

Spring 框架用来解决持久性问题的两种最重要方法是 Spring DAO 和 Spring ORM 模块。下面列出了其中的一些问题及 Spring 框架是如何帮助解决这些问题的:

不同的数据源使测试变得困难。如上所述,Spring 的 IoC 方法使交换与对象关系 (O/R) 相关的各种实体的实现和配置的位置变得十分简单。这简化了孤立地测试与持久性相关的代码的各个片段的过程。

特定于供应商的数据访问异常。Spring 可以将来自任选的 O/R 映射工具的异常转换成已定义的异常集,这些异常更易于理解。

事务管理。Spring 负责处理事务语义和处理回滚等操作的相应事务。

供应商固定,并允许混合-搭配的实现策略。使用 Spring 的解耦方法,可以在运行时使用和更改各种 API、数据源和实现。使用此方法不必固定使用特定供应商的产品和服务,并可以根据需要混合搭配。

软件开发人员一直以来都在寻求一种持久保持模型的简洁方法,但未取得很大成功。最佳的持久性框架允许使用一种透明的方法而无需侵入域模型。Spring 框架用它的两个模块 DAO 和 ORM 来完成此工作。

Spring DAO

Spring 框架设计允许直接集成到一些常见的 O/R 映射 API,例如 JDO、Hibernate 和 iBATIS。其主要目标是隐藏和抽象数据库事务(如从应用程序代码中创建连接以及管理这些连接及其他内容)的繁琐细节。Spring DAO 模块使您可以定义在应用程序中负责数据中心操作的接口(类必须实现的抽象类型)。

Spring JDBC

Spring JDBC API 提供了各种软件包以服务于所有数据库需求。其核心软件包包含 JdbcTemplate 类,该类负责与数据库操作相关的所有函数。另外它还提供了一种更通用的异常处理架构。

要了解关于这两种技术的更多信息,请阅读本系列教程的 第 1 部分。接下来将开始扩展在 第 2 部分 中构建的电话本应用程序以使用 Spring JDBC 和 DAO 模块。

回顾电话本应用程序和创建数据库

要扩展应用程序以使用 Spring DAO 和 Spring ORM 模块,必须先在 Derby 中添加并填充模型所需的表。在那之前,先要检查工作区目录结构、回顾已有电话本应用程序的事件流程、查看新的事件流程,这些事件将反映出使用了 JDBC-DAO 和 ORM 的扩展应用程序的功能。

工作区的目录结构

图 1 展示了应用程序的布局。因为您已经在本系列教程中执行过此过程,现在可以直接下载本教程附带的压缩文件(请参阅 下载 部分)并将其解压缩到硬盘中。这应当会创建一个具有所有必需的文件及子文件夹的工作区目录(在我的计算机中,它是 k:/workspace。在本教程的其余部分,我将把此目录称为 <WORKSPACE>)。

本教程中涵盖了 图 1 中所示的所有目录和文件。

图 1. 解压缩源文件后应用程序的目录结构
Apache Geronimo 和 Spring 框架,第 3 部分: 集成 DAO 与 ORM

回顾当前的电话本应用程序

在创建新的事件流程之前,让我们先来迅速回顾一下本系列教程的 第 2 部分 中在 Geronimo 内创建的事件(参见 图 2)。

图 2. Geronimo 应用服务器处理请求的流程
Apache Geronimo 和 Spring 框架,第 3 部分: 集成 DAO 与 ORM

如 图 2 所示,对 home.do 页面的请求将转到 Geronimo 应用服务器。运行在 Geronimo 容器内部的 Tomcat Web 容器将处理对应用服务器的请求。Tomcat 随后读取配置文件(尤其是 web.xml),发现 DispatcherServlet 已被配置为处理此请求,因此该请求被转发到 DispatcherServlet。

DispatcherServlet 是 Spring MVC Framework 的一部分,因此 Spring 容器将从此处接管并尝试判断如何处理此请求 —— 或者判断应当由哪个控制器处理此请求。此容器使用依赖性注入来执行此操作(IoC,也称为依赖性注入,是一种用于解决组件的依赖性解析、配置和生命周期的设计模式)。然后它使用在 phonebook-servlet.xml 中定义的 Application Context(Spring 框架中用于解析组件间的依赖性的中心接口),phonebook-servlet.xml 文件帮助 Spring 容器把 /home.do 请求与 PhonebookController 关联起来。然后请求被转发给 PhonebookController 类。这将返回 home.jsp 页面。响应被转发给 Tomcat 容器,该容器编译 JSP 并将响应返回给浏览器。

使用 Spring DAO 和 JDBC 处理 home-dao.jsp

看一看使用 Spring DAO 和 JDBC 后的事件。对 home-dao.jsp(应用程序的 JDBC DAO 实现)的请求将经历 图 2 中所示的事件,直至请求到达 JSP 页面。此页面的前几行告诉容器只能使用 PhonebookDataProvider 服务获取数据。Spring 容器将从 Application Context 中读取 PhonebookDataProvider 的 Bean 定义。它还使用依赖性注入将 PhonebookJdbcDao 与此实例的 PhonebookDataProvider 动态地关联起来。实现 Spring DAO API 的 PhonebookJdbcDao 将使用 Spring 容器的 IoC(依赖性注入)或使用为该目的定义的 Bean 读取要使用的数据源。按照预期,一切依序进行,PhonebookDataProvider 将使用 Spring JdcbTemplate 类从数据库中读取数据。这种解释听起来可能有点太过复杂不容易理解,但是随着后面应用程序的开发,这些解释会变得清晰起来。图 3 显示了处理此请求的事件流程。

图 3. JDBC-DAO 应用程序的事件流程
Apache Geronimo 和 Spring 框架,第 3 部分: 集成 DAO 与 ORM

使用 Spring ORM 和 iBATIS 处理 home-orm.jsp

接下来,将使用 ORM 从数据库中读取数据。对 home-orm.jsp(应用程序的 ORM 实现)的请求将经历 图 2 中所示的事件,直至请求到达 JSP 页面。home-orm.jsp 的前几行告诉容器只能使用 PhonebookDataProvider 服务获取数据。

Spring 容器将从 Application Context 中读取 PhonebookDataProvider 的 Bean 定义。它使用依赖性注入将 SqlMapPhonebookDao 与此实例的 PhonebookDataProvider 动态地关联起来。使用 Spring ORM API 的 SqlMapPhonebookDao 将使用 Spring 容器的 IoC 或使用为该目的定义的 Bean 读取要使用的数据源。Spring iBATIS API 的 SqlMapClientFactoryBean 将把 SQL 映射与 PhonebookEntry 对象关联起来。并且所有这些都是使用 IoC 来实现的,因此各组件之间可能的耦合量最小。

所有关联准备好后,PhonebookDataProvider 使用 Spring SqlMapClientSupport 类从数据库中读取数据。它将抽象所有来自用户的繁琐细节,同时提供所有功能,例如事件处理、对象的生命周期管理等等。同样地,这种解释听起来可能有点太复杂难以理解,但是随着后面应用程序的开发,这些解释会变得清晰起来。图 4 展示了 ORM 应用程序的事件流程。

图 4. ORM 应用程序的事件流程
Apache Geronimo 和 Spring 框架,第 3 部分: 集成 DAO 与 ORM

数据模型

现在可以将注意力转到 Derby,并添加应用程序所需的表。

数据模型很简单,它描述了在数据库中组织业务数据的方法,由两张表构成:

PB_ENTRY —— 应用程序中的每个电话本条目都有名字、姓名和惟一 ID,这些数据是在这张表中维护的。

PB_DETAILS —— 与电话本中的每个条目相关的所有细节都是在这张表中维护的。每个联系人都可以在这张表中登记一个或多个电话号码,这些号码与联系人的惟一 ID 相关联。

下面开始定义本教程的所有版本的应用程序均可使用的透明模型。清单 1 展示了电话本应用程序的模型。

清单 1. PhonebookEntry 对象用作应用程序的模型

public class PhonebookEntry { 
   
  private int entryID; 
  private String fName; 
  private String lName; 
  private int rowID; 
  private String homeNumber; 
  private String workNumber; 
  private String cellNumber; 
  private String email; 
   
  public PhonebookEntry() { 
  } 
 
  public int getEntryID() { 
    return entryID; 
  } 
 
  public void setEntryID(int entryID) { 
    this.entryID = entryID; 
  } 
 
  public String getFName() { 
    return fName; 
  } 
 
  public void setFName(String fName) { 
    this.fName = fName; 
  } 
... 
... 
// other getter and setter methods 

创建和填充应用程序的数据库

创建样例应用程序的第一步是创建应用程序要连接的数据库。然后必须创建表并将值放入其中,以便数据库读入。我已经创建了以下两个 SQL 脚本,您可以使用它们来实现上述操作,二者均位于 <WORKSPACE>/scripts 目录:

createTables.sql 将创建应用程序必需的表。

loadTables.sql 将用值填充这些表。

使用 Derby 数据库的最简单方法是通过 Geronimo Web 控制台。请按照以下步骤创建数据库:

通过更改到 Geronimo 安装目录并在命令行窗口中键入命令:java -jar server.jar,启动 Geronimo。注:如果您刚开始使用 Geronimo 并需要了解如何安装和运行 Geronimo,请参阅本教程末尾的 参考资料 部分以获得链接,帮助您开始使用 Apache Geronimo。

服务器启动后,通过在浏览器中指向 URL http://localhost:8080/console 登录到 Geronimo Web 控制台。注:需要以管理员身份登录以访问 Administration Console。使用默认的用户名 system 和默认的密码 manager。

单击位于左侧的 Console Navigation 面板底部的 DB Manager 链接以打开 Derby 数据库管理器。控制台窗口应当如 图 5 所示。

图 5. DB Manager 控制台
Apache Geronimo 和 Spring 框架,第 3 部分: 集成 DAO 与 ORM

下一步是创建数据库。

在 Create DB 文本区域中,键入 phonebook 并单击 Create 按钮。这应当会在 Derby 中创建数据库。

将 清单 2 中的 SQL 语句粘贴到 SQL Commands 文本框中。

清单 2. 在电话本数据库中创建表

CREATE TABLE PB_ENTRY ( 
  ENTRY_ID INT NOT NULL, 
  ENTRY_FNAME VARCHAR(80) , 
  ENTRY_LNAME VARCHAR(80) , 
 CONSTRAINT PB_ENTRY_PK PRIMARY KEY (ENTRY_ID) 
); 
 
CREATE TABLE PB_DETAILS ( 
 ENTRY_ID INT NOT NULL, 
  ROW_ID INT NOT NULL, 
 HOME_NUMBER VARCHAR(20) , 
 WORK_NUMBER VARCHAR(20) , 
 CELL_NUMBER VARCHAR(20) , 
  EMAIL VARCHAR(200), 
 CONSTRAINT PB_DETAILS_PK PRIMARY KEY (ENTRY_ID, ROW_ID), 
 CONSTRAINT PB_DETAILS_FK FOREIGN KEY (ENTRY_ID) REFERENCES PB_ENTRY(ENTRY_ID) 
); 

从数据库列表中选择 phonebook,然后单击 Run SQL。这应当会为应用程序创建所需的两张表。此外,还可以在文本编辑框中打开 createTables.sql 脚本,并将所有语句复制并粘贴到 SQL Commands 文本框中。

所需的表已经准备好。现在需要在表中放入一些数据,因此执行以上描述的相同步骤,并运行以下 SQL 语句;请记住选择 phonebook 作为数据库(参见 清单 3)。

清单 3. 用应用程序的数据填充表

INSERT INTO PB_ENTRY (ENTRY_ID, ENTRY_FNAME, ENTRY_LNAME) VALUES 
 (0,'Default','Entry'); 
 
INSERT INTO PB_ENTRY (ENTRY_ID, ENTRY_FNAME, ENTRY_LNAME) VALUES ((SELECT 
 MAX(ENTRY_ID)+1 FROM PB_ENTRY),'Adam','Clark'); 
 
INSERT INTO PB_ENTRY (ENTRY_ID, ENTRY_FNAME, ENTRY_LNAME) VALUES ((SELECT 
 MAX(ENTRY_ID)+1 FROM PB_ENTRY),'Charlie','Smearlas'); 
 
INSERT INTO PB_ENTRY (ENTRY_ID, ENTRY_FNAME, ENTRY_LNAME) VALUES ((SELECT 
 MAX(ENTRY_ID)+1 FROM PB_ENTRY),'Don','Brownie'); 
 
INSERT INTO PB_ENTRY (ENTRY_ID, ENTRY_FNAME, ENTRY_LNAME) VALUES ((SELECT 
 MAX(ENTRY_ID)+1 FROM PB_ENTRY),'Harry','Potter'); 
 
INSERT INTO PB_DETAILS (ENTRY_ID,ROW_ID) VALUES (0,0); 
 
INSERT INTO PB_DETAILS 
 (ENTRY_ID,ROW_ID,HOME_NUMBER,WORK_NUMBER,CELL_NUMBER,EMAIL) VALUES 
 (1,(SELECT MAX(ROW_ID)+1 FROM PB_DETAILS),'1 978 234 7839','1 978 134 
 7830','1 978 378 7578','adam@clark.com'); 
 
INSERT INTO PB_DETAILS 
 (ENTRY_ID,ROW_ID,HOME_NUMBER,WORK_NUMBER,CELL_NUMBER,EMAIL) VALUES 
 (2,(SELECT MAX(ROW_ID)+1 FROM PB_DETAILS),'1 617 456 6783','1 617 290 
 3556','1 617 980 2467','charlie@gmail.com'); 
 
INSERT INTO PB_DETAILS 
 (ENTRY_ID,ROW_ID,HOME_NUMBER,WORK_NUMBER,CELL_NUMBER,EMAIL) VALUES 
 (3,(SELECT MAX(ROW_ID)+1 FROM PB_DETAILS),'1 345 333 5680','1 533 290 
 3556','1 678 980 2837','don@brownie.com'); 
 
INSERT INTO PB_DETAILS 
 (ENTRY_ID,ROW_ID,HOME_NUMBER,WORK_NUMBER,CELL_NUMBER,EMAIL) VALUES 
 (4,(SELECT MAX(ROW_ID)+1 FROM PB_DETAILS),'1 221 456 6453','1 567 389 
 2356','1 908 354 2467','harry@potter.com'); 

也可以在文本编辑器中打开所提供的 loadTables.sql 脚本并使用该脚本来将数据装入表中。

至此为应用程序新创建的数据库就准备好了。

创建 JDBC-DAO 版本的电话本应用程序

在本节中,将创建 JDBC-DAO 版本的电话本应用程序。

定义 DAO 接口

现在可以开始实施了,先为应用程序定义 DAO 接口。

清单 4 显示了 DAO 接口,它只有一个方法,但已足够。impl 类将在其中保存实际代码。

















清单 4. 应用程序的 DAO 接口

/* 
 * IPhonebookDAO.java 
 * This interface lists the database operations that can be performed for the 
 * phonebook application. To make things simple, this one only has a method 
 * to get a list of all phonebook entries. 
 */ 
public interface IPhonebookDAO { 
   
  public List getPhonebookEntries() throws Exception; 
   
} 

DAO 接口的 JDBC 实现

IPhonebookDAO 只是一个接口;它不为应用程序提供任何功能,但它确实 能够使应用程序的设计更具可扩展性。在 清单 5 的 DAO 的实现中,使用了 Spring JDBC 核心库中的 JdbcDaoSupport 类。

JdbcDaoSupport 是一个用于 JDBC 数据访问对象的类。它要求设定一个数据源,并为其子类提供一个 JdbcTemplate。

JdbcTemplate 是 JDBC 核心软件包中的中心类。此类通过处理创建及发布资源简化了在应用程序中使用 JDBC 的过程。正如在 清单 5 中可以看到的那样,开发人员无需知道关于数据库的任何信息也无需关心关闭打开的连接。JdbcTemplate 会负责处理这一切。它还捕捉 JDBC 异常并把这些异常转换为在 Spring 中定义的更具信息性的一般的异常层次结构。清单 5 显示了 Java 代码(它并不完整,因此请参考项目工作区的 src 目录中的 Java 文件)。

清单 5. PhonebookJdbcDao 使用 Spring JDBC 库

/** 
 * This class represents the DAO implementation of your application. 
 * It uses the Spring JdbcDaoSupport class from the Spring JDBC module. 
 */ 
public class PhonebookJdbcDao extends JdbcDaoSupport implements 
 IPhonebookDAO{ 
 ... 
  public java.util.List getPhonebookEntries() throws Exception { 
    JdbcTemplate jt = getJdbcTemplate(); 
    return jt.query("SELECT * FROM PB_ENTRY A, PB_DETAILS B WHERE 
 A.ENTRY_ID = B.ENTRY_ID AND A.ENTRY_ID <> 0", 
            new RowMapperResultReader(new PhonebookRowMapper())); 
  } 
... 

现在已经创建了接口,接下来需要定义应用程序从中读取数据的数据源。您将看到如何使用 Spring 连接到 Derby 数据库。

在 Application Context 中定义 DataSource

使用在 Geronimo 中运行的 Spring 框架的 IoC 容器来提供可以注入到 PhonebookJdbcDao 对象中的 DataSource。IoC 容器是提供依赖性注入的核心功能的一种 Spring 容器。正如 清单 6 所示,定义一个新的 ApplicationContext.xml 文件,它与 servlet-context.xml 中定义的上下文不同。

必须定义一个指向已经创建的电话本数据库的 Datasource Bean。该 Bean 还指示 Spring 容器使用 Derby 核心 API 的嵌入式驱动程序。之所以使用该嵌入式驱动程序是因为 Derby 数据库运行于 Spring 应用程序所在的相同的 JVM 中。

接下来的 Bean 定义告诉 Spring 容器将这个新定义的 DataSource 注入到为应用程序定义的 JDBC DAO 对象。因此,现在应用程序知道它只能使用 Derby JDBC 驱动程序连接到数据库,而此数据库是由运行在 Geronimo 应用服务器中的 Derby 定义的。

最后,将为 DataProvider 对象定义另一个 Bean。在此,将告诉 Spring 容器在运行时把 DAO 对象注入到 PhonebookDataProvider 中。

正如 清单 6 所示,Spring 允许实现所有这些组件之间的最大解耦 —— 而且会透明地进行。这让测试变得简单,也使动态更改或添加其他组件变得更为轻松。假设需要使用不同的 DataSource,只需更改该 Bean 中的配置,该 Bean 就会使用新配置 —— 而根本不需要触及任何代码!

很棒,对不对?

清单 6. 通过 Spring 容器为依赖性注入定义 DataSource 和其他 Bean

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
 "http://www.springframework.org/dtd/spring-beans.dtd"> 
 
<!-- 
- Application context definition for "phonebook" application. 
--> 
<beans> 
   
  <!-- Bean Definitions required for data access using Spring Jdbc DAO 
 Support --> 
     
  <bean id="pbJdbcDataSource" 
    class="org.springframework.jdbc.datasource.DriverManagerDataSource" 
    destroy-method="close"> 
    <property 
 name="driverClassName"><value>org.apache.derby.jdbc.EmbeddedDriv 
er</value></property> 
    <property 
 name="url"><value>jdbc:derby:phonebook</value> 
</property> 
    <property 
 name="username"><value>APP</value></property> 
    <property 
 name="password"><value></value></property> 
  </bean> 
     
  <bean id="phonebookDao" 
 class="phonebook.dao.jdbc.PhonebookJdbcDao"> 
    <property name="dataSource"> 
      <ref local="pbJdbcDataSource"/> 
    </property> 
  </bean> 
     
  <bean id="phonebookJdbcDataProvider" 
 class="phonebook.dao.PhonebookDataProvider"> 
    <property name="pbDao"> 
      <ref local="phonebookDao"/> 
    </property> 
  </bean> 
</beans> 

让 web.xml 识别 Application Context

由于应用程序正在应用服务器内运行,因此需要对其 web.xml 文件做一些更改,以便让它能够识别 ApplicationContext.xml 配置文件。

可以使用 Spring 的 ContextLoaderListener 来完成此项任务,并按照 Geronimo 应用服务器的规则在 web.xml 中添加新的侦听程序。清单 7 展示了定义此侦听程序的 XML。

清单 7. 向 web.xml 中添加新的侦听程序

 
  <listener> 
 <listener-class>org.springframework.web.context.ContextLoaderListe 
ner</listener-class> 
  </listener> 
  <context-param> 
    <param-name>contextConfigLocation</param-name> 
    
 <param-value>/WEB-INF/ApplicationContext.xml 
</param-value> 
  </context-param> 

在 JDBC/DAO 的顶部定义 PhonebookDataProvider 服务

PhonebookDataProvider 类汇集了上述所有内容(参见 清单 8)。它还是由客户机(JSP 页面)实例化以从数据库中读取数据的类。如果查看最后一个定义的 bean phonebookJdbcDataProvider,则 Spring 容器将注入 PhonebookDao 对象以实现 IoC。现在回头再看 图 3 应当更有意义。

清单 8. 客户机 (JSP) 的电话本 DataProvider 服务

/* 
 * PhonebookDataProvider.java 
 * This class works as a Service Provider class for your application 
 */ 
 
public class PhonebookDataProvider { 
   
  private IPhonebookDAO pbDao; 
   
  /** Creates a new instance of PhonebookDataProvider */ 
  public PhonebookDataProvider() { 
  } 
   
  public void setPbDao(IPhonebookDAO pbDao) { 
    this.pbDao = pbDao; 
  } 
   
  public List getPhonebookEntries() throws Exception{ 
    return pbDao.getPhonebookEntries(); 
  } 
} 

将主页从 home.jsp 更改为 home-dao.jsp

现在来了解一下 JSP 如何利用 PhonebookDataProvider 服务。正如在 清单 9 所见,客户机只需要三行代码就可以获取电话本条目的列表。所有 JDBC 细节都已被处理;也无需显式的错误处理代码。它就是一段干净的代码,而且具有高可配置性并易于测试!

JSP 中的代码将获取电话本条目的列表并迭代这些条目来填充视图。清单 9 提供了一个代码小片段来展示如何将这段代码添加到 JSP 中。

清单 9. home-dao.jsp 将透明地使用 JDBC DAO 动态装入来自数据库的数据

... 
<% 
try{ 
  WebApplicationContext ctx = 
 WebApplicationContextUtils.getWebApplicationContext(application); 
  PhonebookDataProvider pb = (PhonebookDataProvider) 
 ctx.getBean("phonebookJdbcDataProvider"); 
  List pbDetails = pb.getPhonebookEntries(); 
%> 
  
<html> 
...    
  <!-- Dynamically populate all the fields read from the database --> 
      <% 
      for (int i=0; pbDetails!=null && i<pbDetails.size();i++){ 
        PhonebookEntry pbEntry = 
 (PhonebookEntry)pbDetails.get(i); 
      %>   
 
      <TR> 
        <TD align=center><input type=checkbox name=cb_1 
 alt="Select to Delete" align="middle"></TD> 
        <TD align=center><%=pbEntry.getFName()+" 
 "+pbEntry.getLName()%></TD> 
        <TD 
 align=center><%=pbEntry.getHomeNumber()%></TD> 
        <TD 
 align=center><%=pbEntry.getWorkNumber()%></TD> 
        <TD 
 align=center><%=pbEntry.getCellNumber()%></TD> 
        <TD 
 align=center><%=pbEntry.getEmail()%></TD> 
      </TR> 
       
</html> 
... 

略微更改 PhonebookController servlet

最后一项任务是使 PhonebookController servlet 识别这个新的 JSP 页面。清单 10 中的代码无需过多解释。控制器将基于 URI 返回相应的视图。

清单 10. PhonebookController 被更改为处理其他请求

/* 
 * This class is a simple Spring Controller. This controller is registered 
 * as a Bean in the Phonebook ApplicationContext. 
 */ 
public class PhonebookController implements Controller{ 
   
  /** 
   * You use this Phonebook as central Controller to handle all input 
   * requests coming to your application. In this case, it returns a View 
   * of page requested depending on the URI. 
   */ 
  public ModelAndView handleRequest(HttpServletRequest request, 
 HttpServletResponse response) 
  throws ServletException, IOException { 
    // request for the JDBC DAO implementation page 
    if (request.getRequestURI().indexOf("home-dao")!=-1) { 
      return new ModelAndView("/WEB-INF/jsp/home-dao.jsp"); 
    } 
    // request for the ORM DAO implementation page 
    else if (request.getRequestURI().indexOf("home-orm")!=-1) { 
      return new ModelAndView("/WEB-INF/jsp/home-orm.jsp"); 
    } 
    // Default page with static values 
    return new ModelAndView("/WEB-INF/jsp/home.jsp"); 
     
  } 
   
} 

构建、部署并运行

本教程附带的源代码的压缩文件含有所需的所有类、配置文件和 Ant 构建文件。第二个压缩文件是含有全部所需内容的可配置的 .war 文件。可以使用任何一种方法来获取 phonebook.war 文件。

另外还需要确保 readme.txt 文件中提及的所有 JAR 都位于 <WORKSPACE>/phonebook/lib 目录中。建议仔细阅读该文件中的指导信息,并确保将所有必需的文件复制到 <WORKSPACE>/phonebook/lib 中。

注:可以参考本系列教程的 第 2 部分 中的构建说明。

使用 Geronimo 中的 Deploy 新工具部署 phonebook.war。如果运行正常,将在 Geronimo 控制台上看到一条消息,说明 Phonebook application deployed successfully。

现在只需将浏览器指向新页面:http://localhost:8080/phonebook/home-dao.jsp。

如果运行正常,主页应当会如 图 6 所示。

图 6. 在应用服务器中运行的 home-dao.jsp
Apache Geronimo 和 Spring 框架,第 3 部分: 集成 DAO 与 ORM

接下来您将看到 iBATIS ORM 是怎样适应应用程序的。

创建 iBATIS ORM 版本的电话本应用程序

在本节中,将使用 iBATIS 框架的 SQL 映射和 Spring ORM 模块的 ORM 支持类让应用程序读取数据。Spring 在资源管理、DAO 实现支持和事务策略方面提供了与 iBATIS 的简单集成。

用来通过 SQL 映射从 iBATIS 中读取数据的配置

利用 Spring ORM API 提供的 DAO 支持来抽象实现中的数据源细节,并且需要做出以下配置以使其成为可能:

SQL 映射的 Application Context —— 必须定义 Data Mapper 库所连接的数据源。定义四个 Bean:一个用于数据源;另外三个用于使应用程序能与 SQL 映射结合使用。

SQL 映射配置文件 —— 此文件将定义可能需要的所有特定于 iBATIS 的配置设置,并且它还声明了应当通过此配置文件访问的 SQL 映射文件的位置。

SQL 映射 —— 一个映射到惟一的业务实体(电话本)的 SQL 映射文件。SQL 语句也是在此文件中创建的。

此处首先要执行的是将 iBATIS .jar 文件复制到项目目录中。把 ibatis-sqlmap.jar、ibatis-dao.jar 和 ibatis-common.jar 放入 <WORKSPACE>/phonebook/lib 目录中。

创建 SQL 映射

iBatis 有一个定义好的模式用于将 Java 对象映射到 XML 中。在 清单 11 中,定义一个 SQL SELECT 语句并让 iBATIS 框架知道 resultClass 是数据模型 PhonebookEntry 对象。查看 清单 11 以更好地了解这一切是如何实现的。

清单 11. phonebook.xml 把业务模型映射到 SQL 语句

<sqlMap namespace="Phonebook"> 
  <select id="getPhonebookEntries" 
 resultClass="phonebook.dao.PhonebookEntry"> 
    SELECT 
    A.ENTRY_ID as entryID, 
    A.ENTRY_FNAME as fName, 
    A.ENTRY_LNAME as lName, 
    B.ROW_ID as rowID, 
    B.HOME_NUMBER as homeNumber, 
    B.WORK_NUMBER as workNumber, 
    B.CELL_NUMBER as cellNumber, 
    B.EMAIL as email  
    FROM PB_ENTRY A, PB_DETAILS B 
    WHERE A.ENTRY_ID = B.ENTRY_ID 
    AND A.ENTRY_ID != 0 
  </select> 
</sqlMap> 

创建 SQL 映射配置文件

这是 iBATIS API 所需的另一个文件。它将告诉运行 iBATIS 框架的容器在何处可以找到为应用程序定义的 SQL 映射。清单 12 中定义的设置是可选的;如果删除这些设置,应用程序应当仍能正常运行。

创建 sqlmap-config.xml 文件并将其放在 <WORKSPACE>/phonebook/web/META-INF 文件夹中(参见 清单 12)。

清单 12. sqlmap-config.xml 文件告知 iBatis 容器应用程序的 SQL 映射

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE sqlMapConfig PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN" 
 "http://www.ibatis.com/dtd/sql-map-config-2.dtd" > 
 
<!-- Always be sure to use the correct XML header as above --> 
 
<sqlMapConfig> 
   
  <settings cacheModelsEnabled="true" enhancementEnabled="true" 
       lazyLoadingEnabled="true" maxRequests="32" 
       maxSessions="10" maxTransactions="5" 
       useStatementNamespaces="false" /> 
 
  <sqlMap resource="/phonebook/dao/orm/Phonebook.xml" /> 
 
</sqlMapConfig> 

DAO 接口的 ORM 实现

使用 Spring 框架提供的 SqlMapClientDaoSupport 类来支持 iBATIS。这是一个实用工具类,它负责处理创建、管理和关闭连接这些复杂的工作。

同 JdbcTemplate 类一样,SqlMapClientTemplate 类通过 iBATIS SQL 映射的 SqlMapClient API 简化了数据访问。它还处理所有 SQL 异常并将这些异常转换为一般的 Spring DAO 异常。它与 JdbcTemplate 一样使用 SQLExceptionTranslator 机制。

清单 13 显示了 SqlMapPhonebookDao 类。

清单 13. DAO 接口的 ORM 实现

/** 
 * This is the ORM SQL Maps implementation for your application. 
 * The PhonebookEntry object is mapped directly to the database using the 
 * iBATIS framework. You can use any kind of database for RDBMS. 
 */ 
public class SqlMapPhonebookDao extends SqlMapClientDaoSupport 
 implements IPhonebookDAO { 
     
  public List getPhonebookEntries() throws Exception { 
    return getSqlMapClientTemplate() 
 queryForList("getPhonebookEntries",null); 
  } 
   
} 

注:如果在 Geronimo 中重新部署应用程序,可能需要重新启动服务器。由于某种原因,当重新部署应用程序时,Derby 数据库不会重新初始化电话本数据库。

向 Spring Application Context 中添加 ORM

需要让 Application Context 知道 ORM 实现,并且需要注入 SqlMapClientTemplate 类所需的 SqlMapClient 对象。还要定义用于 ORM 示例的不同数据源。之所以这么做是想说明更改数据源以供测试或者更改应用程序是多么容易。

之前我曾经要求您安装 Apache Commons dbcp 和 pools 软件包,那是因为本文要定义的数据源将使用 Apache Commons API。可以为此数据源使用同一个 Derby 嵌入式驱动程序。

清单 14 中显示的第二个定义使用 SQLMapClientFactoryBean 来创建 SqlMapClient 对象的实例并告诉它使用在前面的 JDBC-DAO 部分中定义的数据源。

第三个 Bean 将指示 Spring 框架在运行时把 SqlMapClient 对象注入到 ORM DAO 实现对象中。

最后一个 Bean 是为 JDBC DAO 访问定义的。它告诉 Spring 框架在运行时把第三个 Bean 创建的 DAO 对象注入到 PhonebookDataProvider 类中(参见 清单 14)。

清单 14. 向 Application Context 中添加与 ORM 相关的 Bean

<!-- Bean Definitions for ORM beans, required for dataAccess via ORM SQL 
 maps --> 
     
  <bean id="pbOrmDataSource" 
 class="org.apache.commons.dbcp.BasicDataSource" 
 destroy-method="close"> 
    <property name="driverClassName" 
 value="org.apache.derby.jdbc.EmbeddedDriver"/> 
    <property name="url" value="jdbc:derby:phonebook"/> 
    <property name="username" value="APP"/> 
    <property name="password" value=""/> 
  </bean> 
 
     
  <bean id="sqlMapClient" 
 class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> 
    <property name="configLocation" 
 value="/META-INF/sqlmap-config.xml"/> 
    <property name="dataSource" ref=" 
 pbOrmDataSource"/> 
  </bean> 
       
  <bean id="phonebookOrmDao" 
 class="phonebook.dao.orm.SqlMapPhonebookDao"> 
    <property name="sqlMapClient" ref="sqlMapClient"/> 
  </bean> 
 
  <bean id="phonebookOrmDataProvider" 
 class="phonebook.dao.PhonebookDataProvider"> 
    <property name="pbDao"> 
      <ref local="phonebookOrmDao"/> 
    </property> 
  </bean> 

把主页从 home.jsp 更改为 home-orm.jsp

最后一步是更改 home.jsp 来使用新定义的 ORM Data Provider。这与在本教程的 DAO 应用程序中创建的 home-dao.jsp 类似。惟一的区别在于需要访问不同的 Bean 来访问 ORM 对象。创建一个单独的 .jsp 文件还可以并排比较这两个实现。

在 清单 15 中,可以看到 JSP 如何用 Bean 的名称获取 Bean,并使用它填充视图。可以查看 home-orm.jsp 以获得完整代码(请参阅 下载 部分以获取有关链接)。

清单 15. home-orm.jsp 中的代码片段显示了如何获取 PhonebookOrmDataProvider bean

  WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(application); 
|-------10--------20--------30--------40--------50--------60--------70--------80--------9| 
|-------- XML error: The previous line is longer than the max of 90 characters ---------| 
  PhonebookDataProvider pb = (PhonebookDataProvider) ctx.getBean("phonebookOrmDataProvider"); 
|-------10--------20--------30--------40--------50--------60--------70--------80--------9| 
|-------- XML error: The previous line is longer than the max of 90 characters ---------| 
  List pbDetails = pb.getPhonebookEntries(); 

构建并运行

好的!全部完成了。运行 Ant 来构建并创建 .war 文件。使用 Geronimo 中的 Deploy New 工具进行部署。

让浏览器指向新页面:http://localhost:8080/phonebook/home-orm.jsp。

主页看上去应该没什么变化,但是这一次所有数据都是使用 iBATIS Data Mapper API 从 Derby 数据库中读取的。

整合 JDBC-DAO 和 iBATIS ORM 版本的电话本应用程序

至此,您已经在本教程中了解了几种技术:Spring JDBC、Spring DAO、Spring ORM 和 iBATIS API,现在是时候把它们整合在一起了。

整合

首先需要做的是更改 ORM SqlMapClient 所连接的数据源,以使该数据源可以用作为 JDBC 示例而定义的 JDBC 数据源。

要让 iBATIS 使用 JDBC 数据源,仅需更改 ApplicationContext.xml 中的一行(参见 清单 16)。















清单 16. 让 iBATIS 使用 JDBC 数据源

<bean id="sqlMapClient" 
 class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> 
    <property name="configLocation" 
 value="/META-INF/sqlmap-config.xml"/> 
    <property name="dataSource" ref="pbOrmcDataSource"/> 
  </bean> 

更改此 Bean 引用的数据源 pbJdbcDataSource,如 清单 17 所示。

清单 17. 更改数据源

<bean id="sqlMapClient" 
 class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> 
    <property name="configLocation" 
 value="/META-INF/sqlmap-config.xml"/> 
    <property name="dataSource" ref=" pbJdbcDataSource "/> 
 </bean> 

注:在源代码提供的 Application Context 中,只能手动做出此更改才能看到它运行。以上提及的其他两个示例都预打包在可部署的 .war 文件中,该文件和源代码一样由本教程随附。

这就是在此处需要做的全部工作。

构建并部署 .war 文件

构建、部署并运行.war 文件,然后指向同一个 home-orm.jsp 页面。您将看到同一个主页,但现在数据是使用 Spring JDBC 驱动程序管理器从 Derby 数据库中读取的。

让浏览器指向 JSP 页面:http://localhost:8080/phonebook/home-orm.jsp。

至此就完成了对本教程中涵盖的所有技术的整合!

Spring 的优点

您已经看到了进行诸如更改应用程序中的数据库之类的重大更改而无需触及任何代码是多么轻松。可以利用 XML 配置进行组件解耦是使用 Spring 框架最大、最重要的优点。除此之外,下面还有一些在应用程序中使用 Spring JDBC、DAO 和 ORM 框架的其他优点:

Spring 提供了数据库连接和管理问题的完整抽象。您无需担心忘记关闭连接。在本文中介绍的 Spring 容器类将为您处理所有那些问题。

Spring 管理应用程序所需的所有异常处理。还将检查出的异常(如 java.sql.SQLException)转换为更具一般性的未检查出的 Spring DAO 异常。

Spring 框架提供了一个简单的数据源实现,它可在容器外部使用并且仅通过一个配置文件就可以管理。它还提供了一个可以根据需要覆盖的抽象数据源类。

Spring 提供了各种易于使用的 DAO 支持类,例如在应用程序中使用的两个类(JdbcDaoSupport 和 SqlMapClientDaoSupport)。

Spring 框架支持多项 ORM 技术,例如 Hibernate、JDO 和本文中介绍的 iBATIS。而且,您可以十分轻松地从一种实现切换到另一种实现。

Spring 易于测试 —— 通过不同的数据库和不同的实现。

结束语

在本系列教程的第 3 部分中,您了解了几种易于使用的重要技术:Spring JDBC、Spring DAO、Spring ORM 和 iBATIS。在下一部分中,您将开始学习更有趣的 Spring 模块:Spring AOP 和 Spring Web Flow。使用 Spring 面向方面编程的 API,任何对象经过管理都可以变为面向方面的。您将使用由 Spring AOP 提供的声明性事务管理服务。

Spring Web Flow 是一种声明式定义 Web 流程的方法。在本系列教程后续部分中,您将扩展电话本应用程序使其拥有更多功能(比如添加新条目、修改条目或删除条目的功能)和更多页面。敬请关注!

下载

描述名字大小下载方法
第 3 部分的源代码geronimo.spring3.source.zip100KBHTTP
第 3 部分的 WAR 文件geronimo.spring3.war.zip4159KBHTTP

Tags:Apache Geronimo Spring

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接