使用 Java Persistence API 与 JavaServer Faces 开发 Web 应用程序

IBM Rational Application Developer V7.5 如何使得构建 JPA Web 应用程序变得更加容易

当您需要应用程序支持数据时, JavaPersistence API (JPA)为使用关系型数据库提供了一个简单的方法。尽管过去 JPA 一直与 Enterprise JavaBeans (EJBs)一起使用,但是 JPA 与 Web 应用程序一起直接使用也十分方便。本篇文章描述了 IBM Rational ApplicationDeveloper for WebSphere Software V7.5 提供的简化程序模型与工具,它们能方便地构建使用 JPA 的 Web 应用程序。注意:本篇文章基于 IBM Rational Application Developer V7.5 Open Beta 版

通过本文的姐妹篇 “使用 IBM Rational Application Developer V7.5 中的 JPA,Ajax 与 Dojo 工具开发 Web 2.0 应用程序”,了解怎样使用 IBM Rational Application Developer V7.5 中的 JPA、Ajax 和 Dojo 工具,来创建一个端到端的 Web 应程序。

您还可以查看 “产品演示:在 IBM Rational Application Developer V7.5 中使用 JPA 支持”,本次演示将介绍 IBM Rational Application Developer V7.5 新特性之一,对 Java 持久性 API (JPA, Java Persistence API) 的支持。

Thomas F Mutdosch, 高级软件工程师, EMC

  Thomas MutdoschThomas 在最近的七年间,在 IBM 公司从事 Web 与数据工具方面的工作,他将研究的重点放在为 Web 应用程序提供数据访问上。在为开发 Service Data Object(SDO)与 Rational Application Developer 的 JPA 应用程序创建工具方面,他也进行了深入的研究。



2008 年 10 月 21 日

Java™Persistence API (JPA)是使用关系型数据库的 Java 模型对象支持的规格说明。本篇文章给您一个关于 JPA 的概述,并向您展示它怎样在 Web 应用程序环境下运行,以及怎样使用 Rational Application Developer V7.5 应用 Java™Server Faces (JSF)来构建 JPA Web 应用程序。

JPA 的概述

JPA 是一个用于对象关系映射以及数据支持的简化程序模型。数据支持确保检索数据与更新数据的应用程序,在后端数据库的当前状态下,被同时保存。在过去,这是通过使用 Java Database Connectivity (JDBC) APIs 与 其他的数据框架来完成的,通常这些步骤相当繁琐,并涉及到书写复杂的查询语句,以添加或修改数据。

通过使用您数据库表,被访问实体的 Java 代表,以及提供一系列的 APIs 以支持和查询数据,JPA 简化了这个过程。

实体

实体对象是 JPA 应用程序中使用的主要模型。一个实体是数据库中为表格建模的 POJO(Plain Old Java Object)。它包含了与表格列相对应的属性,并允许应用程序直接与数据库的概念模型相联系。

范例
这是一个包含 deptname 列的名为 Department 的数据库。在应用程序中,有一个相对应的名为 Department 的 Java 类,还包含了一个名为 deptname 的区域名,以及 正确的 get 与 set 方法。图 1 显示出了整个的 Department 表,以及相应的实体类。
图 1. Department 表与实体
表格中的列列于左边,类列于右边

映射一个实体与关系数据库的信息,例如识别主要关键字和关系,可以使用 Java 注释在一个实体类中直接指定。您也可以选择,在一个单独的映射配置文件中指定这些映射。JPA 在任何可能的情况下,为这些数据映射使用一般意义下的默认值。这就在您的应用程序中消除了大量的错误代码和繁琐的映射信息。例如,假设一个名为 Department 的实体类,与一个名为 Department 的数据库表相映射。您不需要使用注释或者配置文件,来详细定义该映射,除非您需要更改默认值。

检索并支持数据

JPA 为在您的应用程序中映射和支持实体提供一个 API。类 EntityManager 用于检索,支持,更新以及删除实体。在一个 JPA 应用程序中,您总是在直接处理实体对象,并不需要书写任何基本的 JDBC 代码,以插入或更新数据库行。

为了向您的表格添加一个新行,您只需创建一个实体类的实例,设置其属性,并更新类 EntityManager 以支持新实体。更新和删除已存在的数据非常简单。

JPA 提供 Java Persistence Query Language (JPQL)以查询数据库并检索数据。JPQL 是一种语法上类似于 SQL 的完备查询语言,但是它是在实体上,而不是在数据库表上进行操作。

JPQL 查询范例
SELECT d FROM Department d ORDER BY d.deptNo

此次查询可以传递给 Entity Manager,以检索所有 Department 实体的列表,该列表通过它们的 department 号码进行排序。

Web 容器中的 JPA

JPA 最初准备作为 EJB 3.0 规格说明的一部分,但是最后单独成为一个独立的规格说明,因此允许其他领域利用这个优点。这意味着 JPA 可以用于任意 Java EE 环境中,例如 Web 容器,或者甚至独立 Java SE 应用程序中。您可以充分利用 JPA 的益处,以及它提供的便利之处,不管您喜欢什么环境。

引入资源

在 Java Enterprise Edition 5 (Java EE)中,另一项用于简化编程任务的方法,是引入资源,可以直接向您的代码,进行一次 Java EE 容器引入资源,例如数据源,Web 服务以及 EJBs。这就避免了书写处理一些任务所需要的代码。

不是所有的 Web 容器组件都支持资源引入。例如,您不能在一个 JSP 页面中直接使用它。以下 Web 组件支持资源引入:

  • Servlets
  • Servlet 过滤器与接收器
  • Taglib 标签处理器
  • JavaServer Faces Managed Beans

本篇文章稍后介绍的应用程序范例,将使用 JavaServer Faces Managed Beans 中的资源引入。

Rational Application Developer 中的 JPA

Rational Application Developer 提供多种工具,以帮助JPA 开发。您可以使用编辑器,属性视图以及可视化建模图表,来创建并编辑 JPA 实体类。本篇文章将重点放在,能帮助将 JPA 集成到 Web 应用程序中的可视化工具上。

Rational Application Developer 提供能够方便开发员完成许多项任务的向导,如果在一个文本编辑器中去完成这些任务,将会非常耗时并容易出错。

可视化配置与 JPA 相关数据的工具:

  • JPA 实体创建向导,它使用目标数据库计划
  • JPA 实体的可视化配置
    • 设置主要关键字
    • 设置同步性
    • 在实体间添加以及删除关系
    • 添加并配置 Named Queries
  • 可视化 JPQL 查询构建器
    • 轻松为您的查询添加过滤器
    • 为您的结果排序
    • 书写通用查询语句以检索个人列,或者以用户指定的 Java 类形式返回结果
  • 添加并配置 JPA Manager Beans
  • 自动为运行时部署建立您的 JPA 应用程序
    • 建立合适的 JPA 配置文件
    • 自动创建一个 IBM WebSphere Application Server 数据源
  • 获得关系以及已命名查询的支持
  • 验证以及快速配置

当您已经准备好您的 JPA 模型后,可以使用额外的工具,使您的工作变得更加轻松,如下面的列表所示。

向 Web 页面添加 JPA 数据的工具:

  • Palette 以及 Page Data 视图,在这里您可以向您的页面添加 JPA 数据
  • JPA Manager Beans 提供的外观模式,用于服务方式
  • 在不涉及 Web 页面代码的前提下,那些页面使用的能方便改变查询的工具(在您的 Web 页面访问 JPA 数据以后)
  • 为复杂 JPA 实体添加 JSF,以及为关系区域添加不同 UI 控制器的工具

向 Web 页面添加 JPA 数据

在一个 Web 应用程序中直接使用 JPA 是非常容易的,如果您熟悉双层 Web 应用程序编程,或者不需要 EJBs 复杂性的话,使用 JPA 更加明智。JPA 规格说明不需要在 Java Archives,或者 JARs (因为它们必须在 EJB 环境中)中包裹的实体,因此,JPA 实体类可以在您的 Web 应用程序中被释放。这自然与传统的 Web 应用程序模型相符。

JPA Manager Beans

JPA Manager Beans 的目的

JPA Manager Beans 是一个由 Rational Application Developer 引入的概念,并不是 JPA 规格说明的一部分。它用于提供一个易掌握的编程模型,并完成 JPA 类型的任务。

Rational Application Developer V7.5 引入了 JPA Manager Beans 的概念,它是作为外观或者一个特定 JPA 实体的控制器的 service beans。它们使用 JPA 实体装入并抽象出所有的,您的数据库中用于创建,更新,删除以及显示信息的数据访问代码。

JPA Manager Beans 是一个在双层 Web 环境下使用的理想编程模型。它们填充了正常条件下是由 EJB 环境下 session bean 填充的角色。所有与实体相关的业务逻辑,是由 JPA Manager Bean 运行的。

JPA Manager beans 一对一地映射一个 JPA 实体。例如,如果您有一个 Department 实体,在 Rational Application Developer V7.5 中提供的工具,可以用于创建一个名为 DepartmentManager 的 JPA Manager Bean,它包含了所有需要与该实体协同工作的数据访问逻辑(图 2)。

图 2. DepartmentManager 默认方法
方法列表的屏幕截图

JPA Manager Beans 的使用不仅仅限于 Web 应用程序。它们可被用于任何您想要利用数据抽象能力优势的地方,例如 EJB 项目, JPA Utility 项目,或者甚至是一个普通的 Java 项目。就算您的 JPA 实体存在于一个 JPA Utility 项目,甚至 EJB 项目中,您仍然可以为 Web 应用程序中的那些实体,生成 JPA Manager Beans。

集成 JSF

JPA 可以与 JSF Web 应用程序集成的相当完善。 JPA 实体可以用做大多数 JSF 组件需要的数据捆绑模。为了创建或者更新方案,您可以轻松地将一个实体与输入表格联系起来,或者如果您想在一个表格中显示信息的话,您还可以将一系列实体与一个 JSF 数据格联系起来。

使用 JPA Manager Beans 作为一个编程模型

JPA Manager Beans 的服务型本质,使它很容易就可以集成到一个服务型结构(SOA)环境中去,或者一个设计优良的 Web 应用程序中去。在这样的一个环境下,一个 Web 应用程序直接与一个 JPA Manager Bean 接口。然后 Manager Bean 处理逻辑,通过访问合适的 JPA API 来与备份数据库相接(图 3),来检索以及更新实体。该编程模型能让您将重点集中于应用程序的业务逻辑上,而不陷入支持机理的细节中去。

图 3. 应用程序流范例
图表

当使用 JSF 技术时,您可以将您的 JPA Manager beans 注册为 JavaServer Faces-managed beans,并在您的 Web 应用程序中直接访问它们。在接下来的例子中, Faces 配置文件(/WEB-INF/faces-config.xml)在需求范围内(见于图 4 以及列表 1)包含了一个 managed bean, DepartmentManager。

图 4. JPA Manager Bean 被定义为一个 Managed Bean
带 3 个区域的 Managed Bean 屏幕元素
列表 1. 定义 Managed Bean 的代码
        <managed-bean>
        <managed-bean-name>departmentManager</managed-bean-name>
        <managed-bean-class>entities.controller.DepartmentManager</managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>
        </managed-bean>

这使您能够在应用程序的任何地方,使用 DepartmentManager bean。您不必担心将其实例化,因为它是由 JSF 管理的。另一个使用 managed beans 带来的优势是,它允许您使用资源注入,因为 bean 是由 Web 容器管理的。

使用 Rational Application Developer 构建一个 Web Application with JPA 的范例

目标:您将开发一个应用软件,该软件能显示出公司部门以及它们分配的员工。您还要增加软件的功能,使其能够在文件中更新所有雇员的信息。您将使用带单个 Web 模型的双层结构,该单个 Web 模型包含了所有的 JPA 实体,JPA Manager Beans 以及 Web 页面。您的 Web 页面将与数据层直接交流,而不使用 EJBs。

在本例的第一部分,通过构建 JPA 实体与 JPA Manager Beans,您将构建您的数据层,以和备份 Derby 数据库相交流。在第二部分中,您将重点放在 Web 页面中使用那些 JPA 元素。

第一部分. 建立使用的 JPA 数据

首先,您需要创建一个新的动态 Web 项目。

  1. 在菜单栏中,选择 File > New > Dynamic Web Project

这将启动 Dynamic Web Project 向导(图 5)。

图 5. 创建新的动态 Web 项目
动态 Web Project 向导视图
  1. 输入 JpaWebExample 作为项目名。
  2. 设置目标运行时为 WebSphere Application Server V7.0 (它包含了 JPA 设置),并设置 Web Module 版本为 2.5
  3. 在 Configuration 拉下菜单中,选择 Faces Project 以能够在您的项目中使用 JavaServer Faces。
  4. 点击 Finish

在创建 Web 项目之后,您已经为向您的应用软件添加 JPA 数据做好了准备。在您的应用软件中,您可以选择自定向下方式以创建您的实体。这意味着您的数据库表已经存在,而且您的实体将基于它们已存在的方案生成。在另一个概述中,您也可以使用 自底向上 方式来开发,在这种方式中,您首先要创建您的实体对象,然后从实体中构建数据库表。Rational Application Developer V7.5 同时支持两种方式。

通过创建实体对象以及它们相应的 JPA manager beans,您将开始构建您的应用程序。

  1. 在 Enterprise Explorer 中右击 Web 项目并选择 JPATools > Add JPA Manager Beans (见于图 6)。
图 6. 启动 JPA Manager Bean 向导
按描述所做的选择

JPA Manager Bean 向导将显示出项目中所有已存在的实体,或者 classpath 中所有的 Utility 项目(图 7)。

图 7. JPA Manager Bean 向导
没有可用实体的向导视图

现在您还没有任何 JPA 实体,所以您需要从一个已存在的数据库中生成实体。

  1. 点击 Create New JPA Entities 按钮。
  2. 在打开 Generate Entities 向导后,在向导中(图 8),您可以选择一个对某个数据库已存在的链接(或者创建一个新链接)。
图 8. 生成 JPA 实体
数据库联系设置视图

您将使用 Rational Application Developer V7.5 提供的 Derby Sample Connection。

  1. 选择 Derby Sample Connection 以及 SAMP 方案,然后点击 Next

在第二页面中,所有数据库链接可用的表格被显示出来(图 9)。对于本应用软件,您只关注部门,员工以及员工照片表。因此,您只需为这些表格构建实体。

  1. 选中 DEPARTMENTEMPLOYEE,以及 EMP_PHOTO 复选框,然后点击 Finish
图 9.选中表格以生成实体
从 Tables 视图生成的实体

JPA Manager Bean Wizard 现在显示出新创建的实体,接下来您需要生成相应的 JPA Manager Beans(图 10)。

图 10. 选择实体以构建 JPA Manager Beans
选中的 Department 与 Employee 实体
  1. 选择 Department 以及 Employee 实体,然后点击 Next 以继续翻到向导的下一页。

Tasks 页面显示出 manager beans 和目标实体可以配置的各种设置(图 11)。您可以设置主要关键字,创建并编辑查询,添加并删除关系,以及设置一个同步列。

图 11. JPA Manager Bean 任务
选择的 deptno 字符串

您的数据库表还没有定义的主要关键字,所以现在您需要设置它们。

  1. 在主要关键字 Tasks 页面中,选择 deptno 以作为 Department 实体的主要关键字。
  2. 点击 Employee 实体,并选择 empno 作为它的主要关键字。
  3. 然后点击 Department 实体。

可能您想要在 Department 与 Employee 之间构建一对多关系(因为一个部门拥有多名员工)。例子中的数据库没有详细地定义这种关系,所以现在您需要向您的实体添加关系。

  1. 选择位于左列的 Relationships 任务,并点击 Add 以启动 Add Relationship 目录(图 12)。.
图 12. 添加一个关系
  1. 从下拉菜单中选择 Employee。设置多样性为 one-to-many,然后选择一个 bidirectional 关系。

双向关系

一个双向关系意味着 Department 实体将包含许多 Employees,相反的,Employee 将包含他所属于的 Department。如果您只想要创建一个单向关系,这样 一个 Employee 就不知道他属于哪个Department,那么您就选择单向关系

“Employee”是这个关系的所有者(这就是说, Employee 是 many 边的,因为它包含了 Department 的外部关键字)。因此您需要将 Employee 的外部关键字属性,与 Department 的主关键字映射上。

  1. 在表格的 Employee Foreign Keys 列中选中 workdept 属性,并点击 OK 以创建关系。

怎样添加 JPA 查询

让我们看一下 JPA 查询是怎样添加的。

  1. 点击 Query Methods 任务。本页面显示出了所有的,将会在您的 JPA Manager Bean 中生成的 Query Methods。

提示:点击每个 Query Method 将会显示出 JPQL 查询,该查询当您运行该方法时会随之运行 (图 13)。

图 13. Query Methods 任务
JPA Manager Bean 向导, Tasks 视图

您可以添加新方法,以符合您的应用软件的用例。为了查看一个范例,创建一个返回部门号以及部门名,由部门所在地过滤结果的查询。

  1. 点击 Add 以启动 Add Query Method 目录(图 14)。
图 14. Query Method 构建器
Add Query Method 视图
  1. 将您的查询名更改为比默认值更具有描述意义的值:getDepartmentNamesAndNumbers
  2. 在标签 Result Attributes 之下,取消对除“deptname”与“deptno”之外所有属性的选择。该查询只从数据库中返回这两个属性。

目录底部的 Query Statement 区域,显示出了实际生成的 JPQL 查询,该区域会不断更新以反映目录的当前状态。您也可以直接在此文本区域编辑该查询。

  1. 切换至 Filter Results 标签,并点击绿色加号按钮,以向您的查询添加一个过滤器。该操作会启动 Add Filter Condition 目录(图 15)。
图 15. 添加一个查询过滤器
Add Filter Condition 视图

对于本次练习,假设您对展示位于北卡罗来纳州的部门感兴趣。

  1. 选择 location 作为用做过滤器的属性。
  2. 选择 LIKE 作为过滤器操作符。
  3. 选择 Constant/Entity Attribute 按钮并输入 NC 作为值。
  4. 点击 OK 以向列表添加查询语句。

提示:
您还可以选择,如果您不想硬码状态名,您可以使用一个变量,并动态的将此变量在查询运行时,传递给查询方法。

  1. 最后,点击 Order Results 标签,以让您得到的结果按您想要的方式进行排序(图 16)。
图 16. 向查询添加一个 OrderBy
选中的 deptname 属性

整理部门名,让您得到的结果按照字母顺序进行排序。

  1. 从 Available Attributes 列表中选择 deptname,并点击 Order
  2. 在 Add Query Method 条目中中点击 OK

这就是最终您创建的查询:

SELECT d.deptname, d.deptno FROM Department d 
WHERE d.location LIKE 'NC' ORDER BY d.deptname
  1. 为了看到额外的高级选项,点击Other任务(图 17)。
图 17. 高级选项
向导中的 Tasks 视图

在本页面中,您可以设置您的 manager bean 的名字,并考虑以下高级选项:

  • Use Resource Injection。该设置决定了是否在 JPA Manager Bean 代码中使用资源注入。您将在此处决定,因为您的应用软件在一个 Web 容器中,而且您正在使用 Faces Managed Beans。
  • Use Named Queries。如果该选项能用,在合适的实体中将生成已命名的查询,而且 JPA Manager Bean 查询方法将使用它们。如果该选择没被选中,那么 JPQL 查询语句将在 JPA Manager Bean 代码中直接使用。
  • Update Entity for use in JSF applications。通过确保实体使用的是,与 JSF 协同工作良好的 Java 类别,该选项能帮助确保 JSF 概述中工作顺利进行。
  • Update Relationship fetch types。设置与所有关系相符的 Eager 或者 Lazy。

您还可以使用该页面,来为您的应用软件设置运行时配置细节:

  1. 选择 Configure Project for JDBC Deployment 链接以启动“Set up connections for deployment”条目(图 18)。
图 18. 设置运行时联系细节
  1. 在本条目中,您可以建立以下配置文件,JPA 需要使用这些文件,以在运行时使用合适的联系信息:
    • persistence.xml 文件,它包含了联系的细节信息
    • orm.xml 文件,它包含了映射信息,以及方案细节

接下来,选择一个开发时间联系,它包含了运行时细节。

  1. 在联系区域选择您的 Derby Sample Connection 。这将在剩余的条目中自动填充默认值。

您要设置您想在运行时使用的数据源或者资源参考,以及为所有实体使用的默认方案。如果您要使用单独的测试和部署方案,那么您可以在这里改变默认的方案。选择 Deploy JDBC Connection to Server 复选框,将会在您的 EAR 文件的扩展信息中,自动创建一个数据源。

  1. 点击 OK ,以编辑合适配置文件,并创建数据源。

您不用再去做其他任何事,以让您的应用软件在运行时与数据库联系起来,这种操作通常是复杂,且容易出错的。

  1. 完成 Add Manager Bean 向导并观察生成了什么产物。Enterprise Explorer 视图显示出了所有新生成的实体。

注意它们包含了与数据库表列,以及设置的主关键字、关系相对应的区域(图 19)。

图 19. JPA 实体
实体列表的屏幕截图

列表 2 显示了 Department 实体的 Java 代码。

列表 2. Department 实体代码
@Entity
@NamedQueries( {
     @NamedQuery(name = "getDepartment", 
                query = "SELECT d FROM Department d"),
     @NamedQuery(name = "getDepartmentByDeptname", 
                query = "SELECT d FROM Department d WHERE d.deptname = :deptname"),
     @NamedQuery(name = "getDepartmentByMgrno", 
                query = "SELECT d FROM Department d WHERE d.mgrno = :mgrno"),
     @NamedQuery(name = "getDepartmentByAdmrdept", 
                query = "SELECT d FROM Department d WHERE d.admrdept = :admrdept"),
     @NamedQuery(name = "getDepartmentByLocation", 
                query = "SELECT d FROM Department d WHERE d.location = :location"),
     @NamedQuery(name = "getDepartmentOrdered", 
                query = "SELECT d FROM Department d ORDER BY d.deptno"),
     @NamedQuery(name = "getDepartmentNamesAndNumbers", 
                query = "SELECT d.deptname, d.deptno FROM Department d WHERE
d.location LIKE \'NC\' ORDER BY d.deptname") })
public class Department implements Serializable {
    @Id
    private String deptno;

    private String deptname;

    private String mgrno;

    private String admrdept;

    private String location;

    private static final long serialVersionUID = 1L;

    @OneToMany(mappedBy="workdept",fetch=FetchType.EAGER)
    private List<Employee> employeeList;

    // … 
    // …

类带上了注释@Entity,以向 JPA 指示该类是一个被映射的实体。所有已命名的查询在实体上被添加了 NamedQuery 注释。这就使您不管在应用软件中的任何地方,都能够根据它们的查询名来参考这些查询,您不必去担心实际的 Java Persistence Query Language (JPQL)语句。 这种方法让这些查询在您的应用软件中具有很高的可重用性。如果您想要改变已命名查询自身的话,它能使您避免更改所有的参考。

接下来,您可以看到“deptno”已经带上了@Id注释,意味着它就是主关键字属性。Employee 对象的列表被标上了 OneToMany 注释,以让 JPA 运行时过程知道,该区域是由一个关系填充的。

现在让我们来看看,您的 JPA Manager Beans 是怎样与实体进行对接的。图 20 在 Page Data View 视图下,给您一个关于 JPA Manager Beans 的概述。

图 20. JPA Manager Beans
Manager Beans 列表的屏幕截图

您将注意到创建,检索,更改以及删除(CRUD)操作,都已被生成(createDepartment, deleteDepartment, updateDepartment),还有您已经指定的所有查询方法,也已生成。Department 实体创建的每一个已命名查询,都将会有一个与之相对应的查询方法。

列表 3 显示了 DepartmentManager 的 Java 代码。

列表 3. DepartmentManager 代码
@JPAManager(targetEntity=entities.Department.class)
@SuppressWarnings("unchecked")
public class DepartmentManager {

    @PersistenceUnit
    private EntityManagerFactory emf;
    @Resource
    private UserTransaction utx;

    public DepartmentManager() { 
    } 

    @Action(Action.ACTION_TYPE.DELETE)
    public String deleteDepartment(Department department) throws Exception {
        EntityManager em = getEntityManager();
        try {
            utx.begin();
            em.joinTransaction();
            department = em.merge(department);
            em.remove(department);
            utx.commit();
        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (Exception e) {
                ex.printStackTrace();
                throw e;
            }
            throw ex;
        } finally {
            em.close();
        }
        return "";
    }

    @Action(Action.ACTION_TYPE.UPDATE)
    public String updateDepartment(Department department) throws Exception {
        EntityManager em = getEntityManager();
        try {
            utx.begin();
            em.joinTransaction();
            department = em.merge(department);
            utx.commit();
        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (Exception e) {
                ex.printStackTrace();
                throw e;
            }
            throw ex;
        } finally {
            em.close();
        }
        return "";
    }

    @NamedQueryTarget("getDepartmentByLocation")
     public List<Department> getDepartmentByLocation(String location) {
        EntityManager em = getEntityManager();
        List<Department> results = null;
        try {
            Query query = em.createNamedQuery("getDepartmentByLocation");
            query.setParameter("location", location);
            results = (List<Department>) query.getResultList();
        } finally {
            em.close();
        }
        return results;
    }

// …
// …

需要注意的第一件事,是将用到资源引入的两处区域。因为 JPA Manager Bean 被定义为 Faces Managed Bean,所以 Web 容器能够向代码引入 EntityManagerFactoryUserTransaction ,该代码如下所示:

 @PersistenceUnit
 private EntityManagerFactory emf;
 @Resource
 private UserTransaction utx;

EntityManagerFactory 用于得到一个 EntityManager ,您需要在一个 UserTransaction 中集中所有的处理代码。

分析 deleteDepartment 方法, 您可以看到该方法接受了一个将从数据库中删除的 Department 实体。该方法首先获得了一个 EntityManager,然后开始进行处理。在证实您已经获得那个实体的最新版本之后,该方法将调用 EntityManager 将它从数据库中删除。最终,处理被授权执行,在授权之前,该操作不会实际运行。

现在让我们看一下用于检索部门的查询方法。方法 getDepartmentByLocation 有一个部门所在地的参数,并返回 Department 实体。该方法得到一个 EntityManager ,然后通过使用在 Department 实体中指定的已命名查询的名字,创建一个 NamedQuery 。其中 NamedQuery 如下所述:

 @NamedQuery(name = "getDepartmentByLocation", query = "SELECT 
  d FROM Department d WHERE d.location = :location")

然后方法设置名为“location”的参数(由 namedQuery 中的 :location 标示),它使用传递给方法的用户提供的值。访问 query.getResultList() 实际上运行了查询并返回了结果。

现在您已经准备好了您的数据层,您的应用软件已经可以使用您的实体和 JPA Manager Beans 了。

第二部分. 向 Web 应用软件添加 JPA 数据

对于本例中的软件,您想要显示出一些部门,以及这些部门相对应的员工。另外,您还想要该软件具有更新所有员工信息的功能。

您将要创建两个 Web 页面:一个显示出所有的部门,另一个用于更新员工的信息。

  1. 在项目上右击并选择 New > Web Page 。创建两个页面:listDepartments.jsp 以及 updateEmployee.jsp

现在您需要向您的应用软件添加 JPA 数据。

  1. 打开 listDepartments.jsp 文件。

在 Palette 视图下,有一个 Data and Services 类别,它能让您向 Web 页面添加不同种类的数据(图 21)。

  1. 在选框中选择 JPA data 项目,并将其拖到页面上。
图 21. Data 选框
指向项目拖动的箭头

打开 Add JPA data 向导(图 22)。在第一页中,您可以选择一个可用的,您想在当前页面中使用的 JPA Managers。您还可以选择是显示单个记录还是一个列表,以及是否创建或者更改一个实体。您当前的列表将会显示出一系列部门。

  1. 选择 DepartmentManager ,然后选择 Retrieve a list of data 按钮。
图 22. Add JPA Data 向导
选中的 DepartmentManager
  1. 点击 Next,这样您可以在 DepartmentManager bean 中看到所有可用的查询方法(图 23)。
图 23. 选择 JPA Query Method
选中的 getDepartmentOrdered

这里,您将会显示出各个部门,这些部门是根据它们的部门号进行分类的。

  1. 选择 getDepartmentOrdered 方法。

Query Statement 区域显示出了,该方法将会采用的实际已命名查询。

  1. 切换到下一页,您可以选择您想在 Web 页面中显示的列(图 24)。
图 24. 选择在 Web 页面中显示的列
选中的 3 列

考虑到它的目的,您只关心在您的列表中每个部门的简要概述,例如部门号,部门名,以及部门员工。

  1. 确保列表中只有 deptnodeptname,以及 employeeList 区域被选中。

employeeList 区域是该部门所有员工的一个列表。 默认条件下,每一个部门都会被添加一个员工数据表。您可以选择您想为员工显示的列。

  1. 点击行右边的按钮,以配置 employeeList 控件,然后选择 empnofirstname,以及 lastname
  2. 点击 Finish 以向 Web 页面添加 JPA 数据。

图 25 显示了 Page Designer 的设计视图下,结果得到的数据表的外观。

图 25. 数据表显示了部门的列表
3 列,包括 EmployeeList (3 个 子列)

分析以下 JSP 中生成的源代码,您会发现,数据表已经与 JSP 的 Page Code 文件中的数据方法捆绑上了(当前页面的备份 bean):

<hx:dataTableEx id="departmentList1" value="#{pc_ListDepartments.departmentList}"

Managed beans

注意到 datatable 与 Web 页面的 Page Code 联系上了,并会访问 DepartmentManager。该机理使您的 Web 页面独立出来,这就允许您轻松改变,您为页面检索数据用的 Manager Bean。

Page Code 方法包含了检索部门列表的逻辑(见于列表 4)。方法 getDepartmentList 检索 DepartmentManager managed bean,然后简单的访问了 getDepartmentOrdered()方法,后者是您在向导中选择的,并将结果返回至页面。

列表 4. Page Code 中的 getDepartmentList 方法
@JPA(targetEntityManager = entities.controller.DepartmentManager.class, 
targetNamedQuery = "getDepartmentOrdered")
public List<Department> getDepartmentList() {
    if (departmentList == null) {
        DepartmentManager departmentManager = (DepartmentManager)
getManagedBean("departmentManager");
        departmentList = departmentManager.getDepartmentOrdered();
    }
    return departmentList;
}

既然现在您已经有了部门的列表,那么您就可以完成您的第二个页面,该页面能让您更新一个员工的记录。

  1. 打开 updateEmployee.jsp 页面。
  2. 从 palette 中选择 JPA Data 项,并将其拖住页面。
  3. 选择 EmployeeManager 然后选择操作为 Update an Existing Record(图 26)。
图 26. 添加数据以更新一个 Employee
选中的 EmployeeManager

在向导的下一页中,出于更新的考虑,您可以选择怎样去检索一个特定的员工记录(图 27)。典型的方法是根据实体的主关键字进行查询,因为这可以保证产生一个独一无二的结果。

检索独一无二的实体

除了根据实体的主关键字进行检索,您也可以使用 Manager Bean 中定义的任何一种查询方法。这将会返回搜索值的第一个结果。如果您有一种您确保可以返回独一无二结果的查询的话,甚至该查询不使用主关键字(例如 getEmployeeBySSN),您也可以使用这种技术。

图 27. 根据员工的主关键字进行检索
屏幕截图
  1. 选择 Get Record By Primary Key 按钮。

在接下来的页面中,您要选择在什么地方检索主关键字的值(图 28)。您可以输入一个常量或者任意 JSF 表达式。在目前的情况下,您可以使用默认值,这是一个参数范围内的变量。

  1. 使用 JSF 表达式(param.empno)作为主关键字的值。
图 28. 设置主关键字的值
设置 Filter Values 视图

当您 完成 向导上的步骤以后,Web 页面以及备用的 Page Code 文件将会生成代码。图 29 显示出了页面设计视图的外观。

图 29. 显示以及更新员工记录的用户界面
Page designer 视图 (区域)

当您点击 Submit 后,将会生成一个显示所有员工属性,并允许更新员工信息的输入表格。另外,还会产生一个 Delete 按钮,该按钮能允许您去删除某个特定的员工信息。让我们再次查看 Page Code 文件中生成的备份代码。

一个 getEmployee data 方法会被生成并与 JSF 表格捆绑住(列表 5)。该方法检索 EmployeeManager managed bean 并访问一个方法,以解决 JSF 参数范围内传递的参数问题。最终,它访问 EmployeeManager 的 findEmployeByEmpno 方法,传递给值 empno ,然后返回检索后的 Employee 记录。

列表 5. getEmployee 方法
public Employee getEmployee() {
    if (employee == null) {
        EmployeeManager employeeManager = (EmployeeManager) 
getManagedBean("employeeManager");
        String empno = (String) resolveParam("employee_empno",
            "#{param.empno}", "java.lang.String");
        employee = employeeManager.findEmployeeByEmpno(empno);
    }
    return employee;
}

方法updateEmployeeActiondeleteEmployeeAction在 EmployeeManager 上访问其各自的updateEmployeedeleteEmployee,因此从页面的当前 Employee 进行传递(列表 6)。

列表 6. 更新以及删除员工信息操作
public String updateEmployeeAction() {
    EmployeeManager employeeManager = (EmployeeManager) 
getManagedBean("employeeManager");
    try {
        employeeManager.updateEmployee(employee);
    } catch (Exception e) {
        logException(e);
    }
    return "";
} 
public String deleteEmployeeAction() {
    EmployeeManager employeeManager = (EmployeeManager) 
getManagedBean("employeeManager");
    try {
        employeeManager.deleteEmployee(employee);
    } catch (Exception e) {
        logException(e);
    }
    return "";
}

您需要完成的最后一件事,是当您选择一个员工条目时,建立从您的列表页面。到您的更新页面之间的链接。

  1. 在页面 listDepartments.jsp 上,选择 EmployeeList 数据库,并在 Properties 视图下,选择 DisplayOptions
  2. 点击“添加一条点击行时会运行的操作”旁边的 Add 按钮(图 30)。
图 30. 添加一个行操作
Properties tab 视图

接下来,确保不论何时点击行时,您都将进入 updateEmployee 页面:

  1. 在 Properties 视图下点击 requestRowAction
  2. 点击 Add Rule 按钮,以为操作添加一条 JSF 导航规则(图 31)。
  3. 为页面选择 updateEmployee.jsp,设置按钮为“该规则只为这条操作使用”,然后点击 OK
图 31. 向 updateEmployee 页面添加一条导航规则
Add Navigation Rule 视图

您还需要向 updateEmployee 页面传递一个参数,以指示显示哪一个员工。页面 updateEmployee 被传递给一个名为 empno 的参数。

  1. 在 Properties 视图下,选择 requestRowAction,然后选择 Parameter 属性(图 32)。
  2. 创建一个名为 empno 的参数,然后将其赋予员工的 empno 值,为选中的行:
    #{varemployeeList.empno}
图 32. 传递 empno 作为一个参数
Name 与 Value 参数的区域

在服务器上运行应用软件

现在您已经可以在服务器上运行应用软件。

  1. 在 Project Explorer 中右击 listDepartments.jsp ,然后选择 Run As > Run on Server
  2. WebSphere Application Server V7.0 上运行 listDepartments.jsp 页面。

部门列表将会显示出来,V根据部门号进行排序,同时显示的还有部门中的员工列表(图 33)。

图 33. 在服务器上显示一系列部门
屏幕输出:列与子列

您可以在任意一个员工条目上点击,以查看更具体的细节信息。

  1. 在第一个员工条目 Christine Haas 上点击。

您将会前进到 updateEmployee 页面,在这里您可以改变员工条目的任意细节信息(图 34)。

图 34. 更新一个员工记录
员工记录区域

也许您想要更改员工的名字:

  1. 将名字改为 CHRISTY,姓改为 JOHNSON ,然后点击 Submit
  2. 现在返回至 listDepartments 页面。

更新后的员工信息现在显示出来(图 35)。

图 35. 列表中显示的更新后的员工信息
前面显示的列的更新后的视图

总结

您从本篇文章学到,JPA 在您的应用程序中提供了一种数据支持的简单方式。除了回顾 JPA 的概念以及优势,您还可以学到怎样在一个 Web 页面中使用 JPA。本篇文章还引入了 JPA Manager Beans,并解释了怎样在 Web 应用软件中使用它们,以处理所有的数据支持逻辑。IBM Rational Application Developer V7.5 提供的这些 beans 以及其他工具,能帮助您通过使用 JPA 和 JSF,轻松构建一个 Web 应用程序。

致谢

作者希望感谢 Christopher Jaun 对本篇文章作出的杰出贡献,同样感谢 Daniel Lee 对本文的编辑支持。

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Rational
ArticleID=346986
ArticleTitle=使用 Java Persistence API 与 JavaServer Faces 开发 Web 应用程序
publish-date=10212008