【IT168 技术文章】
引言
Enterprise JavaBeans 3.0 是 EJB 规范的一个主要增强功能,它引入了新的基于 POJO 的编程模型,从而大大简化了开发过程。EJB 3.0 规范是根据 JSR 220 创建的,它分为三个文档:
EJB 3.0 简化 API:定义用于编码 EJB 组件(特别是会话 Bean 和消息驱动的 Bean)的新简化的 API。
Enterprise JavaBeans 核心契约和要求:定义 Bean 和 EJB 容器之间的 EJB 契约。
Persistence API:为 Java 定义新的持久模型。
本文将向您介绍 IBM WebSphere Application Server V6.1 Feature Pack for EJB 3.0,并从头向您介绍如何构建 Java Persistence API (JPA) 和会话 Bean POJO,此外,还将向您介绍如何在 WebSphere Application Sever 内运行它。为了重点介绍 EJB 3.0 规范的面向方面的编程增强功能,本文还将让您了解如何构建其他会话 Bean、演示 POJO 注入,并帮助您构建 EJB 3.0 拦截器。
本文将使用 WebSphere Application Server Toolkit V6.1 和 Eclipse 提供的 Dali 插件。IBM Rational® Application Developer V7.5 是作为开放的测试版发布的,它已与 Eclipse Dali 功能绑定,因此,您可能需要深入了解该功能。您会注意到,以后的文章将使用 Rational Application Developer V7.5 来构建 EJB 3.0 应用程序。
准备
在本文中,将使用新的 Feature Pack for EJB 3.0 在 WebSphere Application Server V6.1 中构建 EJB 3.0 应用程序。对于本例而言,示例应用程序是一个简单的客户订单系统,在此订单系统中,客户将创建一个订单,添加行项目,然后提交该订单。图 1 说明了此示例系统中的用例,图 2 的活动图表中介绍了其中用例的执行顺序。
图 1. 示例应用程序用例
图 2. 示例应用程序用例顺序
本文假定:
安装了 WebSphere Application Server V6.1。
已下载了 WebSphere Application Server V6.1 Feature Pack for Enterprise JavaBeans 3.0。
安装了 WebSphere Application Server Toolkit v6.1。
已配置了数据库模式和测试数据。(对于本文,下载资料中提供了用来填充 Derby 数据库的 DDL。)
安装 Feature Pack
要将 EJB 3.0 应用程序部署到 WebSphere Application Server V6.1 内,您需要安装 EJB 3.0 Feature Pack。本部分重点概述将 Feature Pack 安装到现有 WebSphere Application Server V6.1 的主要步骤。请务必检查 Feature Pack Read Me 文件,以了解其他先决条件。以下步骤解释如何将 Feature Pack for EJB 3.0 安装到由单个应用程序服务器概要组成的简单配置中。有关安装到 Network Deployment 配置的规划和安装指南,请参考安装向导的结果面板上的链接。(有关完整的安装说明,请参阅 WebSphere Application Server 信息中心)
在您的 Feature Pack 安装镜像中找到 EJB3 目录。双击 install.exe 启动该安装向导。
在安装向导的欢迎面板中,通过单击 Next 检查是否在安装 EJB3 Feature Pack。
接受许可协议的条款,并单击 Next。
这将执行先决条件检查。Feature Pack 安装镜像还包含两个必需的修补程序包,如果它们不存在,将会提示您安装。单击 Next 继续安装。
使用 Browse 按钮导航到 WebSphere Application Server 安装目录。
如果应用程序服务器仍在运行,可能会向您显示图 3 中所示的先决条件错误消息。如果是这样,请转到 Back,然后手动停止应用程序服务器。在该应用程序服务器停止后,请再次单击前一面板中的 Next 以继续。
图 3. 先决条件错误
接下来将显示摘要面板,列出将要安装的项(图 4)。单击 Next 以安装该 Feature Pack。
图 4. 安装摘要
当安装完成时,将显示图 10 中所示的面板。取消选中 Launch the Profile management tool 并单击 Finish。(此面板上还将提供一些链接和将 Feature Pack 安装到 Network Deployment 拓扑的说明和注意事项。)
图 5. 启动 Profile Manager
将 Dali 添加到 Application Server Toolkit
Dali 是一个针对 Eclipse 的对象/关系映射插件,该插件提供支持自顶向下、自底向上和中间相遇 O/R 映射的 Java™ Persistence 透视图。Dali 还为管理各种 Java Persistence API (JPA) 注释提供了向导和编辑框。可以将 Dali 添加到 WebSphere Application Server Toolkit 或 Rational Application Developer V7.0.x。(记住,Dali 是一个开源插件,所以支持来自 Eclipse。您还可以将 Rational Application Developer V7.5 Beta 用作备用选项,后者随安装的 Dali 插件一起提供。将 Dali 添加到 Application Server Toolkit:
下载最新的 0.5 版 Dali。
确保停止了 Application Server Toolkit。
将 Dali 下载文件的内容提取到 Application Server Toolkit 主安装目录中。(之所以这样做,是因为该文件包含该目录路径,该目录路径以驻留在 Application Server Toolkit 安装目录中的 eclipse 子目录为起始点。)
从命令提示符转到 Application Server Toolkit 安装目录,并使用以下命令启动该工具包:
ast.exe -clean
此命令将确保能够检测到 Dali 插件。
一个简单的例子
现在,您已经准备好为 WebSphere Application Server 构建第一个 EJB 3.0 应用程序了。通过采用本部分中的步骤,您将使用前面介绍的示例开发一个简单的 JPA POJO 和一个会话 Bean,然后测试此场景:
设置开发环境
创建第一个 JPA POJO
创建简单的会话 Bean
导入客户端
A. 设置开发环境
首先,需要创建所需的项目:
从 Application Server Toolkit 的 J2EE 透视图中,在 Project Explorer 内右键单击,然后选择 New => Enterprise Application Project(图 6)。
图 6. 新建企业应用程序项目
在 New EAR Application Project 向导的 Target Runtime 部分下,单击 New。
在 New Server Runtime 向导(图 12)中,选择 WebSphere Application Server v6.1 并单击 Next。
图 7. WebSphere Application Server v6.1 运行时
如果您的 WebSphere Application Server 安装目录与缺省目录不同,请输入您的安装目录名称。单击 Finish。
为项目 OrderSystem 命名,并确保 Target Runtime 是 WebSphere Application Server v6.1(图 8)。(请不要选择存根选项)。单击 Finish。
图 8. EAR Application Project 向导
您的 EAR 文件应显示一个错误,这是因为没有 Java EE 模块。EJB 3.0 是一个普通的 POJO 模型,因此,您需要创建一个简单的 Java 项目作为包含您的 EJB 3.0 POJO 的实用工具项目。
右键单击 OrderSystem EAR,并选择 New=>Other(图 9)。
图 9. 创建新项目
展开 J2EE => Utility Project,并单击 Next(图 10)。实用工具项目是一个普通的 Java 项目,它被打包在 EAR 中。稍后将在 application.xml 中将此项目标记为 EJB 模块。
图 10. 选择 Utility Project
命名项目 OrderSystemEJB(图 11)。确保 EAR Project Name 与已创建的 EAR 项目匹配。
图 11. Utility Project 向导
现在,您需要更新该 EAR 文件,以便该文件将 Java 项目视为 EJB 模块:
展开 OrderSystem 并双击 Deployment Descriptor Editor(图 12)。
图 12. 打开 Deployment Descriptor
转到源标记,如图 13 所示。将以下 XML 片段添加到显示名标记之后:
2 <ejb>OrderSystemEJB.jar</ejb>
3 </module>
结果应该与图 13 类似
图 13. application.xml
保存编辑器并关闭 Application Deployment Descriptor。
将本地 WebSphere Application Server 作为服务器添加到 Application Server Toolkit 工作区中:
在 J2EE 透视图中,转到 Servers 选项卡。在服务器列表中右键单击,然后选择 New=>Server(图 14)。
图 14. 新服务器
接受缺省值,然后单击 Next。
取消选中 Run server with resources within the workspace,然后单击 Finish。
图 15. WebSphere 服务器设置
右键单击新服务器,并选择 Start。
添加 Derby 数据库的数据源,您已经用 CUSTSCH-derby.ddl 中提供的 CUSTSCH 模式填充了该数据库:
右键单击该服务器,然后选择 Run administrative console(图 16)。
图 16. 启动管理控制台
图 17. 创建 DataSource
在步骤 1 的面板(图 18)上,为 Data source name 输入 Order DS,为 JNDI name 输入 jdbc/orderds,然后单击 Next。
图 18. DataSource 向导
在步骤 2 的面板(图 19)上,选择 Create new JDBC Provider,然后单击 Next。
图 19. JDBC 提供程序
在步骤 2.1 的面板(图 20)上,选择以下值:
Database type:Derby
Provider type:Derby Network Server Using Derby Client
Implementation type:Connection pool data source
在 Description 中输入可选的描述文本,然后单击 Next。
图 20. JDBC 提供程序
在步骤 3 的面板(图 21)上,在 Database name 中输入 JPATEST,然后单击 Next。
图 21. 特定于数据库的属性
在管理控制台中,导航到 Resources=>JDBC=>Data sources。
选择该数据源的范围(图 17)。您所做的选择因您的安装而不同。请为您的本地服务器选择一个服务器范围,然后单击 New。
在步骤 4 的面板(图 22)上,检查数据,确保正确地输入了所有的值,然后单击 Finish。
图 22. 摘要
通过单击该页顶部的 Save 链接保存所做的更改。
确认 Derby 网络数据库正在运行,然后选择 Order DS 数据源,并单击 Test Connection(图 23)。
图 23. 测试连接
您会在该面板的顶部收到一条类似于图 24 中所示的消息
图 24. 连接确认
创建第一个 JPA POJO
EJB 3.0 规范定义了新的永久体系结构,即 Java Persistence Architecture (JPA),使用它可以将简单的 POJO 映射到关系数据库中。JPA POJO 已不再是 EJB 组件。相反,EJB 3.0 规范定义了在 EJB 3.0 容器中管理 JPA 对象的方式。但是,JPA 应用程序仍可以在 Java SE 环境中运行。在此示例中,您将创建一个在 WebSphere Application Server 中运行的 JPA 对象。
首先,创建一个简单的 POJO:
展开 OrderSystemEJB 项目。右键单击 src 目录,并选择 New => Class(图 25)。
图 25. 新建类
在 New Java Class 面板中(图 26),将该类存储在一个名为 com.ibm.ejb3.order.entities 的数据包中,并将该类命名为 Customer,然后单击 Finish。
图 26. 类向导
创建两个简单的属性:
customerId:int
name:String
2
3 import java.io.Serializable;
4
5 public class Customer implements Serializable
6 {
7 private int customerId;
8 private String name;
9 }
在 Ourtline 视图中,右键单击 Customer 类,然后选择 Source => Generate Getters and Setters(图 27)。
图 27. Generate Getters and Setters
单击 Select All 和 OK(图 28)。
图 28. 选择属性
得到的结果是一个简单的 POJO,如图 29 所示。
图 29. Customer 类
现在已经可以将您的 POJO 作为 JPA 实体。在大多数情况下,您所需要的是一个良好的 Java 编辑器。此示例使用了 Eclipse 中用来创建 JPA 应用程序的编辑器:
在该类声明上方,开始输入 @Enti,然后按 Ctrl-Space以触发 Eclipse 内容辅助功能。选择 @Entity。(图 30)
图 30. Entity 注释
Entity 类需要一个主键。在 customerId 属性上方,添加 @Id 注释(图 31)。
图 31. Id 注释
如果您的属性名称与该数据库的名称匹配,则表示已完成,您已经创建了第一个 JPA POJO。但是,此示例中的模式稍有不同,您可以使用 JPA Eclipse 工具来获取帮助:
切换到 Java Persistence 透视图。通过右键单击面板右上角的透视图图标并选择 Other 可以开始 Open Perspective 向导。(图 32)
图 32. 打开 Other
从显示的列表中选择 Java Persistence。
转到 Database Explorer 视图。若要加载数据库,请右键单击 Connections,然后选择 New => Connection...(图 33)
图 33. 新建连接
输入或选择下列属性(图 34):
取消选中 Use default naming convention
Connection Name: OrderDB
Database Manager:Derby => 10.1
Database Name: JPATEST
Class Location:导航到 Derby lib 目录(例如:\WebSphere\AppServer\derby\lib\derbyclient.jar)
User Id 和 Password:为您的系统输入适当的值。
图 34. 连接参数
导航到 OrderDB => JPATEST => Schemas => CUSTSCH => Tables => CUSTOMER 并检查这些字段(图 35)。
图 35. CUSTOMER 表
您需要将 persistence.xml 文件添加到 JPA 项目。此文件是用于配置 JPA 属性的 JPA 部署描述符。您可以使用 JPA 开发工具执行以下操作:
在 Package Explorer 视图中,右键单击 OrderSystemEJB 并选择 Java Persistence => Add Java Persistence...(图 36)
图 36. 添加 Java Persistence 支持
对于 Connection,请选择 OrderDB,对于 Schema,请选择 CUSTSCH。(该连接可能已超时。如果已超时,请选择 Reconnect... 链接并输入同一用户 ID 和密码)。对于 Persistence unit name,请输入 OrderDB(图 37)。
图 37. JPA 项目内容
执行这些操作之后,您的 Customer POJO 将出现错误,这是因为 Eclipse JPA 插件将检测到该数据库模型与 JPA 对象模型之间不匹配。您将在下一步中修复此问题。
打开 OrderSystemEJB 的 META-INF 文件夹,然后打开新生成的 persistence.xml(图 38)。
图 38. persistence.xml
您需要将该数据源添加到 JPA 提供程序。打开 persistence.xml 然后单击 Source 选项卡。将此 xml 标记添加到 persistence-unit 标记中,如图 39 所示:
jdbc/orderds
图 39. 添加 DataSource
保存并关闭 persistence.xml。(由于该文件将在 Java EE 容器中运行,所以将使用缺省的提供程序。)
由于该数据库与您的 POJO 不精确匹配,所以需要稍微修复您的映射:
在 Outline 视图中,选择 customerId 属性。
在该面板的右下角,确保将 Map As 设置为 Id。在 Column Name 下,选择 CUST_ID。确保其余的字段与图 40 中所示的值匹配。
图 40. Persistence 属性
选择 PK Generation 选项卡。选中 Primary Key Generation 然后为 Strategy 选择 Identity(图 41)。JPA 启用了几个自动化主键生成策略。在此示例中,JPA 将委派给 DB2 的标识功能。
图 41. 标识策略
在 Customer POJO 中,您需要覆盖缺省的模式名称,这是因为该数据库将在另一个模式中存储。添加 @Table 注释,如图 42 所示。为 name 添加 CUSTOMER,为 schema 添加 CUSTSCH。
图 42. 设置模式
保存并关闭 Customer.java。
创建简单的会话 Bean
与 JPA 非常相似,EJB 3.0 的所有内容都通过 POJO 编程模型得到了简化,从而使得 EJB 3.0 非常易于编码。下面,您将创建一个简单的会话 Bean,该会话 Bean 将使用 JPA API 来访问客户 JPA POJO。
首先,需要创建一个简单的接口;创建接口通常是一个很好的实践。否则,EJB 提供程序将为您生成一个。在此练习中,仅创建一个简单的接口。
在 OrderSystemEJB 项目下,右键单击 src 文件夹,然后选择 New=>Interface(图 43)。
图 43. 新建接口
对于 Package 名称,请输入或浏览到 com.ibm.ejb3.order.session。对于类名称,请输入 CustomerTask(图 44)。
图 44. 接口向导
将以下方法添加到该接口,然后单击 Finish。
2
您会收到一条错误,这是因为没有称为 CustomerDoesNotExist 的类。请使用 Eclipse 建议创建该类,如图 45 所示。
图 45. 创建异常
确保选中了 Constructors from superclass,并且超类是 java.lang.Exception(图 46)。
图 46. 类向导
接下来,将 @Local 注释添加到类声明上方,如图 47 中所示。这可以使您的会话 Bean 成为本地 EJB。
图 47. 本地注释
在该编辑器中右键单击,然后选择 Source => Organize Imports(图 48)
图 48. 添加导入
接下来将创建会话 Bean POJO:
右键单击 com.ibm.ejb3.order.session 包,然后选择 New => Class(图 49)。
图 49. 新建类
将该类命名为 CustomerTaskImpl,将该 CustomerTask 接口添加到接口列表中(图 50)。
图 50. 类向导
要使 CustomerTaskImpl 成为会话 Bean,请添加 @Stateless 注释,如图 51 所示。这可使您的 POJO 成为无状态会话 Bean。
图 51. 无状态注释
EJB 3.0 容器还是一个注入服务器。要具有持久操作,您需要一个实体管理器。这可以通过注入轻松地添加到该会话 Bean 中。当在 EJB 3.0 容器中使用 JPA 时,该容器会自动管理,为您传递正确的持久上下文。这就是所谓的容器管理的实体管理器。通过使用此样式的 JPA,EJB 3.0 容器将正确地管理持久上下文的生命周期,并跨 EJB 调用传播正确的上下文。
将以下所示的 EntityManager 声明添加到 CustomerTaskImpl 类中。
@PersistenceContext(name="OrderDB")
EntityManager em;
您可以使用代码辅助功能引入 PersistenceContext 导入(图 52)。
图 52. PersistenceContext 注释
将此代码片段添加到 findCustomer 方法中:
Customer customer = (Customer) em.find(Customer.class, customerId);
if (customer == null) {
throw new CustomerDoesNotExist("Customer does not exist");
}
return customer;
如前所述组织导入(图 53),然后保存并关闭该文件。
图 53. 客户实现
导入客户端
接下来,将使用 Servlet 导入简单的 WAR 文件,并将使用该 Servlet 测试您的会话 Bean 和 JPA 对象。该 Servlet 演示从 Web 应用程序访问会话 Bean 会有多么方便。本文中包括了一个简单的 WAR 文件,并且已提供代码。
导入 Web 应用程序并检查该代码:
从文件菜单选择 File => Import。
在 Import 向导中,展开 Web => WAR File(图 54)。
图 54. 导入 WAR 文件
对于 WAR 文件,请单击 Browse 定位到您保存所提供的 OrderClient.war 文件的位置。对于 Web 项目,请保留缺省名称,确保 OrderSystem 是 EAR 项目名称(图 55)。
图 55. 选择要导入到的 EAR 项目
接下来,您可以检查 Servlet 代码。
展开 Java Resources:src 文件夹,并打开 EJBClientServlet.java 文件(图 56)。
图 56. 测试 Servlet
注意,EJB 会话 Bean 可以正好注入到 Servlet 中。
2 implements javax.servlet.Servlet {
3 /* (non-Java-doc)
4 * @see javax.servlet.http.HttpServlet#HttpServlet()
5 */
6
7 @EJB
8 private CustomerTask customerTask = null;
在 displayCustomerAndOrder 方法中,请注意,如果您具有该会话 Bean 实例,也只是简单的 Java。
2 CustomerDoesNotExist {
3 Customer customer = customerTask.findCustomer(customreId);
4 out.println("<br>Customer ID => " + customer.getCustomerId());
5 out.println("<br>Customer Name => " + customer.getName());
现在可以部署并测试该应用程序:
从 J2EE 透视图中,转到 Servers 视图,右键单击该服务器并按下 Start(图 57)。
图 57. 启动服务器
在服务器启动后,再次右键单击该服务器并选择 Add and Remove Projects...(图 58)
图 58. 添加项目
通过从左边的 Available projects 列表中选择 OrderSystem 应用程序,然后按下 Add 添加该应用程序(图 59)。
图 59. 移动项目
回到 Project Explorer 视图中,展开 Deployment Descriptor => Servlets,然后右键单击 EJBClientServlet 并选择 Run As => Run on Server(图 60)。
图 60. 运行 Servlet
选择 Choose an existing server 并选中 Set server as project default 选项,然后选择 Finish(图 61)。
图 61. 按照缺省形式关联服务器
浏览器应该显示 Customer ID 和 Customer Name(图 62)。
图 62. 检查浏览器
关闭浏览器并从该服务器中删除该项目(图 63 和 64)
图 63. 删除该项目
图 64. 删除该项目
关闭并保存所有的文件。
高级 EJB 功能
EJB 3.0 在不影响功能的情况下提供了简单的编程模型。基本原则是使用缺省值,仅在必要时才使用其他值覆盖缺省值。例如,EJB 3.0 仍允许 XML 配置。XML 部署描述符可以覆盖注释,因此,您可以针对不同的部署环境外部化配置并覆盖设置。在本部分中,您将继续使用注释,但是将向该应用程序添加更多功能,以便了解 EJB 3.0 的一些其他特征。(此处使用的应用程序是专门为说明不同的 EJB 3.0 和 JPA 功能而设计的,而不是作为应用程序设计非常好的实践的一个示例。)
本部分的操作步骤包括:
构建 JPA 对象
构建另一个会话 Bean
添加拦截器
测试应用程序
A. 构建 JPA 对象
以下步骤介绍了如何使用 JPA Eclipse 工具来生成更多 JPA POJO,然后将更新映射,以针对用例进行自定义。
JPA Eclipse 工具支持自顶向下、中间相遇、自底向上映射。您将使用自底向上工具来生成其余的实体:
在 Package Explorer 视图中,右键单击 OrderSystemEJB 并选择 Java Persistence => Generate Entities(图 65)。
图 65. 生成实体
选择您的连接(您的数据库连接现在可能已关闭;必要时请选择 Reconnect 链接并再次输入您的凭据),然后选择 CUSTSCH 模式。按 Next(图 66)。
图 66. 选择连接和模式
在 Generate Entities from Tables 面板上,仅选择 LINEITEM、CUSTORDER 和 PRODUCT。确保您的 Package 名称为 com.ibm.ejb3.order.entities。由于 CUSTORDER 表名称不同于实体名称(“Order”,这是该实体所需的名称),因此请输入 Order 作为 CUSTORDER 的实体名称(图 67)。单击 Finish。
图 67. 选择表
接下来将更新这些映射:
打开 Order 实体(图 68)。
图 68. 检查 Order 类
使用 Java Persistence Properties Editor 或添加以下用粗体标记的注释:
需要使用 @Table 注释来覆盖该模式名称。
@NamedQuery 将向您的 JPA POJO 添加一个查询。JPA 有非常丰富的查询语言可供您使用,即 EJB-QL。此查询将向该客户询问开放订单。
与您前面的操作相同,必须将订单属性注释为 ID。另外,您将添加标识的生成策略。
最后,请注意 Order 与 LineItems 有关系。您需要添加 Eager 的获取规则。这意味着,当有人请求订单时,JPA 引擎还将获取与该订单相关的所有行项目。
2
3 @Table(name="CUSTORDER", schema="CUSTSCH")
4
5 @NamedQuery(name="getCurrentOrder",
6
7 query="select o from Order o where o.customerId = :customerId and
8
9 o.status ='OPEN'")
10
11 public class Order implements Serializable {
12
13 @Id
14
15 @Column(name="ORDER_ID")
16
17 @GeneratedValue(strategy=GenerationType.IDENTITY)
18
19 private int orderId;
20
21 private String status;
22
23 private double total;
24
25 @Column(name="CUSTOMER_ID")
26
27 private int customerId;
28
29 @OneToMany(mappedBy="orderId",fetch=FetchType.EAGER)
30
31 private Set lineitemCollection;
32
33
您需要更新该 Customer 实体,使之与 Order 关联。系统要求 Customer 仅有一个开放订单。您需要创建与该开放订单的关系。由于一个 Customer 有许多订单,因此您不需要生成该关系,但当该客户仅有一个开放订单时则需要生成该关系。
打开 Customer 实体。如以下代码所示,添加 Order currentOrder。使用 Eclipse Java 工具生成 setters 和 getters。
2
3 import java.io.Serializable;
4 import javax.persistence.Entity;
5 import javax.persistence.Id;
6 import javax.persistence.GeneratedValue;
7 import javax.persistence.GenerationType;
8 import javax.persistence.Column;
9 import javax.persistence.Table;
10
11 @Entity
12 @Table(name="CUSTOMER", schema="CUSTSCH")
13 public class Customer implements Serializable {
14
15 @Id
16 @GeneratedValue(strategy=GenerationType.IDENTITY)
17 @Column(name="CUST_ID")
18 private int customerId;
19 private String name;
20
21
22 private Order currentOrder;
23
24 public int getCustomerId() {
25 return customerId;
26 }
27 public void setCustomerId(int customerId) {
28 this.customerId = customerId;
29 }
30 public String getName() {
31 return name;
32 }
33 public void setName(String name) {
34 this.name = name;
35 }
36 public Order getCurrentOrder() {
37 return currentOrder;
38 }
39 public void setCurrentOrder(Order currentOrder) {
40 this.currentOrder = currentOrder;
41 }
42
43 }
如下所示,添加以下 @OneToOne 和 @JoinColumn 注释。
注意,该获取规则被设置为 Lazy,所以当有人查询该客户时,您将不获取该订单。
您已经设置某些级联规则。如果有人更新该开放订单,更改将按照定义级联。
该关系是可选的,这意味着,该客户并不能始终拥有开放订单。
您已经指定了 Join 列(该名称与 Customer 表上的外键对应,而 referencedColumn 名称与 Custorder 表的主键对应。)如果您未指定 Join 列,JPA 将根据命名方案尝试解析这些键。
2 {CascadeType.MERGE,CascadeType.REFRESH},optional=true )
3
4 @JoinColumn(name="OPEN_ORDER_ID",referencedColumnName="ORDER_ID")
5 private Order currentOrder;
像处理 Customer 和 Order 那样,更新 LineItem 类以指向正确的模式(图 69)。
图 69. LineItem 模式
类似地,覆盖产品模式(图 70)。
图 70. 产品模式
保存并关闭实体。
构建另一个会话 Bean
在此,您将构建另一个会话 Bean,以了解如何将另一个会话 Bean 注入其中。
与前面一样,创建一个接口:
在会话数据包中,选择 New => Interface(图 71)并命名 Interface OrderTask。单击 Finish。
图 71. 新建接口
如下所示,将该类注释为 Local,并添加 getCurrentOrder 方法。
2
3 import com.ibm.ejb3.order.entities.Order;
4
5 @Local
6 public interface OrderTask {
7 public Order getCurrentOrder(int customerId) throws CustomerDoesNotExist;
8 }
创建新的无状态会话 Bean。
右键单击 session 的数据包并选择 New => Class(图 72)。
图 72. 新建类
确保该类实现了您刚创建的 OrderTask 接口(图 73)。
图 73. OrderTaskImpl
将该 Bean 注释为无状态。添加下面以粗体显示的代码。注意,您可以将 CustomerTask 注入到 OrderTask 中。这避免了 JNDI 查找代码,因此极大地简化了操作。另外,还将像前面那样注入一个实体管理器。
2
3 import javax.ejb.EJB;
4 import javax.ejb.Stateless;
5
6 import com.ibm.ejb3.order.entities.Order;
7
8 @Stateless
9 public class OrderTaskImpl implements OrderTask {
10
11 @EJB(beanName = "CustomerTaskImpl")
12 CustomerTask customerTask;
13
14 @PersistenceContext(name="OrderDB")
15 EntityManager em;
16
17 public Order getCurrentOrder(int customerId) throws CustomerDoesNotExist {
18 // TODO Auto-generated method stub
19 return null;
20 }
21
22 }
最后,将下面的粗体代码添加到 getCurrentOrder 方法中。您会注意到访问查询有多么方便。另外请注意,订单方法将与 Customer 对象交互。因为两个类都在使用 @PersistenceContext,所以它们可以正确传播 Entity Manager,了解这一点非常重要。
2
3 Query query = em.createNamedQuery("getCurrentOrder");
4
5 query.setParameter(1, customerId);
6
7 Order currentOrder = (Order)query.getSingleResult();
8
9 if(currentOrder != null)
10
11 {
12
13 Customer customer = customerTask.findCustomer(customerId);
14
15 Order alreadySet = customer.getCurrentOrder();
16
17 if(alreadySet == null)
18
19 {
20
21 customer.setCurrentOrder(currentOrder);
22
23 }
24
25 }
26
27 return currentOrder;
28
29 }
30
31
不要忘记组织您的导入。
添加拦截器
EJB 3.0 还增加了在您的应用程序中使用面向方面的编程技术这一能力。EJB 3.0 支持拦截器,这使您能够拦截用于横切技术的代码。接下来,您将创建审核拦截器,该拦截器将显示客户在管理控制台上的操作。
创建简单的 Audit 类:
创建一个称为 AuditInterceptor 的类(图 74)。
图 74. 新建 Java 类
添加下面以粗体显示的代码。@AroundInvoke 将告诉该容器拦截器进行拦截时将调用什么方法。该代码访问 customerId 和方法名称并将它们显示出来。
2
3 import java.lang.reflect.Method;
4
5 import javax.interceptor.AroundInvoke;
6 import javax.interceptor.InvocationContext;
7
8 public class AuditInterceptor {
9
10 @AroundInvoke
11 public Object audit(InvocationContext invocationContext) throws Exception
12 {
13 Method operation = invocationContext.getMethod();
14 String name = operation.getName();
15 Object param[] = invocationContext.getParameters();
16 System.out.println
17 ("Customer Id " + param[0] + " executing operation => " + name);
18 return invocationContext.proceed();
19 }
20
21 }
您可以通过使用 @Interceptor 注释来注释类,将拦截器添加到会话 Bean 中。为了简便起见,在此练习中可以这样做,但是对于类来说,非常好的实践是不要知道谁在拦截它。EJB 3.0 支持部分 XML 部署描述符,其中您可以定义外部化拦截器与类匹配的拦截器绑定。外部化拦截器通常是非常好的实践。
如下所示,打开 OrderTaskImpl 类并添加 @Interceptors (AuditInterceptors.class)。
2 @Interceptors(AuditInterceptor.class)
3 public class OrderTaskImpl implements OrderTask {
对 CustomerTask 执行相同的操作。
2 @Interceptors(AuditInterceptor.class)
3 public class CustomerTaskImpl implements CustomerTask {
添加拦截器
您需要修改该客户端以调用该新功能:
本文中提供了您所需要的代码,但是这些代码被注释掉了。取消下面以粗体显示的 OrderTask 声明的注释。
2 javax.servlet.Servlet {
3 /* (non-Java-doc)
4 * @see javax.servlet.http.HttpServlet#HttpServlet()
5 */
6
7 @EJB
8 private CustomerTask customerTask = null;
9
10 @EJB
11 private OrderTask orderTask = null;
12
13 public EJBClientServlet() {
14 super();
15 }
16
17 @Override
18 public void init() throws ServletException {
19 super.init();
20 }
取消以下粗体代码的注释。注意,您可以通过 LineItems 访问订单和遍历功能。由于您设置了强获取规则,所以将填写 LineItems。
2 CustomerDoesNotExist {
3 Customer customer = customerTask.findCustomer(customerId);
4 out.println("<br>Customer ID => " + customer.getCustomerId());
5 out.println("<br>Customer Name => " + customer.getName());
6
7 Order order = orderTask.getCurrentOrder(customerId);
8 if(order != null)
9 {
10 out.println("<br>order id => " + order.getOrderId());
11 out.println("<br>status => " + order.getStatus());
12 out.println("<br>Total => " + order.getTotal());
13 Collection<Lineitem> lineItems = order.getLineitemCollection();
14 for(Lineitem lineItem: lineItems)
15 {
16 out.println("<br>****");
17 out.println("<br>Line Item Id => " + lineItem.getLiId());
18 out.println("<br>Quantity => " + lineItem.getQuantity());
19 out.println("<br>Total => " + lineItem.getAmount());
20 }
21
22 }
23 else
24 {
25 out.println("<br>Customer has no open order...");
26 }
27
28 }
如前所述,将该项目添加到服务器中(图 75)。
图 75. 再次添加项目
运行 Servlet(图 76)。
图 76. 在服务器上运行
浏览器应显示该客户、当前的订单和所有的行项目(图 77)。
图 77. 检查结果
最后,检查该控制台以查看该拦截器的打印语句(图 78)。您会注意到,findCustomer 任务被调用了两次,一次是从客户端,另一次是从订单任务,而 getCurrentOrder 任务被调用了一次。
图 78. 检查拦截器结果
结束语
EJB 3.0 是简化企业中的应用程序开发的主要发展方向。本文向您介绍了如何使用简化的编程模型、新的 Java Persistence API 和拦截器为 WebSphere Application Server V6.1 构建 EJB 3.0 应用程序。通过使用 EJB 3.0 Feature Pack for WebSphere Application Server,您可受益于简化的开发体验、新的持久性模型和一些新功能(如拦截器),同时仍可以将其部署到强健的 WebSphere 平台。