原文地址:
http://testwww.netbeans.org/kb/55/ejb30_zh_CN.html
EJB 3.0 Enterprise Beans
本文档介绍了有关使用 EJB 3.0 技术(作为 Java EE 5 平台的一部分)开发企业应用程序的基础知识,同时说明了 EJB 3.0 技术是如何简化企业应用程序的开发过程的。本文档使用的是 NetBeans IDE 5.5 发行版本。
预计持续时间:30 分钟
先决条件
本文档假定您已具备了以下技术的一些基本知识或编程经验:
本教程所需的软件
在学习本教程之前,您需要在计算机中安装以下软件:
- NetBeans IDE 5.5(下载)
- Java Standard Development Kit (JDK) 版本 5.0 或版本 6.0(下载)
- Sun Java System Application Server Platform Edition 9(下载)
在学习本教程之前,您需要在 IDE 中注册 Sun Java System Application Server 的本地实例。
教程练习
建立企业应用程序项目
本练习的目的是:创建包含一个 EJB 模块和一个 Web 模块的 NewsApp 企业应用程序项目。NewsApp 应用程序使用消息驱动 Bean 接收和处理 Servlet 发送到队列中的消息。该应用程序使用 Servlet 将消息发送到消息驱动 Bean 并显示消息。
创建企业应用程序
- 从主菜单中选择“文件”>“新建项目”(Ctrl-Shift-N)。
- 从“企业”类别中选择“企业应用程序”,然后单击“下一步”。
- 将项目命名为 NewsApp,并将服务器设置为 Sun Java System Application Server。
- 将 J2EE 版本设置为 "Java EE 5",然后选中“创建 EJB 模块”和“创建 Web 应用程序模块”(如果未选中)。
- 单击“完成”。
小结
在本练习中,我们创建了包含一个 EJB 模块和一个 Web 模块的 Java EE 5 企业应用程序。
对 EJB 模块进行编码
在本练习中,我们将在 EJB 模块中创建对象。我们将创建实体类、消息驱动 Bean 和会话 Facade。我们还将创建一个持久性单元,以便为容器提供用于管理实体的信息,以及消息驱动 Bean 将使用的 Java 消息服务 (Java Message Service, JMS) 资源。
创建持久性单元
首先,我们将创建一个持久性单元,它用于定义在应用程序中使用的数据源和实体管理器。
- 右键单击 EJB 模块,然后选择“新建”>“文件/文件夹”。
- 从“持久性”类别中,选择“持久性单元”,然后单击“下一步”。
- 保留缺省的持久性单元名称。
- 对于持久性提供程序,请选择“TopLink(缺省)”。
- 对于数据源,请选择缺省的数据源 jdbc/sample。
- 检查是否为持久性单元选中了“使用 Java 事务 API”,以及“表生成策略”是否设置为“创建”,以便在部署应用程序时创建基于实体类的表。
- 单击“完成”。
单击“完成”后,IDE 将创建 persistence.xml,并在源代码编辑器中将其打开。关闭 persistence.xml。
创建 NewsEntity 实体类
在本练习中,我们将创建 NewsEntity 实体类。实体类是一个简单的 Java 类。在创建实体类时,IDE 会添加 @Entity 标注以将该类定义为实体类。当创建了类后,我们将在该类中创建字段以表示表中所需的数据。
每个实体类都必须具有一个主键。在创建实体类时,IDE 会添加 @Id 标注以声明要用作主键的字段。此外,IDE 还会添加 @Generated 标注以指定主 Id 的键生成策略。
要创建 NewsEntity 类,请执行以下操作:
- 在“项目”窗口中右键单击 EJB 模块,然后选择“新建”>“文件/文件夹”打开“新建文件”向导。
- 从“持久性”类别中,选择“实体类”,然后单击“下一步”。
- 键入 NewsEntity 作为类名,键入 ejb 作为包名,并将“主键类型”保留为 Long。单击“完成”。
单击“完成”后,将在源代码编辑器中打开实体类 NewsEntity.java。在源代码编辑器中,请执行以下操作:
- 将以下字段声明添加到类中:
String title;
String body;
- 在源代码编辑器中单击鼠标右键,然后选择“重构”>“封装字段”以便为每个字段生成 getter 和 setter。在“封装字段”对话框中,确保选中了字段 id、title 和 body 的 getter 和 setter 复选框。
- 在“封装字段”对话框中单击“下一步”,然后在“输出”窗口的“重构”标签中单击“执行重构”。IDE 将为各字段添加 getter 和 setter 方法,并将字段的可视性更改为 private。
- 保存对文件所做的更改。
在接下来的步骤中,我们将创建 NewMessage 消息驱动 Bean。
创建 NewMessage 消息驱动 Bean
现在我们将在 EJB 模块中创建 NewMessage 消息驱动 Bean。我们将使用“新建消息驱动 Bean”向导来创建 Bean 和所需的 JMS 资源。
要创建 NewMessage 消息驱动 Bean,请执行以下操作:
- 在“项目”窗口中右键单击 EJB 模块,然后选择“新建”>“文件/文件夹”以打开“新建文件”向导。
- 从“企业”类别中,选择“消息驱动 Bean”,然后单击“下一步”。
- 键入 NewMessage 作为类名。
- 从“包”下拉列表中选择 "ejb"。
- 选择“队列”作为目标类型,然后单击“完成”。
单击“完成”后,将在源代码编辑器中打开新建的消息驱动 Bean 类 NewMessage.java。您可以看到该类具有以下标注:
@MessageDriven(mappedName = "jms/NewMessage")
此标注向容器说明:该组件是一个消息驱动 Bean 并且该 Bean 使用 JMS 资源。当 IDE 生成类时,将从类 (NewMessage.java) 名称派生资源的映射名 (jms/NewMessage)。JMS 资源被映射到目标的 JNDI 名称,Bean 从该目标中接收消息。“新建消息驱动 Bean”向导已为我们创建了 JMS 资源。通过 EJB 3.0 API,我们可以从 Bean 类中查找 JNDI 名称空间中的对象,这样就不需要配置部署描述符来指定 JMS 资源了。
EJB 3.0 规范允许您使用标注将资源直接引入类中。现在,我们准备使用标注将 MessageDrivenContext 资源引入类中,然后注入 PersistenceContext 资源,EntityManager API 将使用该资源来管理持久性实体实例。我们要在源代码编辑器中将标注添加到类中。
- 通过在类中添加以下带标注的字段(以粗体显示),将 MessageDrivenContext 资源注入到类中:
public class NewMessage implements MessageListener {
@Resource
private MessageDrivenContext mdc;
- 在代码中单击鼠标右键,然后从弹出式菜单中选择“持久性”>“使用实体管理器”,将实体管理器引入类中。
这会在源代码中添加以下标注:
@PersistenceContext
private EntityManager em;
并在代码中生成以下方法:
public void persist(Object object) {
// TODO:
// em.persist(object);
}
- 按如下所示修改 persist 方法:
public void save(Object object) {
em.persist(object);
}
- 通过将以下代码添加到主体中来修改 onMessage 方法:
ObjectMessage msg = null;
try {
if (message instanceof ObjectMessage) {
msg = (ObjectMessage) message;
NewsEntity e = (NewsEntity) msg.getObject();
save(e);
}
} catch (JMSException e) {
e.printStackTrace();
mdc.setRollbackOnly();
} catch (Throwable te) {
te.printStackTrace();
}
- 按 Alt-Shift-F 组合键生成所有必要的 import 语句。在生成 import 语句时,我们需要确保导入 jms 和 javax.annotation.Resource; 库。
- 保存该文件。
创建会话 Bean
接下来,我们将为 NewsEntity 实体类创建一个会话 Facade。要创建会话 Facade,请执行以下操作:
- 右键单击 EJB 模块,然后选择“新建”>“文件/文件夹”。
- 从“持久性”类别中,选择“实体类的会话 Bean”,然后单击“下一步”。
- 从可用的实体类列表中,选择 "NewsEntity",单击“添加”,然后单击“下一步”。
- 检查是否将包设置为 ejb 以及是否选中了创建本地接口。
- 单击“完成”。
单击“完成”后,将创建会话 Facade 类 NewsEntityFacade.java,并在源代码编辑器中将其打开。IDE 还将创建本地接口 NewsEntityFacadeLocal.java。
EJB 3.0 技术通过减少所需的代码量来简化会话 Bean 的创建。您可以看到,标注 @Stateless 用于将类声明为无态会话 Bean 组件,并且该类不再需要实现 javax.ejb.SessionBean 的语句。此外,代码也更为简洁了,因为利用 EJB 3.0 技术,业务方法不再需要使用代码来声明其抛出了所检查到的异常。
您会看到,在创建会话 Facade 时,PersistenceContext 资源已直接注入到会话 Bean 组件中。
小结
在本练习中,我们已对 EJB 模块中的实体类和消息驱动 Bean 进行了编码,然后为实体类创建了会话 Facade。此外,我们还创建了应用程序使用的 JMS 资源。
对 Web 模块进行编码
现在,我们将在 Web 模块中创建 Servlet ListNews 和 PostMessage。这些 Servlet 将用于读取和添加消息。
创建 ListNews Servlet
在本练习中,我们将创建一个用于显示数据的简单 Servlet。我们将使用标注从 Servlet 中调用实体 Bean。
- 右键单击 Web 模块项目,然后选择“新建”> "Servlet"。
- 键入 ListNews 作为类名。
- 输入 web 作为包名,然后单击“完成”。
单击“完成”后,将在源代码编辑器中打开类 ListNews.java。在源代码编辑器中,请执行以下操作:
- 在源代码中,单击鼠标右键,然后选择“企业资源”>“调用 Enterprise Bean”。
- 在“调用 Enterprise Bean”对话框中,选择 "NewsEntityFacade",然后单击“确定”。单击“确定”后,将使用 @EJB 标注在 Servlet 中注入实体 Bean 资源。
- 在 processRequest 方法中,对其进行如下修改:取消代码注释,然后将下面以粗体显示的行添加到方法主体中。
out.println("<h1>Servlet ListNews at " + request.getContextPath () + "</h1>");
List news = newsEntityFacade.findAll();
for (Iterator it = news.iterator(); it.hasNext();) {
NewsEntity elem = (NewsEntity) it.next();
out.println(" <b>"+elem.getTitle()+" </b><br />");
out.println(elem.getBody()+"<br /> ");
}
out.println("<a href='PostMessage'>Add new message</a>");
out.println("</body>");
- 按 Alt-Shift-F 组合键为类生成所有必要的 import 语句。在生成 import 语句时,我们希望从 util 包中导入类。
- 保存对文件所做的更改。
创建 PostMessage Servlet
在本练习中,我们将创建用于传递消息的 PostMessage Servlet。我们将使用标注将所创建的 JMS 资源直接注入 Servlet 中,并且指定变量名称及其映射到的名称。然后,添加用于发送 JMS 消息的代码,以及用于在 HTML 表单中添加消息的代码。
- 右键单击 Web 模块项目,然后选择“新建”> "Servlet"。
- 键入 PostMessage 作为类名。
- 输入 web 作为包名,然后单击“完成”。
单击“完成”后,将在源代码编辑器中打开类 PostMessage.java。在源代码编辑器中,请执行以下操作:
- 通过添加下面以粗体显示的字段声明,使用标注来注入 ConnectionFactory 和 Queue 资源:
public class PostMessage extends HttpServlet {
@Resource(mappedName="jms/NewMessageFactory")
private ConnectionFactory connectionFactory;
@Resource(mappedName="jms/NewMessage")
private Queue queue;
- 现在,通过将下面以粗体显示的代码添加到 processRequest 方法中,添加用于发送 JMS 消息的代码:
response.setContentType("text/html;charset=UTF-8");
// Add the following code to send the JMS message
String title=request.getParameter("title");
String body=request.getParameter("body");
if ((title!=null) && (body!=null)) {
try {
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer messageProducer = session.createProducer(queue);
ObjectMessage message = session.createObjectMessage();
// here we create NewsEntity, that will be sent in JMS message
NewsEntity e = new NewsEntity();
e.setTitle(title);
e.setBody(body);
message.setObject(e);
messageProducer.send(message);
messageProducer.close();
connection.close();
response.sendRedirect("ListNews");
} catch (JMSException ex) {
ex.printStackTrace();
}
}
PrintWriter out = response.getWriter();
- 现在,将对输出 HTML 的代码取消注释,并添加用于添加消息的 Web 表单。将下面以粗体显示的代码行添加到 processRequest 方法中:
out.println("Servlet PostMessage at " + request.getContextPath() + "</h1>");
// Add the following code to add the form to the web page
out.println("<form>");
out.println("Title: <input type='text' name='title'><br/>");
out.println("Message: <textarea name='body'></textarea><br/>");
out.println("<input type='submit'><br/>");
out.println("</form>");
out.println("</body>");
- 按 Alt-Shift-F 组合键为类生成所有必要的 import 语句。在选择 Connection、ConnectionFactory、Session 和 Queue 的 import 语句时,将导入 java.jms 库。
- 保存对文件所做的更改。
运行项目
现在可以运行项目了。在运行项目时,我们希望浏览器打开包含 ListNews Servlet 的页面。可以通过在企业应用程序的“属性”对话框中指定该页的 URL 来实现这一目的。该 URL 是应用程序的上下文路径的相对 URL。输入相对 URL 后,可以从“项目”窗口中生成、部署并运行应用程序。
要设置相对 URL 并运行应用程序,请执行以下操作:
- 在“项目”窗口中,右键单击 "NewsApp" 企业应用程序节点,然后从弹出式菜单中选择“属性”。
- 在“类别”窗格中选择“运行”。
- 在“相对 URL”文本字段中,键入 /ListNews。
- 单击“确定”。
- 在“项目”窗口中,右键单击 "NewsApp" 企业应用程序节点,然后选择“运行项目”。
运行项目时,将在浏览器中打开 ListNews Servlet,该 Servlet 用于显示数据库中的消息列表。如果您是第一次运行项目,则数据库为空,但是您可以单击“添加消息”来添加消息。
当您使用 PostMessage Servlet 添加消息时,消息会发送到消息驱动 Bean 以写入到持久性存储中,并会调用 ListNews Servlet 来显示数据库中的消息。由 ListNews 检索的数据库中的消息列表通常不包含新消息,这是因为消息服务是异步的。
疑难解答
下面是您创建项目时可能会遇到的一些问题。
JMS 资源问题
使用向导来创建 JMS 资源时,您可能会在输出窗口中看到以下服务器错误消息:
[com.sun.enterprise.connectors.ConnectorRuntimeException:
JMS resource not created : jms/Queue]
此消息可能表明没有创建 JMS 资源,或者没有在应用服务器中注册该资源。您可以使用 Sun Java System Application Server 管理控制台来检查、创建以及编辑 JMS 资源。
要打开管理控制台,请执行以下操作:
- 在 IDE 的“运行环境”中,展开“服务器”节点以确认 Sun Java System Application Server 正在运行。"Sun Java System Application Server" 节点旁边的小绿色箭头表示服务器正在运行。
- 右键单击 "Sun Java System Application Server" 节点,然后选择“查看管理控制台”以在浏览器中打开登录窗口。
- 登录到 Sun Java System Application Server。缺省的用户名为 admin,口令为 adminadmin。
- 在浏览器的管理控制台中,依次展开左框架中的“资源”节点和“JMS 资源”节点。
- 在左框架中单击“连接工厂”和“目标资源”链接以检查是否在服务器中注册了这些资源,并在必要时修改这些资源。如果这些资源不存在,您可以在管理控制台中创建这些资源。
您需要确保将 PostMessage Servlet 中的 JMS 连接工厂资源映射到在 Sun Java System Application Server 中注册的 JMS 连接工厂资源的对应 JNDI 名称上。
应在 Sun Java System Application Server 中注册以下资源:
- 具有 JNDI 名称 jms/NewMessage 和类型 javax.jms.Queue 的目标资源
- 具有 JNDI 名称 jms/NewMessageFactory 和类型 javax.jms.QueueConnectionFactory 的连接工厂资源
后续步骤
有关使用 NetBeans IDE 5.5 开发 Java EE 应用程序的更多信息,请参见以下资源:
您可以在 Java EE 5 教程中找到有关使用 EJB 3.0 Enterprise Beans 的详细信息。
要发送意见和建议、获得支持以及随时了解 NetBeans IDE Java EE 开发功能的最新开发情况,请加入 nbj2ee 邮件列表。