- EJB 教程
- EJB - 首页
- EJB - 概述
- EJB - 环境设置
- EJB - 创建应用程序
- EJB - 无状态 Bean
- EJB - 有状态 Bean
- EJB - 持久性
- EJB - 消息驱动 Bean
- EJB - 注解
- EJB - 回调
- EJB - 定时器服务
- EJB - 依赖注入
- EJB - 拦截器
- EJB - 可嵌入对象
- EJB - Blob/Clob
- EJB - 事务
- EJB - 安全性
- EJB - JNDI 绑定
- EJB - 实体关系
- EJB - 访问数据库
- EJB - 查询语言
- EJB - 异常处理
- EJB - Web 服务
- EJB - 打包应用程序
- EJB 有用资源
- EJB - 快速指南
- EJB - 有用资源
- EJB - 讨论
EJB - 快速指南
EJB - 概述
EJB 代表 **企业** **Java** **Bean**。EJB 是 J2EE 平台的重要组成部分。J2EE 平台具有基于组件的架构,为企业级应用程序提供多层、分布式和高事务特性。
EJB 提供了一种架构来开发和部署基于组件的企业应用程序,同时考虑了健壮性、高可扩展性和高性能。EJB 应用程序可以部署在任何符合 J2EE 1.3 标准规范的应用程序服务器上。
在本教程中,我们将详细讨论 EJB 3.0。
类型
EJB 主要分为三类;下表列出了它们的名字及其简要描述:
序号 | 类型和描述 |
---|---|
1 |
会话 Bean 会话 Bean 为特定用户的单个会话存储数据。它可以是 **有状态的** 或 **无状态的**。与实体 Bean 相比,它占用较少的资源。会话 Bean 在用户会话终止后会被销毁。 |
2 |
实体 Bean 实体 Bean 代表持久数据存储。用户数据可以通过实体 Bean 保存到数据库,稍后可以从实体 Bean 中的数据库中检索。 |
3 |
消息驱动 Bean 消息驱动 Bean 用于 JMS(Java 消息服务)的上下文中。消息驱动 Bean 可以从外部实体消费 JMS 消息并相应地执行操作。 |
优势
以下是 EJB 的重要优势:
简化大型企业级应用程序的开发。
应用程序服务器/EJB 容器提供大多数系统级服务,如事务处理、日志记录、负载平衡、持久性机制、异常处理等等。开发人员只需要关注应用程序的业务逻辑。
EJB 容器管理 EJB 实例的生命周期,因此开发人员无需担心何时创建/删除 EJB 对象。
EJB - 环境设置
EJB 是 Java 的框架,因此第一个要求是在您的机器上安装 **J**ava **D**evelopment **K**it(JDK)。
系统要求
JDK | 1.5 或更高版本。 |
---|---|
内存 | 没有最低要求。 |
磁盘空间 | 没有最低要求。 |
操作系统 | 没有最低要求。 |
步骤 1 - 验证系统中 Java 的安装
现在打开控制台并执行以下 **java** 命令。
操作系统 | 任务 | 命令 |
---|---|---|
Windows | 打开命令控制台 | c:\> java -version |
Linux | 打开命令终端 | $ java -version |
Mac | 打开终端 | machine:~ joseph$ java -version |
让我们验证所有操作系统的输出:
操作系统 | 输出 |
---|---|
Windows |
java version "1.6.0_21" Java(TM) SE Runtime Environment (build 1.6.0_21-b11) Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode) |
Linux |
java version "1.6.0_21" Java(TM) SE Runtime Environment (build 1.6.0_21-b11) Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode) |
Mac | java version "1.6.0_21" Java(TM) SE Runtime Environment (build 1.6.0_21-b11) Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode) |
如果您没有安装 Java,请从 www.oracle.com 安装 Java 软件开发工具包 (SDK)。在本教程中,我们假设 Java 1.6.0_21 为已安装版本。
步骤 2 – 设置 JAVA 环境
设置 **JAVA_HOME** 环境变量以指向 Java 在系统中安装的基本目录位置。例如,
操作系统 | 输出 |
---|---|
Windows | 将环境变量 JAVA_HOME 设置为 C:\Program Files\Java\jdk1.6.0_21 |
Linux | export JAVA_HOME=/usr/local/java-current |
Mac | export JAVA_HOME=/Library/Java/Home |
将 Java 编译器位置添加到系统路径。
操作系统 | 输出 |
---|---|
Windows | 将字符串 ;C:\Program Files\Java\jdk1.6.0_21\bin 附加到系统变量 Path 的末尾。 |
Linux | export PATH=$PATH:$JAVA_HOME/bin/ |
Mac | 不需要 |
使用上面解释的 **java -version** 命令验证 Java 安装。
步骤 3 – 下载并安装 NetBeans IDE
从 netbeans.org 下载最新版本的 NetBeans IDE。在撰写本教程时,我下载了Netbeans 7.3,它使用以下链接捆绑了 JDK 1.7 www.oracle.com
操作系统 | 安装程序名称 |
---|---|
Windows | Netbeans 7.3 |
Linux | Netbeans 7.3 |
Mac | Netbeans 7.3 |
步骤 4 – 设置 JBoss 应用程序服务器
您可以从 www.jboss.org 下载最新版本的 JBoss 服务器。根据平台下载存档。将 Jboss 解压缩到机器上的任何位置。
操作系统 | 文件名 |
---|---|
Windows | jboss-5.1.0.GA-jdk6.zip |
Linux | jboss-5.1.0.GA-src.tar.gz |
Mac | jboss-5.1.0.GA-src.tar.gz |
步骤 5 - 将 JEE 插件配置到 Netbeans
使用“工具”>“插件”打开插件窗口。打开“可用插件”选项卡,然后在“Java Web 和 EE”类别下选择“Java EE 基础”和“EJB 和 EAR”。单击安装按钮。Netbeans 将下载并安装相应的插件。使用“已安装”选项卡验证插件安装(如下面的图像所示)。
步骤 6 - 在 Netbeans 中配置 JBoss 服务器
转到“服务”选项卡,然后右键单击服务器以添加新服务器。
将打开“添加服务器实例”向导。选择 JBoss,并在下一步中输入相关详细信息以在 netbeans 中配置服务器。
配置完成后,您将看到以下屏幕。
步骤 7 - 安装数据库服务器 (PostGreSql)
从 www.postgresql.org 下载最新版本的 PostGreSql 数据库服务器。在撰写本教程时,我下载了PostGreSql 9.2
操作系统 | 安装程序名称 |
---|---|
Windows | PostGreSql 9.2 |
Linux | PostGreSql 9.2 |
Mac | PostGreSql 9.2 |
EJB - 创建应用程序
要创建一个简单的 EJB 模块,我们将使用 NetBeans,“新建项目”向导。在下面给出的示例中,我们将创建一个名为 Component 的 EJB 模块项目。
创建项目
在 NetBeans IDE 中,选择文件 > 新建项目 >。您将看到以下屏幕
在类别Java EE下选择项目类型,项目类型为EJB 模块。单击下一步 >按钮。您将看到以下屏幕。
输入项目名称和位置。单击下一步 >按钮。您将看到以下屏幕。
选择服务器为JBoss 应用程序服务器。单击完成按钮。您将看到 NetBeans 创建的以下项目。
创建一个示例 EJB
要创建一个简单的 EJB,我们将使用 NetBeans“新建”向导。在下面给出的示例中,我们将创建一个名为 librarySessionBean 的无状态 EJB 类,位于 EjbComponent 项目下。
在项目浏览器窗口中选择项目 EjbComponent 并右键单击它。选择新建 > 会话 Bean。您将看到新建会话 Bean向导。
输入会话 Bean 名称和包名称。单击完成按钮。您将看到 NetBeans 创建的以下 EJB 类。
LibrarySessionBean - 无状态会话 Bean
LibrarySessionBeanLocal - 会话 Bean 的本地接口
我将本地接口更改为远程接口,因为我们将在基于控制台的应用程序中访问我们的 EJB。远程/本地接口用于公开 EJB 必须实现的业务方法。
LibrarySessionBeanLocal 重命名为 LibrarySessionBeanRemote,并且 LibrarySessionBean 实现 LibrarySessionBeanRemote 接口。
LibrarySessionBeanRemote
package com.tutorialspoint.stateless; import java.util.List; import javax.ejb.Remote; @Remote public interface LibrarySessionBeanRemote { void addBook(String bookName); List getBooks(); }
LibrarySessionBean
package com.tutorialspoint.stateless; import java.util.ArrayList; import java.util.List; import javax.ejb.Stateless; @Stateless public class LibrarySessionBean implements LibrarySessionBeanRemote { List<String> bookShelf; public LibrarySessionBean() { bookShelf = new ArrayList<String>(); } public void addBook(String bookName) { bookShelf.add(bookName); } public List<String> getBooks() { return bookShelf; } }
构建项目
- 在项目浏览器窗口中选择 EjbComponent 项目。
- 右键单击它以打开上下文菜单。
- 选择清理并构建。
您将在 NetBeans 控制台输出中看到以下输出。
ant -f C:\\EJB\\EjbComponent clean dist init: undeploy-clean: deps-clean: Deleting directory C:\EJB\EjbComponent\build Deleting directory C:\EJB\EjbComponent\dist clean: init: deps-jar: Created dir: C:\EJB\EjbComponent\build\classes Copying 3 files to C:\EJB\EjbComponent\build\classes\META-INF Created dir: C:\EJB\EjbComponent\build\empty Created dir: C:\EJB\EjbComponent\build\generated-sources\ap-source-output Compiling 2 source files to C:\EJB\EjbComponent\build\classes warning: [options] bootstrap class path not set in conjunction with -source 1.6 Note: C:\EJB\EjbComponent\src\java\com\tutorialspoint\stateless \LibraryPersistentBean.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. 1 warning compile: library-inclusion-in-archive: Created dir: C:\EJB\EjbComponent\dist Building jar: C:\EJB\EjbComponent\dist\EjbComponent.jar dist: BUILD SUCCESSFUL (total time: 3 seconds)
启动应用程序服务器
- 在“服务”窗口的“服务器”下选择 JBoss 应用程序服务器。
- 右键单击它以打开上下文菜单。
- 选择启动。
您将在 NetBeans 中看到以下输出,JBoss 应用程序服务器下的输出。
Calling C:\jboss-5.1.0.GA\bin\run.conf.bat ========================================================================= JBoss Bootstrap Environment JBOSS_HOME: C:\jboss-5.1.0.GA JAVA: C:\Program Files (x86)\Java\jdk1.6.0_21\bin\java JAVA_OPTS: -Dprogram.name=run.bat -Xms128m -Xmx512m -server CLASSPATH: C:\jboss-5.1.0.GA\bin\run.jar ========================================================================= 16:25:50,062 INFO [ServerImpl] Starting JBoss (Microcontainer)... 16:25:50,062 INFO [ServerImpl] Release ID: JBoss [The Oracle] 5.1.0.GA (build: SVNTag=JBoss_5_1_0_GA date=200905221634) ... 16:26:40,420 INFO [TomcatDeployment] deploy, ctxPath=/admin-console 16:26:40,485 INFO [config] Initializing Mojarra (1.2_12-b01-FCS) for context '/admin-console' 16:26:42,362 INFO [TomcatDeployment] deploy, ctxPath=/ 16:26:42,406 INFO [TomcatDeployment] deploy, ctxPath=/jmx-console 16:26:42,471 INFO [Http11Protocol] Starting Coyote HTTP/1.1 on http-127.0.0.1-8080 16:26:42,487 INFO [AjpProtocol] Starting Coyote AJP/1.3 on ajp-127.0.0.1-8009 16:26:42,493 INFO [ServerImpl] JBoss (Microcontainer) [5.1.0.GA (build: SVNTag=JBoss_5_1_0_GA date=200905221634)] Started in 52s:427ms
部署项目
- 在项目浏览器窗口中选择 EjbComponent 项目。
- 右键单击它以打开上下文菜单。
- 选择部署。
您将在 NetBeans 控制台输出中看到以下输出。
ant -f C:\\EJB\\EjbComponent -DforceRedeploy=true -Ddirectory.deployment.supported=false -Dnb.wait.for.caches=true run init: deps-jar: compile: library-inclusion-in-archive: Building jar: C:\EJB\EjbComponent\dist\EjbComponent.jar dist-directory-deploy: pre-run-deploy: Checking data source definitions for missing JDBC drivers... Distributing C:\EJB\EjbComponent\dist\EjbComponent.jar to [org.jboss.deployment.spi.LocalhostTarget@1e4f84ee] Deploying C:\EJB\EjbComponent\dist\EjbComponent.jar Application Deployed Operation start started Operation start completed post-run-deploy: run-deploy: run: BUILD SUCCESSFUL (total time: 2 seconds)
JBoss 应用程序服务器日志输出
16:30:00,963 INFO [DeployHandler] Begin start, [EjbComponent.jar] ... 16:30:01,233 INFO [Ejb3DependenciesDeployer] Encountered deployment AbstractVFSDeploymentContext@12038795{vfszip:/C:/jboss-5.1.0.GA/server/default/deploy/EjbComponent.jar/} ... 16:30:01,281 INFO [JBossASKernel] jndi:LibrarySessionBean/remote-com.tutorialspoint.stateless.LibrarySessionBeanRemote 16:30:01,281 INFO [JBossASKernel] Class:com.tutorialspoint.stateless.LibrarySessionBeanRemote 16:30:01,281 INFO [JBossASKernel] jndi:LibrarySessionBean/remote 16:30:01,281 INFO [JBossASKernel] Added bean(jboss.j2ee:jar=EjbComponent.jar,name= LibrarySessionBean,service=EJB3) to KernelDeployment of: EjbComponent.jar 16:30:01,282 INFO [JBossASKernel] installing bean: jboss.j2ee:jar=EjbComponent.jar,name=BookMessageHandler,service=EJB3 16:30:01,282 INFO [JBossASKernel] with dependencies: 16:30:01,282 INFO [JBossASKernel] and demands: 16:30:01,282 INFO [JBossASKernel] jboss.ejb:service=EJBTimerService ... 16:30:01,283 INFO [EJB3EndpointDeployer] Deploy AbstractBeanMetaData@5497cb{name=jboss.j2ee:jar=EjbComponent.jar, name=LibrarySessionBean, service=EJB3_endpoint bean=org.jboss.ejb3.endpoint.deployers.impl.EndpointImpl properties=[container] constructor=null autowireCandidate=true} ... 16:30:01,394 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibrarySessionBean,service=EJB3 16:30:01,395 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibrarySessionBean ejbName: LibrarySessionBean 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibrarySessionBean/remote - EJB3.x Default Remote Business Interface LibrarySessionBean/remote-com.tutorialspoint.stateless.LibrarySessionBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibrarySessionBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibrarySessionBean ejbName: LibrarySessionBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibrarySessionBean/remote - EJB3.x Default Remote Business Interface LibrarySessionBean/remote-com.tutorialspoint.stateless.LibrarySessionBeanRemote - EJB3.x Remote Business Interface
创建客户端以访问 EJB
在 NetBeans IDE 中,选择文件 > 新建项目 >。
在类别Java下选择项目类型,项目类型为Java 应用程序。单击下一步 > 按钮
输入项目名称和位置。单击完成 >按钮。我们已选择名称为 EjbTester。
右键单击项目浏览器窗口中的项目名称。选择属性。
使用添加项目按钮在编译选项卡中添加前面创建的 EJB 组件项目。
使用添加 jar/文件夹按钮在编译选项卡中添加 jboss 库。Jboss 库可以位于 <jboss 安装文件夹>> client 文件夹中。
在项目(例如 EjbTester)下创建 jndi.properties。
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
在其中创建包 com.tutorialspoint.test 和 EJBTester.java 类。
EJBTester.java
package com.tutorialspoint.test; import com.tutorialspoint.stateless.LibrarySessionBeanRemote; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Properties; import javax.naming.InitialContext; import javax.naming.NamingException; public class EJBTester { BufferedReader brConsoleReader = null; Properties props; InitialContext ctx; { props = new Properties(); try { props.load(new FileInputStream("jndi.properties")); } catch (IOException ex) { ex.printStackTrace(); } try { ctx = new InitialContext(props); } catch (NamingException ex) { ex.printStackTrace(); } brConsoleReader = new BufferedReader(new InputStreamReader(System.in)); } public static void main(String[] args) { EJBTester ejbTester = new EJBTester(); ejbTester.testStatelessEjb(); } private void showGUI() { System.out.println("**********************"); System.out.println("Welcome to Book Store"); System.out.println("**********************"); System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: "); } private void testStatelessEjb() { try { int choice = 1; LibrarySessionBeanRemote libraryBean = (LibrarySessionBeanRemote)ctx.lookup("LibrarySessionBean/remote"); while (choice != 2) { String bookName; showGUI(); String strChoice = brConsoleReader.readLine(); choice = Integer.parseInt(strChoice); if (choice == 1) { System.out.print("Enter book name: "); bookName = brConsoleReader.readLine(); libraryBean.addBook(bookName); }else if (choice == 2) { break; } } List<String> booksList = libraryBean.getBooks(); System.out.println("Book(s) entered so far: " + booksList.size()); for (int i = 0; i < booksList.size(); ++i) { System.out.println((i+1)+". " + booksList.get(i)); } LibrarySessionBeanRemote libraryBean1 = (LibrarySessionBeanRemote)ctx.lookup("LibrarySessionBean/remote"); List<String> booksList1 = libraryBean1.getBooks(); System.out.println( "***Using second lookup to get library stateless object***"); System.out.println( "Book(s) entered so far: " + booksList1.size()); for (int i = 0; i < booksList1.size(); ++i) { System.out.println((i+1)+". " + booksList1.get(i)); } } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); } finally { try { if(brConsoleReader !=null) { brConsoleReader.close(); } } catch (IOException ex) { System.out.println(ex.getMessage()); } } } }
运行客户端以访问 EJB
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在 Netbeans 控制台中验证以下输出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Java ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. Learn Java ***Using second lookup to get library stateless object*** Book(s) entered so far: 0 BUILD SUCCESSFUL (total time: 13 seconds)
在接下来的章节中,我们将涵盖此完整 EJB 应用程序的多个方面。
EJB - 无状态 Bean
无状态会话Bean是一种企业Bean,通常用于执行独立的操作。顾名思义,无状态会话Bean没有任何与客户端关联的状态,但它可以保留其实例状态。EJB容器通常会创建一些无状态Bean对象的池,并使用这些对象来处理客户端的请求。由于池的存在,实例变量的值在查找/方法调用之间不能保证相同。
创建无状态EJB的步骤
创建无状态EJB需要以下步骤:
创建公开业务方法的远程/本地接口。
此接口将由EJB客户端应用程序使用。
如果EJB客户端与要部署EJB会话Bean的环境相同,则使用@Local注解。
如果EJB客户端与要部署EJB会话Bean的环境不同,则使用@Remote注解。
创建实现上述接口的无状态会话Bean。
使用@Stateless注解表示它是一个无状态Bean。EJB容器在部署期间通过读取此注解自动创建相关配置或接口。
远程接口
import javax.ejb.Remote; @Remote public interface LibrarySessionBeanRemote { //add business method declarations }
无状态EJB
@Stateless public class LibrarySessionBean implements LibrarySessionBeanRemote { //implement business method }
示例应用程序
让我们创建一个测试EJB应用程序来测试无状态EJB。
步骤 | 描述 |
---|---|
1 | 在包com.tutorialspoint.stateless下创建一个名为EjbComponent的项目,如EJB - 创建应用程序章节中所述。您也可以使用EJB - 创建应用程序章节中创建的项目,以便在本节中理解无状态EJB的概念。 |
2 | 创建LibrarySessionBean.java和LibrarySessionBeanRemote,如EJB - 创建应用程序章节中所述。保持其余文件不变。 |
3 | 清理并构建应用程序,以确保业务逻辑按要求工作。 |
4 | 最后,将应用程序以jar文件的形式部署到JBoss应用服务器上。如果JBoss应用服务器尚未启动,它将自动启动。 |
5 | 现在创建EJB客户端,一个基于控制台的应用程序,其方式与EJB - 创建应用程序章节中主题创建访问EJB的客户端中所述相同。 |
EJBComponent(EJB模块)
LibrarySessionBeanRemote.java
package com.tutorialspoint.stateless; import java.util.List; import javax.ejb.Remote; @Remote public interface LibrarySessionBeanRemote { void addBook(String bookName); List getBooks(); }
LibrarySessionBean.java
package com.tutorialspoint.stateless; import java.util.ArrayList; import java.util.List; import javax.ejb.Stateless; @Stateless public class LibrarySessionBean implements LibrarySessionBeanRemote { List<String> bookShelf; public LibrarySessionBean() { bookShelf = new ArrayList<String>(); } public void addBook(String bookName) { bookShelf.add(bookName); } public List<String> getBooks() { return bookShelf; } }
一旦您在JBOSS上部署了EjbComponent项目,请注意jboss日志。
JBoss已自动为我们的会话Bean创建了一个JNDI条目 - LibrarySessionBean/remote。
我们将使用此查找字符串获取类型为com.tutorialspoint.stateless.LibrarySessionBeanRemote的远程业务对象。
JBoss 应用程序服务器日志输出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibrarySessionBean/remote - EJB3.x Default Remote Business Interface LibrarySessionBean/remote-com.tutorialspoint.stateless.LibrarySessionBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibrarySessionBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibrarySessionBeanRemote ejbName: LibrarySessionBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibrarySessionBean/remote - EJB3.x Default Remote Business Interface LibrarySessionBean/remote-com.tutorialspoint.stateless.LibrarySessionBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客户端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
这些属性用于初始化java命名服务的InitialContext对象。
InitialContext对象将用于查找无状态会话Bean。
EJBTester.java
package com.tutorialspoint.test; import com.tutorialspoint.stateful.LibrarySessionBeanRemote; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Properties; import javax.naming.InitialContext; import javax.naming.NamingException; public class EJBTester { BufferedReader brConsoleReader = null; Properties props; InitialContext ctx; { props = new Properties(); try { props.load(new FileInputStream("jndi.properties")); } catch (IOException ex) { ex.printStackTrace(); } try { ctx = new InitialContext(props); } catch (NamingException ex) { ex.printStackTrace(); } brConsoleReader = new BufferedReader(new InputStreamReader(System.in)); } public static void main(String[] args) { EJBTester ejbTester = new EJBTester(); ejbTester.testStatelessEjb(); } private void showGUI() { System.out.println("**********************"); System.out.println("Welcome to Book Store"); System.out.println("**********************"); System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: "); } private void testStatelessEjb() { try { int choice = 1; LibrarySessionBeanRemote libraryBean = LibrarySessionBeanRemote)ctx.lookup("LibrarySessionBean/remote"); while (choice != 2) { String bookName; showGUI(); String strChoice = brConsoleReader.readLine(); choice = Integer.parseInt(strChoice); if (choice == 1) { System.out.print("Enter book name: "); bookName = brConsoleReader.readLine(); Book book = new Book(); book.setName(bookName); libraryBean.addBook(book); } else if (choice == 2) { break; } } List<Book> booksList = libraryBean.getBooks(); System.out.println("Book(s) entered so far: " + booksList.size()); int i = 0; for (Book book:booksList) { System.out.println((i+1)+". " + book.getName()); i++; } LibrarySessionBeanRemote libraryBean1 = (LibrarySessionBeanRemote)ctx.lookup("LibrarySessionBean/remote"); List<String> booksList1 = libraryBean1.getBooks(); System.out.println( "***Using second lookup to get library stateless object***"); System.out.println( "Book(s) entered so far: " + booksList1.size()); for (int i = 0; i < booksList1.size(); ++i) { System.out.println((i+1)+". " + booksList1.get(i)); } } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); }finally { try { if(brConsoleReader !=null) { brConsoleReader.close(); } } catch (IOException ex) { System.out.println(ex.getMessage()); } } } }
EJBTester执行以下任务:
从jndi.properties加载属性并初始化InitialContext对象。
在testStatelessEjb()方法中,使用名称"LibrarySessionBean/remote"进行jndi查找以获取远程业务对象(无状态ejb)。
然后向用户显示一个图书馆商店用户界面,并要求他/她输入选择。
如果用户输入1,系统会要求输入书名,并使用无状态会话Bean的addBook()方法保存书籍。会话Bean将其存储在实例变量中。
如果用户输入2,系统将使用无状态会话Bean的getBooks()方法检索书籍并退出。
然后使用名称"LibrarySessionBean/remote"再次进行jndi查找以获取远程业务对象(无状态EJB),并列出书籍。
运行客户端以访问 EJB
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在 Netbeans 控制台中验证以下输出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Java ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. Learn Java ***Using second lookup to get library stateless object*** Book(s) entered so far: 0 BUILD SUCCESSFUL (total time: 13 seconds)
再次运行客户端以访问EJB
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在 Netbeans 控制台中验证以下输出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 0 ***Using second lookup to get library stateless object*** Book(s) entered so far: 1 1. Learn Java BUILD SUCCESSFUL (total time: 12 seconds)
上面显示的输出可能会有所不同,具体取决于JBoss维护了多少个无状态EJB对象。
如果维护单个无状态EJB对象,则在每次查找后您可能会看到相同的书籍列表。
EJB容器可能会为每次查找返回相同的无状态EJB对象。
无状态EJB Bean会保留实例变量的值,直到服务器重新启动。
EJB - 有状态 Bean
有状态会话Bean是一种企业Bean,它保留与客户端的会话状态。顾名思义,有状态会话Bean在其实例变量中保留关联的客户端状态。EJB容器为每个客户端请求创建一个单独的有状态会话Bean。一旦请求范围结束,有状态会话Bean就会被销毁。
创建有状态EJB的步骤
创建有状态EJB需要以下步骤:
创建公开业务方法的远程/本地接口。
此接口将由EJB客户端应用程序使用。
如果EJB客户端与要部署EJB会话Bean的环境相同,则使用@Local注解。
如果EJB客户端与要部署EJB会话Bean的环境不同,则使用@Remote注解。
创建实现上述接口的有状态会话Bean。
使用@Stateful注解表示它是一个有状态Bean。EJB容器在部署期间通过读取此注解自动创建相关配置或接口。
远程接口
import javax.ejb.Remote; @Remote public interface LibraryStatefulSessionBeanRemote { //add business method declarations }
有状态EJB
@Stateful public class LibraryStatefulSessionBean implements LibraryStatefulSessionBeanRemote { //implement business method }
示例应用程序
让我们创建一个测试EJB应用程序来测试有状态EJB。
步骤 | 描述 |
---|---|
1 | 在包com.tutorialspoint.stateful下创建一个名为EjbComponent的项目,如EJB - 创建应用程序章节中所述。您也可以使用EJB - 创建应用程序章节中创建的项目,以便在本节中理解有状态EJB的概念。 |
2 | 创建LibraryStatefulSessionBean.java和LibraryStatefulSessionBeanRemote,如EJB - 创建应用程序章节中所述。保持其余文件不变。 |
3 | 清理并构建应用程序,以确保业务逻辑按要求工作。 |
4 | 最后,将应用程序以jar文件的形式部署到JBoss应用服务器上。如果JBoss应用服务器尚未启动,它将自动启动。 |
5 | 现在创建EJB客户端,一个基于控制台的应用程序,其方式与EJB - 创建应用程序章节中主题创建访问EJB的客户端中所述相同。 |
EJBComponent(EJB模块)
LibraryStatefulSessionBeanRemote.java
package com.tutorialspoint.stateful; import java.util.List; import javax.ejb.Remote; @Remote public interface LibraryStatefulSessionBeanRemote { void addBook(String bookName); List getBooks(); }
LibraryStatefulSessionBean.java
package com.tutorialspoint.stateful; import java.util.ArrayList; import java.util.List; import javax.ejb.Stateful; @Stateful public class LibraryStatefulSessionBean implements LibraryStatefulSessionBeanRemote { List<String> bookShelf; public LibraryStatefulSessionBean() { bookShelf = new ArrayList<String>(); } public void addBook(String bookName) { bookShelf.add(bookName); } public List<String> getBooks() { return bookShelf; } }
一旦您在JBOSS上部署了EjbComponent项目,请注意jboss日志。
JBoss已自动为我们的会话Bean创建了一个JNDI条目 - LibraryStatefulSessionBean/remote。
我们将使用此查找字符串获取类型为com.tutorialspoint.stateful.LibraryStatefulSessionBeanRemote的远程业务对象。
JBoss 应用程序服务器日志输出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryStatefulSessionBean/remote - EJB3.x Default Remote Business Interface LibraryStatefulSessionBean/remote-com.tutorialspoint.stateful.LibraryStatefulSessionBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryStatefulSessionBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateful.LibraryStatefulSessionBeanRemote ejbName: LibraryStatefulSessionBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryStatefulSessionBean/remote - EJB3.x Default Remote Business Interface LibraryStatefulSessionBean/remote-com.tutorialspoint.stateful.LibraryStatefulSessionBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客户端)
jndi.properties
java.naming.factory.initial = org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs = org.jboss.naming:org.jnp.interfaces java.naming.provider.url = localhost
这些属性用于初始化java命名服务的InitialContext对象。
InitialContext对象将用于查找有状态会话Bean。
EJBTester.java
package com.tutorialspoint.test; import com.tutorialspoint.stateful.LibraryStatefulSessionBeanRemote; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Properties; import javax.naming.InitialContext; import javax.naming.NamingException; public class EJBTester { BufferedReader brConsoleReader = null; Properties props; InitialContext ctx; { props = new Properties(); try { props.load(new FileInputStream("jndi.properties")); } catch (IOException ex) { ex.printStackTrace(); } try { ctx = new InitialContext(props); } catch (NamingException ex) { ex.printStackTrace(); } brConsoleReader = new BufferedReader(new InputStreamReader(System.in)); } public static void main(String[] args) { EJBTester ejbTester = new EJBTester(); ejbTester.testStatelessEjb(); } private void showGUI() { System.out.println("**********************"); System.out.println("Welcome to Book Store"); System.out.println("**********************"); System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: "); } private void testStatelessEjb() { try { int choice = 1; LibraryStatefulSessionBeanRemote libraryBean = LibraryStatefulSessionBeanRemote)ctx.lookup("LibraryStatefulSessionBean/remote"); while (choice != 2) { String bookName; showGUI(); String strChoice = brConsoleReader.readLine(); choice = Integer.parseInt(strChoice); if (choice == 1) { System.out.print("Enter book name: "); bookName = brConsoleReader.readLine(); Book book = new Book(); book.setName(bookName); libraryBean.addBook(book); } else if (choice == 2) { break; } } List<Book> booksList = libraryBean.getBooks(); System.out.println("Book(s) entered so far: " + booksList.size()); int i = 0; for (Book book:booksList) { System.out.println((i+1)+". " + book.getName()); i++; } LibraryStatefulSessionBeanRemote libraryBean1 = (LibraryStatefulSessionBeanRemote)ctx.lookup("LibraryStatefulSessionBean/remote"); List<String> booksList1 = libraryBean1.getBooks(); System.out.println( "***Using second lookup to get library stateful object***"); System.out.println( "Book(s) entered so far: " + booksList1.size()); for (int i = 0; i < booksList1.size(); ++i) { System.out.println((i+1)+". " + booksList1.get(i)); } } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); }finally { try { if(brConsoleReader !=null) { brConsoleReader.close(); } } catch (IOException ex) { System.out.println(ex.getMessage()); } } } }
EJBTester执行以下任务:
从jndi.properties加载属性并初始化InitialContext对象。
在testStatefulEjb()方法中,使用名称"LibraryStatefulSessionBean/remote"进行jndi查找以获取远程业务对象(有状态ejb)。
然后向用户显示一个图书馆商店用户界面,并要求他/她输入选择。
如果用户输入1,系统会要求输入书名,并使用有状态会话Bean的addBook()方法保存书籍。会话Bean将其存储在实例变量中。
如果用户输入2,系统将使用有状态会话Bean的getBooks()方法检索书籍并退出。
然后使用名称"LibraryStatefulSessionBean/remote"再次进行jndi查找以获取远程业务对象(有状态EJB),并列出书籍。
运行客户端以访问 EJB
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在Netbeans控制台中验证以下输出:
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Java ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. Learn Java ***Using second lookup to get library stateful object*** Book(s) entered so far: 0 BUILD SUCCESSFUL (total time: 13 seconds)
再次运行客户端以访问EJB
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在 Netbeans 控制台中验证以下输出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 0 ***Using second lookup to get library stateful object*** Book(s) entered so far: 0 BUILD SUCCESSFUL (total time: 12 seconds)
上面显示的输出表明,对于每次查找,都会返回不同的有状态EJB实例。
有状态EJB对象仅保留单个会话的值。在第二次运行中,我们没有获取任何书籍的值。
EJB - 持久性
在EJB 3.0中,EJB 2.0中使用的实体Bean很大程度上被持久化机制所取代。现在,实体Bean是一个简单的POJO,与表具有映射关系。
持久化API中的关键参与者如下:
实体 - 表示数据存储记录的持久化对象。最好是可序列化的。
实体管理器 - 持久化接口,用于对持久化对象(实体)执行添加/删除/更新/查找等数据操作。它还有助于使用查询接口执行查询。
持久化单元 (persistence.xml) - 持久化单元描述持久化机制的属性。
数据源 (*ds.xml) - 数据源描述数据存储相关的属性,如连接URL、用户名、密码等。
为了演示EJB持久化机制,我们需要执行以下任务:
步骤1 - 在数据库中创建表。
步骤2 - 创建与表对应的实体类。
步骤3 - 创建数据源和持久化单元。
步骤4 - 创建具有实体管理器实例的无状态EJB。
步骤5 - 更新无状态EJB。添加方法以通过实体管理器从数据库添加和获取记录。
步骤6 - 基于控制台的应用程序客户端将访问无状态EJB以将数据持久化到数据库中。
创建表
在默认数据库postgres中创建一个名为books的表。
CREATE TABLE books ( id integer PRIMARY KEY, name varchar(50) );
创建实体类
//mark it entity using Entity annotation //map table name using Table annotation @Entity @Table(name="books") public class Book implements Serializable{ private int id; private String name; public Book() { } //mark id as primary key with autogenerated value //map database column id with id field @Id @GeneratedValue(strategy= GenerationType.IDENTITY) @Column(name="id") public int getId() { return id; } ... }
创建数据源和持久化单元
数据源 (jboss-ds.xml)
<?xml version = "1.0" encoding = "UTF-8"?> <datasources> <local-tx-datasource> <jndi-name>PostgresDS</jndi-name> <connection-url>jdbc:postgresql://127.0.0.1:5432/postgres</connection-url> <driver-class>org.postgresql.driver</driver-class> <user-name>sa</user-name> <password>sa</password> <min-pool-size>5</min-pool-size> <max-pool-size>20</max-pool-size> <idle-timeout-minutes>5</idle-timeout-minutes> </local-tx-datasource> </datasources>
持久化单元 (persistence.xml)
<persistence version = "1.0" xmlns = "http://java.sun.com/xml/ns/persistence" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> <persistence-unit name = "EjbComponentPU" transaction-type = "JTA"> <jta-data-source>java:/PostgresDS</jta-data-source> <exclude-unlisted-classes>false</exclude-unlisted-classes> <properties/> </persistence-unit> <persistence-unit name = "EjbComponentPU2" transaction-type = "JTA"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data-source>java:/PostgresDS</jta-data-source> <exclude-unlisted-classes>false</exclude-unlisted-classes> <properties> <property name="hibernate.hbm2ddl.auto" value="update"/> </properties> </persistence-unit> </persistence>
创建具有实体管理器实例的无状态EJB
@Stateless public class LibraryPersistentBean implements LibraryPersistentBeanRemote { //pass persistence unit to entityManager. @PersistenceContext(unitName="EjbComponentPU") private EntityManager entityManager; public void addBook(Book book) { entityManager.persist(book); } public List<Book> getBooks() { return entityManager.createQuery("From Books").getResultList(); } ... }
构建EJB模块后,我们需要一个客户端来访问无状态Bean,我们将在下一节中创建。
示例应用程序
让我们创建一个测试EJB应用程序来测试EJB持久化机制。
步骤 | 描述 |
---|---|
1 | 在包com.tutorialspoint.entity下创建一个名为EjbComponent的项目,如EJB - 创建应用程序章节中所述。您也可以使用EJB - 创建应用程序章节中创建的项目,以便在本节中理解EJB持久化概念。 |
2 | 在包com.tutorialspoint.entity下创建Book.java,并按如下所示修改它。 |
3 | 创建LibraryPersistentBean.java和LibraryPersistentBeanRemote,如EJB - 创建应用程序章节中所述,并按如下所示修改它们。 |
4 | 在EjbComponent > setup文件夹中创建jboss-ds.xml,在EjbComponent > src > conf文件夹中创建persistence.xml。这些文件夹可以在Netbeans的文件选项卡中看到。按上面显示的方式修改这些文件。 |
5 | 清理并构建应用程序,以确保业务逻辑按要求工作。 |
6 | 最后,将应用程序以jar文件的形式部署到JBoss应用服务器上。如果JBoss应用服务器尚未启动,它将自动启动。 |
7 | 现在创建EJB客户端,一个基于控制台的应用程序,其方式与EJB - 创建应用程序章节中主题创建访问EJB的客户端中所述相同。按如下所示修改它。 |
EJBComponent(EJB模块)
Book.java
package com.tutorialspoint.entity; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EntityListeners; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="books") public class Book implements Serializable{ private int id; private String name; public Book() { } @Id @GeneratedValue(strategy= GenerationType.IDENTITY) @Column(name="id") public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
LibraryPersistentBeanRemote.java
package com.tutorialspoint.stateless; import com.tutorialspoint.entity.Book; import java.util.List; import javax.ejb.Remote; @Remote public interface LibraryPersistentBeanRemote { void addBook(Book bookName); List<Book> getBooks(); }
LibraryPersistentBean.java
package com.tutorialspoint.stateless; import com.tutorialspoint.entity.Book; import java.util.List; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Stateless public class LibraryPersistentBean implements LibraryPersistentBeanRemote { public LibraryPersistentBean() { } @PersistenceContext(unitName="EjbComponentPU") private EntityManager entityManager; public void addBook(Book book) { entityManager.persist(book); } public List<Book> getBooks() { return entityManager.createQuery("From Book").getResultList(); } }
一旦您在JBOSS上部署了EjbComponent项目,请注意jboss日志。
JBoss已自动为我们的会话Bean创建了一个JNDI条目 - LibraryPersistentBean/remote。
我们将使用此查找字符串获取类型为com.tutorialspoint.stateless.LibraryPersistentBeanRemote的远程业务对象。
JBoss 应用程序服务器日志输出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.stateless.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryPersistentBeanRemote,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibraryPersistentBeanRemote ejbName: LibraryPersistentBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.stateless.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客户端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
这些属性用于初始化java命名服务的InitialContext对象。
InitialContext对象将用于查找无状态会话Bean。
EJBTester.java
package com.tutorialspoint.test; import com.tutorialspoint.stateless.LibraryPersistentBeanRemote; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Properties; import javax.naming.InitialContext; import javax.naming.NamingException; public class EJBTester { BufferedReader brConsoleReader = null; Properties props; InitialContext ctx; { props = new Properties(); try { props.load(new FileInputStream("jndi.properties")); } catch (IOException ex) { ex.printStackTrace(); } try { ctx = new InitialContext(props); } catch (NamingException ex) { ex.printStackTrace(); } brConsoleReader = new BufferedReader(new InputStreamReader(System.in)); } public static void main(String[] args) { EJBTester ejbTester = new EJBTester(); ejbTester.testEntityEjb(); } private void showGUI() { System.out.println("**********************"); System.out.println("Welcome to Book Store"); System.out.println("**********************"); System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: "); } private void testEntityEjb() { try { int choice = 1; LibraryPersistentBeanRemote libraryBean = LibraryPersistentBeanRemote)ctx.lookup("LibraryPersistentBean/remote"); while (choice != 2) { String bookName; showGUI(); String strChoice = brConsoleReader.readLine(); choice = Integer.parseInt(strChoice); if (choice == 1) { System.out.print("Enter book name: "); bookName = brConsoleReader.readLine(); Book book = new Book(); book.setName(bookName); libraryBean.addBook(book); } else if (choice == 2) { break; } } List<Book> booksList = libraryBean.getBooks(); System.out.println("Book(s) entered so far: " + booksList.size()); int i = 0; for (Book book:booksList) { System.out.println((i+1)+". " + book.getName()); i++; } } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); }finally { try { if(brConsoleReader !=null) { brConsoleReader.close(); } } catch (IOException ex) { System.out.println(ex.getMessage()); } } } }
EJBTester执行以下任务。
从jndi.properties加载属性并初始化InitialContext对象。
在testStatefulEjb()方法中,使用名称"LibraryStatefulSessionBean/remote"进行jndi查找以获取远程业务对象(有状态ejb)。
然后向用户显示一个图书馆商店用户界面,并要求他/她输入选择。
如果用户输入1,系统会要求输入书名,并使用无状态会话Bean的addBook()方法保存书籍。会话Bean通过EntityManager调用将书籍持久化到数据库中。
如果用户输入2,系统将使用有状态会话Bean的getBooks()方法检索书籍并退出。
然后使用名称"LibraryStatelessSessionBean/remote"再次进行jndi查找以获取远程业务对象(无状态EJB),并列出书籍。
运行客户端以访问 EJB
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在Netbeans控制台中验证以下输出:
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Java ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. learn java BUILD SUCCESSFUL (total time: 15 seconds)
再次运行客户端以访问EJB
在访问EJB之前重新启动JBoss。
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在 Netbeans 控制台中验证以下输出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Spring ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 2 1. learn java 2. Learn Spring BUILD SUCCESSFUL (total time: 15 seconds)
上面显示的输出表明,书籍正在存储到持久化存储中,并从数据库中检索。
EJB - 消息驱动 Bean
消息驱动Bean是一种企业Bean,当它从队列或主题接收消息时,由EJB容器调用。消息驱动Bean是一个无状态Bean,用于异步执行任务。
为了演示消息驱动Bean的使用,我们将使用EJB持久化章节,并且我们需要执行以下任务:
步骤1 - 在数据库中创建表(请参阅EJB-持久化章节)。
步骤2 - 创建与表对应的实体类(请参阅EJB-持久化章节)。
步骤3 - 创建数据源和持久化单元(请参阅EJB-持久化章节)。
步骤4 - 创建具有实体管理器实例的无状态EJB(请参阅EJB-持久化章节)。
步骤5 - 更新无状态ejb。添加方法以通过实体管理器从数据库添加和获取记录(请参阅EJB-持久化章节)。
步骤6 - 在JBossdefault应用程序目录中创建一个名为BookQueue的队列。
步骤7 - 基于控制台的应用程序客户端将向此队列发送消息。
步骤 8 − 创建一个消息驱动 Bean,它将使用无状态 Bean 持久化客户端数据。
步骤 9 − JBoss 的 EJB 容器将调用以上消息驱动 Bean,并将客户端发送的消息传递给它。
创建队列
如果在<JBoss 安装文件夹> > server > default > deploy 文件夹中不存在,则创建一个名为 jbossmq-destinations-service.xml 的文件。
这里我们创建一个名为 BookQueue 的队列 -
jbossmq-destinations-service.xml
<mbean code="org.jboss.mq.server.jmx.Queue" name="jboss.mq.destination:service=Queue,name=BookQueue"> <depends optional-attribute-name="DestinationManager"> jboss.mq:service=DestinationManager </depends> </mbean>
启动 JBoss 后,您将在 JBoss 日志中看到类似的条目。
... 10:37:06,167 INFO [QueueService] Queue[/queue/BookQueue] started, fullSize=200000, pageSize=2000, downCacheSize=2000 ...
创建消息驱动 Bean
@MessageDriven( name = "BookMessageHandler", activationConfig = { @ActivationConfigProperty( propertyName = "destinationType", propertyValue = "javax.jms.Queue"), @ActivationConfigProperty( propertyName = "destination", propertyValue ="/queue/BookQueue") } ) public class LibraryMessageBean implements MessageListener { @Resource private MessageDrivenContext mdctx; @EJB LibraryPersistentBeanRemote libraryBean; public LibraryMessageBean() { } public void onMessage(Message message) { } }
LibraryMessageBean 使用 @MessageDriven 注解进行注释,以将其标记为消息驱动 Bean。
其属性定义为 destinationType - Queue 和 destination - /queue/BookQueue。
它实现了 MessageListener 接口,该接口公开 onMessage 方法。
它具有 MessgeDrivenContext 作为资源。
LibraryPersistentBeanRemote 无状态 Bean 在此 Bean 中注入,用于持久化目的。
构建 EjbComponent 项目并在 JBoss 上部署它。构建和部署 EJB 模块后,我们需要一个客户端向 JBoss 队列发送消息。
示例应用程序
让我们创建一个测试 EJB 应用程序来测试消息驱动 Bean。
步骤 | 描述 |
---|---|
1 | 在包com.tutorialspoint.entity下创建一个名为EjbComponent的项目,如EJB - 创建应用程序章节中所述。您也可以使用EJB - 创建应用程序章节中创建的项目,以便在本节中理解EJB持久化概念。 |
2 | 在 EJB-Persistence 章节中创建的包 com.tutorialspoint.entity 下创建 Book.java。 |
3 | 创建 LibraryPersistentBean.java 和 LibraryPersistentBeanRemote,如 EJB-Persistence 章节中所创建。 |
4 | 在EjbComponent > setup 文件夹中创建 jboss-ds.xml,在EjbComponent > src > conf 文件夹中创建 persistence.xml。这些文件夹可以在 Netbeans 的文件选项卡中看到,如 EJB-Persistence 章节中所创建。 |
5 | 在包 com.tutorialspoint.messagebean 下创建 LibraryMessageBean.java,并根据以下所示进行修改。 |
6 | 如上所述在 JBoss 中创建 BookQueue 队列。 |
7 | 清理并构建应用程序,以确保业务逻辑按要求工作。 |
8 | 最后,将应用程序以jar文件的形式部署到JBoss应用服务器上。如果JBoss应用服务器尚未启动,它将自动启动。 |
9 | 现在创建EJB客户端,一个基于控制台的应用程序,其方式与EJB - 创建应用程序章节中主题创建访问EJB的客户端中所述相同。按如下所示修改它。 |
EJBComponent(EJB模块)
LibraryMessageBean.java
package com.tutorialspoint.messagebean; import com.tutorialspoint.entity.Book; import com.tutorialspoint.stateless.LibraryPersistentBeanRemote; import javax.annotation.Resource; import javax.ejb.ActivationConfigProperty; import javax.ejb.EJB; import javax.ejb.MessageDriven; import javax.ejb.MessageDrivenContext; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.ObjectMessage; @MessageDriven( name = "BookMessageHandler", activationConfig = { @ActivationConfigProperty( propertyName = "destinationType", propertyValue = "javax.jms.Queue"), @ActivationConfigProperty( propertyName = "destination", propertyValue ="/queue/BookQueue") } ) public class LibraryMessageBean implements MessageListener { @Resource private MessageDrivenContext mdctx; @EJB LibraryPersistentBeanRemote libraryBean; public LibraryMessageBean() { } public void onMessage(Message message) { ObjectMessage objectMessage = null; try { objectMessage = (ObjectMessage) message; Book book = (Book) objectMessage.getObject(); libraryBean.addBook(book); } catch (JMSException ex) { mdctx.setRollbackOnly(); } } }
EJBTester(EJB客户端)
EJBTester.java
package com.tutorialspoint.test; import com.tutorialspoint.entity.Book; import com.tutorialspoint.stateless.LibraryPersistentBeanRemote; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Properties; import javax.jms.ObjectMessage; import javax.jms.Queue; import javax.jms.QueueConnection; import javax.jms.QueueConnectionFactory; import javax.jms.QueueSender; import javax.jms.QueueSession; import javax.naming.InitialContext; import javax.naming.NamingException; public class EJBTester { BufferedReader brConsoleReader = null; Properties props; InitialContext ctx; { props = new Properties(); try { props.load(new FileInputStream("jndi.properties")); } catch (IOException ex) { ex.printStackTrace(); } try { ctx = new InitialContext(props); } catch (NamingException ex) { ex.printStackTrace(); } brConsoleReader = new BufferedReader(new InputStreamReader(System.in)); } public static void main(String[] args) { EJBTester ejbTester = new EJBTester(); ejbTester.testMessageBeanEjb(); } private void showGUI() { System.out.println("**********************"); System.out.println("Welcome to Book Store"); System.out.println("**********************"); System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: "); } private void testMessageBeanEjb() { try { int choice = 1; Queue queue = (Queue) ctx.lookup("/queue/BookQueue"); QueueConnectionFactory factory = (QueueConnectionFactory) ctx.lookup("ConnectionFactory"); QueueConnection connection = factory.createQueueConnection(); QueueSession session = connection.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE); QueueSender sender = session.createSender(queue); while (choice != 2) { String bookName; showGUI(); String strChoice = brConsoleReader.readLine(); choice = Integer.parseInt(strChoice); if (choice == 1) { System.out.print("Enter book name: "); bookName = brConsoleReader.readLine(); Book book = new Book(); book.setName(bookName); ObjectMessage objectMessage = session.createObjectMessage(book); sender.send(objectMessage); } else if (choice == 2) { break; } } LibraryPersistentBeanRemote libraryBean = (LibraryPersistentBeanRemote) ctx.lookup("LibraryPersistentBean/remote"); List<Book> booksList = libraryBean.getBooks(); System.out.println("Book(s) entered so far: " + booksList.size()); int i = 0; for (Book book:booksList) { System.out.println((i+1)+". " + book.getName()); i++; } } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); }finally { try { if(brConsoleReader !=null) { brConsoleReader.close(); } } catch (IOException ex) { System.out.println(ex.getMessage()); } } } }
EJBTester执行以下任务:
从jndi.properties加载属性并初始化InitialContext对象。
在 testStatefulEjb() 方法中,使用名称 - "/queue/BookQueue" 执行 JNDI 查找以获取在 JBoss 中可用的队列的引用。然后使用队列会话创建发送者。
然后向用户显示一个图书馆商店用户界面,并要求他/她输入选择。
如果用户输入 1,系统将提示输入书籍名称,发送者将书籍名称发送到队列。当 JBoss 容器在队列中收到此消息时,它将调用我们消息驱动 Bean 的 onMessage 方法。然后,我们的消息驱动 Bean 使用有状态会话 Bean 的 addBook() 方法保存书籍。会话 Bean 通过 EntityManager 调用将书籍持久化到数据库中。
如果用户输入 2,则使用名称 - "LibraryStatefulSessionBean/remote" 执行另一个 JNDI 查找以再次获取远程业务对象(有状态 EJB),并完成书籍列表。
运行客户端以访问 EJB
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在Netbeans控制台中验证以下输出:
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn EJB ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 2 1. learn java 1. learn EJB BUILD SUCCESSFUL (total time: 15 seconds)
上面显示的输出表明我们的消息驱动 Bean 正在接收消息并将书籍存储在持久存储中,并且书籍是从数据库中检索的。
EJB - 注解
注解是在 Java 5.0 中引入的。使用注解的目的是在类或类的元数据中附加其他信息,使其包含在源代码中。在 EJB 3.0 中,注解用于描述 EJB 类中的配置元数据。通过这种方式,EJB 3.0 消除了在配置 XML 文件中描述配置数据的需要。
EJB 容器使用编译器工具通过读取这些注解来生成所需的工件,如接口、部署描述符。以下是常用注解的列表。
序号 | 名称 | 描述 |
---|---|---|
1 | javax.ejb.Stateless |
指定给定的 EJB 类是无状态会话 Bean。 属性
|
2 | javax.ejb.Stateful |
指定给定的 EJB 类是有状态会话 Bean。 属性
|
3 | javax.ejb.MessageDrivenBean |
指定给定的 EJB 类是消息驱动 Bean。 属性
|
4 | javax.ejb.EJB |
用于将依赖项指定或注入为 EJB 实例到另一个 EJB 中。 属性
|
5 | javax.ejb.Local |
用于指定会话 Bean 的本地接口。此本地接口声明会话 Bean 的业务方法(可以是无状态或有状态)。 此接口用于向本地客户端公开业务方法,这些客户端在与 EJB 相同的部署/应用程序中运行。 属性
|
6 | javax.ejb.Remote |
用于指定会话 Bean 的远程接口。此远程接口声明会话 Bean 的业务方法(可以是无状态或有状态)。 此接口用于向远程客户端公开业务方法,这些客户端在与 EJB 不同的部署/应用程序中运行。 属性
|
7 | javax.ejb.Activation ConfigProperty |
用于指定消息驱动 Bean 所需的属性。例如,端点、目标、消息选择器等。 此注解作为 javax.ejb.MessageDrivenBean 注解的 activationConfig 属性的参数传递。 属性
|
8 | javax.ejb.PostActivate |
用于指定 EJB 生命周期回调方法。当 EJB 容器刚刚激活/重新激活 Bean 实例时,将调用此方法。 此接口用于向本地客户端公开业务方法,这些客户端在与 EJB 相同的部署/应用程序中运行。 |
EJB - 回调
回调是一种机制,通过该机制可以拦截企业 Bean 的生命周期。EJB 3.0 规范指定了回调,为此创建了回调处理程序方法。EJB 容器调用这些回调。我们可以在 EJB 类本身或单独的类中定义回调方法。EJB 3.0 为回调提供了许多注解。
以下是无状态 Bean 的回调注解列表 -
注解 | 描述 |
---|---|
@PostConstruct | 首次创建 Bean 时调用。 |
@PreDestroy | 当 Bean 从 Bean 池中删除或被销毁时调用。 |
以下是有状态 Bean 的回调注解列表 -
注解 | 描述 |
---|---|
@PostConstruct | 首次创建 Bean 时调用。 |
@PreDestroy | 当 Bean 从 Bean 池中删除或被销毁时调用。 |
@PostActivate | 当加载 Bean 以供使用时调用。 |
@PrePassivate | 当 Bean 放回 Bean 池时调用。 |
以下是消息驱动 Bean 的回调注解列表 -
注解 | 描述 |
---|---|
@PostConstruct | 首次创建 Bean 时调用。 |
@PreDestroy | 当 Bean 从 Bean 池中删除或被销毁时调用。 |
以下是实体 Bean 的回调注解列表 -
注解 | 描述 |
---|---|
@PrePersist | 在数据库中创建实体时调用。 |
@PostPersist | 在数据库中创建实体后调用。 |
@PreRemove | 从数据库中删除实体时调用。 |
@PostRemove | 从数据库中删除实体后调用。 |
@PreUpdate | 在数据库中更新实体之前调用。 |
@PostLoad | 从数据库中提取记录并加载到实体中时调用。 |
示例应用程序
让我们创建一个测试 EJB 应用程序来测试 EJB 中的各种回调。
步骤 | 描述 |
---|---|
1 | 在 EJB - 创建应用程序 章节中解释的包 com.tutorialspoint.stateless 下创建一个名为 EjbComponent 的项目。您也可以使用 EJB - 持久性 章节中创建的项目,为该章节添加各种 EJB 回调。 |
2 | 创建LibrarySessionBean.java和LibrarySessionBeanRemote,如EJB - 创建应用程序章节中所述。保持其余文件不变。 |
3 | 使用 EJB - 持久性 章节中创建的 Bean。添加如下所示的回调方法。保持其余文件不变。 |
4 | 在包 com.tutorialspoint.callback 下创建一个 Java 类 BookCallbackListener。此类将演示回调方法的分离。 |
5 | 清理并构建应用程序,以确保业务逻辑按要求工作。 |
6 | 最后,将应用程序以jar文件的形式部署到JBoss应用服务器上。如果JBoss应用服务器尚未启动,它将自动启动。 |
7 | 现在创建EJB客户端,一个基于控制台的应用程序,其方式与EJB - 创建应用程序章节中主题创建访问EJB的客户端中所述相同。 |
EJBComponent(EJB模块)
BookCallbackListener.java
package com.tutorialspoint.callback; import javax.persistence.PrePersist; import javax.persistence.PostLoad; import javax.persistence.PostPersist; import javax.persistence.PostRemove; import javax.persistence.PostUpdate; import javax.persistence.PreRemove; import javax.persistence.PreUpdate; import com.tutorialspoint.entity.Book; public class BookCallbackListener { @PrePersist public void prePersist(Book book) { System.out.println("BookCallbackListener.prePersist:" + "Book to be created with book id: "+book.getId()); } @PostPersist public void postPersist(Object book) { System.out.println("BookCallbackListener.postPersist::" + "Book created with book id: "+((Book)book).getId()); } @PreRemove public void preRemove(Book book) { System.out.println("BookCallbackListener.preRemove:" + " About to delete Book: " + book.getId()); } @PostRemove public void postRemove(Book book) { System.out.println("BookCallbackListener.postRemove::" + " Deleted Book: " + book.getId()); } @PreUpdate public void preUpdate(Book book) { System.out.println("BookCallbackListener.preUpdate::" + " About to update Book: " + book.getId()); } @PostUpdate public void postUpdate(Book book) { System.out.println("BookCallbackListener.postUpdate::" + " Updated Book: " + book.getId()); } @PostLoad public void postLoad(Book book) { System.out.println("BookCallbackListener.postLoad::" + " Loaded Book: " + book.getId()); } }
Book.java
package com.tutorialspoint.entity; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EntityListeners; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="books") public class Book implements Serializable{ private int id; private String name; public Book() { } @Id @GeneratedValue(strategy= GenerationType.IDENTITY) @Column(name="id") public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
LibraryStatefulSessionBean.java
package com.tutorialspoint.stateful; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.ejb.PostActivate; import javax.ejb.PrePassivate; import javax.ejb.Stateful; @Stateful public class LibraryStatefulSessionBean implements LibraryStatefulSessionBeanRemote { List<String> bookShelf; public LibraryStatefulSessionBean() { bookShelf = new ArrayList<String>(); } public void addBook(String bookName) { bookShelf.add(bookName); } public List<String> getBooks() { return bookShelf; } @PostConstruct public void postConstruct() { System.out.println("LibraryStatefulSessionBean.postConstruct::" + " bean created."); } @PreDestroy public void preDestroy() { System.out.println("LibraryStatefulSessionBean.preDestroy:" + " bean removed."); } @PostActivate public void postActivate() { System.out.println("LibraryStatefulSessionBean.postActivate:" + " bean activated."); } @PrePassivate public void prePassivate() { System.out.println("LibraryStatefulSessionBean.prePassivate:" + " bean passivated."); } }
LibraryStatefulSessionBeanRemote.java
package com.tutorialspoint.stateful; import java.util.List; import javax.ejb.Remote; @Remote public interface LibraryStatefulSessionBeanRemote { void addBook(String bookName); List getBooks(); }
LibraryPersistentBean.java
package com.tutorialspoint.stateless; import com.tutorialspoint.entity.Book; import java.util.List; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Stateless public class LibraryPersistentBean implements LibraryPersistentBeanRemote { public LibraryPersistentBean() {} @PersistenceContext(unitName="EntityEjbPU") private EntityManager entityManager; public void addBook(Book book) { entityManager.persist(book); } public List<Book> getBooks() { return entityManager.createQuery("From Book") .getResultList(); } @PostConstruct public void postConstruct() { System.out.println("postConstruct:: LibraryPersistentBean session bean" + " created with entity Manager object: "); } @PreDestroy public void preDestroy() { System.out.println("preDestroy: LibraryPersistentBean session" + " bean is removed "); } }
LibraryPersistentBeanRemote.java
package com.tutorialspoint.stateless; import com.tutorialspoint.entity.Book; import java.util.List; import javax.ejb.Remote; @Remote public interface LibraryPersistentBeanRemote { void addBook(Book bookName); List<Book> getBooks(); }
一旦您在JBOSS上部署了EjbComponent项目,请注意jboss日志。
JBoss已自动为我们的会话Bean创建了一个JNDI条目 - LibraryPersistentBean/remote。
我们将使用此查找字符串获取类型为com.tutorialspoint.stateless.LibraryPersistentBeanRemote的远程业务对象。
JBoss 应用程序服务器日志输出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.stateless.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryPersistentBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibrarySessionBeanRemote ejbName: LibraryPersistentBean ...
EJBTester(EJB客户端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
这些属性用于初始化java命名服务的InitialContext对象。
InitialContext对象将用于查找无状态会话Bean。
EJBTester.java
package com.tutorialspoint.test; import com.tutorialspoint.stateful.LibrarySessionBeanRemote; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Properties; import javax.naming.InitialContext; import javax.naming.NamingException; public class EJBTester { BufferedReader brConsoleReader = null; Properties props; InitialContext ctx; { props = new Properties(); try { props.load(new FileInputStream("jndi.properties")); } catch (IOException ex) { ex.printStackTrace(); } try { ctx = new InitialContext(props); } catch (NamingException ex) { ex.printStackTrace(); } brConsoleReader = new BufferedReader(new InputStreamReader(System.in)); } public static void main(String[] args) { EJBTester ejbTester = new EJBTester(); ejbTester.testEntityEjb(); } private void showGUI() { System.out.println("**********************"); System.out.println("Welcome to Book Store"); System.out.println("**********************"); System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: "); } private void testEntityEjb() { try { int choice = 1; LibraryPersistentBeanRemote libraryBean = (LibraryPersistentBeanRemote) ctx.lookup("LibraryPersistentBean/remote"); while (choice != 2) { String bookName; showGUI(); String strChoice = brConsoleReader.readLine(); choice = Integer.parseInt(strChoice); if (choice == 1) { System.out.print("Enter book name: "); bookName = brConsoleReader.readLine(); Book book = new Book(); book.setName(bookName); libraryBean.addBook(book); } else if (choice == 2) { break; } } List<Book> booksList = libraryBean.getBooks(); System.out.println("Book(s) entered so far: " + booksList.size()); int i = 0; for (Book book:booksList) { System.out.println((i+1)+". " + book.getName()); i++; } } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); }finally { try { if(brConsoleReader !=null) { brConsoleReader.close(); } } catch (IOException ex) { System.out.println(ex.getMessage()); } } } }
EJBTester执行以下任务:
从jndi.properties加载属性并初始化InitialContext对象。
在 testStatelessEjb() 方法中,使用名称 - "LibrarySessionBean/remote" 执行 JNDI 查找以获取远程业务对象(无状态 EJB)。
然后向用户显示图书馆商店用户界面,并提示用户输入选择。
如果用户输入 1,系统将提示输入书籍名称,并使用无状态会话 Bean 的 addBook() 方法保存书籍。会话 Bean 将书籍存储在数据库中。
如果用户输入2,系统将使用无状态会话Bean的getBooks()方法检索书籍并退出。
运行客户端以访问 EJB
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在 Netbeans 控制台中验证以下输出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Java ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. Learn Java BUILD SUCCESSFUL (total time: 13 seconds)
JBoss 应用程序服务器日志输出
您可以在 JBoss 日志中找到以下回调条目
14:08:34,293 INFO [STDOUT] postConstruct:: LibraryPersistentBean session bean created with entity Manager object ... 16:39:09,484 INFO [STDOUT] BookCallbackListener.prePersist:: Book to be created with book id: 0 16:39:09,531 INFO [STDOUT] BookCallbackListener.postPersist:: Book created with book id: 1 16:39:09,900 INFO [STDOUT] BookCallbackListener.postLoad:: Loaded Book: 1 ...
EJB - 定时器服务
定时器服务是一种机制,通过该机制可以构建计划应用程序。例如,每月 1 日生成工资单。EJB 3.0 规范指定了 @Timeout 注解,该注解有助于在无状态或消息驱动 Bean 中对 EJB 服务进行编程。EJB 容器调用使用 @Timeout 注解的方法。
EJB 定时器服务是 EJB 容器提供的一项服务,它有助于创建定时器并在定时器到期时安排回调。
创建定时器的步骤
使用 @Resource 注解将 SessionContext 注入 Bean 中 -
@Stateless public class TimerSessionBean { @Resource private SessionContext context; ... }
使用 SessionContext 对象获取 TimerService 并创建定时器。以毫秒和消息的形式传递时间。
public void createTimer(long duration) { context.getTimerService().createTimer(duration, "Hello World!"); }
使用定时器的步骤
将 @Timeout 注解应用于方法。返回类型应为 void,并传递类型为 Timer 的参数。我们在第一次执行后取消定时器,否则它将在固定间隔后继续运行。
@Timeout public void timeOutHandler(Timer timer) { System.out.println("timeoutHandler : " + timer.getInfo()); timer.cancel(); }
示例应用程序
让我们创建一个测试 EJB 应用程序来测试 EJB 中的定时器服务。
步骤 | 描述 |
---|---|
1 | 在 EJB - 创建应用程序 章节中解释的包 com.tutorialspoint.timer 下创建一个名为 EjbComponent 的项目。 |
2 | 创建 TimerSessionBean.java 和 TimerSessionBeanRemote,如 EJB - 创建应用程序 章节中所解释。保持其余文件不变。 |
3 | 清理并构建应用程序,以确保业务逻辑按要求工作。 |
4 | 最后,将应用程序以jar文件的形式部署到JBoss应用服务器上。如果JBoss应用服务器尚未启动,它将自动启动。 |
5 | 现在创建EJB客户端,一个基于控制台的应用程序,其方式与EJB - 创建应用程序章节中主题创建访问EJB的客户端中所述相同。 |
EJBComponent(EJB模块)
TimerSessionBean.java
package com.tutorialspoint.timer; import javax.annotation.Resource; import javax.ejb.SessionContext; import javax.ejb.Timer; import javax.ejb.Stateless; import javax.ejb.Timeout; @Stateless public class TimerSessionBean implements TimerSessionBeanRemote { @Resource private SessionContext context; public void createTimer(long duration) { context.getTimerService().createTimer(duration, "Hello World!"); } @Timeout public void timeOutHandler(Timer timer) { System.out.println("timeoutHandler : " + timer.getInfo()); timer.cancel(); } }
TimerSessionBeanRemote.java
package com.tutorialspoint.timer; import javax.ejb.Remote; @Remote public interface TimerSessionBeanRemote { public void createTimer(long milliseconds); }
一旦您在JBOSS上部署了EjbComponent项目,请注意jboss日志。
JBoss 已自动为我们的会话 Bean 创建了一个 JNDI 条目 - TimerSessionBean/remote。
我们将使用此查找字符串来获取类型为 - com.tutorialspoint.timer.TimerSessionBeanRemote 的远程业务对象。
JBoss 应用程序服务器日志输出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: TimerSessionBean/remote - EJB3.x Default Remote Business Interface TimerSessionBean/remote-com.tutorialspoint.timer.TimerSessionBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=TimerSessionBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.timer.TimerSessionBeanRemote ejbName: TimerSessionBean ...
EJBTester(EJB客户端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
这些属性用于初始化java命名服务的InitialContext对象。
InitialContext对象将用于查找无状态会话Bean。
EJBTester.java
package com.tutorialspoint.test; import com.tutorialspoint.stateful.TimerSessionBeanRemote; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Properties; import javax.naming.InitialContext; import javax.naming.NamingException; public class EJBTester { BufferedReader brConsoleReader = null; Properties props; InitialContext ctx; { props = new Properties(); try { props.load(new FileInputStream("jndi.properties")); } catch (IOException ex) { ex.printStackTrace(); } try { ctx = new InitialContext(props); } catch (NamingException ex) { ex.printStackTrace(); } brConsoleReader = new BufferedReader(new InputStreamReader(System.in)); } public static void main(String[] args) { EJBTester ejbTester = new EJBTester(); ejbTester.testTimerService(); } private void showGUI() { System.out.println("**********************"); System.out.println("Welcome to Book Store"); System.out.println("**********************"); System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: "); } private void testTimerService() { try { TimerSessionBeanRemote timerServiceBean = (TimerSessionBeanRemote)ctx.lookup("TimerSessionBean/remote"); System.out.println("["+(new Date()).toString()+ "]" + "timer created."); timerServiceBean.createTimer(2000); } catch (NamingException ex) { ex.printStackTrace(); } } }
EJBTester 正在执行以下任务。
从jndi.properties加载属性并初始化InitialContext对象。
在 testTimerService() 方法中,使用名称 - "TimerSessionBean/remote" 执行 JNDI 查找以获取远程业务对象(定时器无状态 EJB)。
然后调用 createTimer,将 2000 毫秒作为计划时间传递。
EJB 容器在 2 秒后调用 timeoutHandler 方法。
运行客户端以访问 EJB
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在 Netbeans 控制台中验证以下输出。
run: [Wed Jun 19 11:35:47 IST 2013]timer created. BUILD SUCCESSFUL (total time: 0 seconds)
JBoss 应用程序服务器日志输出
您可以在 JBoss 日志中找到以下回调条目
... 11:35:49,555 INFO [STDOUT] timeoutHandler : Hello World! ...
EJB - 依赖注入
EJB 3.0 规范提供了注解,这些注解可以应用于字段或 setter 方法以注入依赖项。EJB 容器使用全局 JNDI 注册表来查找依赖项。以下注解在 EJB 3.0 中用于依赖项注入。
@EJB − 用于注入其他 EJB 引用。
@Resource − 用于注入数据源或单例服务,如 sessionContext、timerService 等。
使用 @EJB 的步骤
@EJB 可以以下列方式用于字段或方法中 -
public class LibraryMessageBean implements MessageListener { //dependency injection on field. @EJB LibraryPersistentBeanRemote libraryBean; ... } public class LibraryMessageBean implements MessageListener { LibraryPersistentBeanRemote libraryBean; //dependency injection on method. @EJB(beanName="com.tutorialspoint.stateless.LibraryPersistentBean") public void setLibraryPersistentBean( LibraryPersistentBeanRemote libraryBean) { this.libraryBean = libraryBean; } ... }
使用 @Resource 的步骤
@Resource 通常用于注入 EJB 容器提供的单例。
public class LibraryMessageBean implements MessageListener { @Resource private MessageDrivenContext mdctx; ... }
示例应用程序
让我们创建一个测试 EJB 应用程序来测试 EJB 中的依赖项注入服务。
步骤 | 描述 |
---|---|
1 | 在 EJB - 创建应用程序 章节中解释的包 com.tutorialspoint.timer 下创建一个名为 EjbComponent 的项目。 |
2 | 使用 EJB - 消息驱动 Bean 章节中创建的 Bean。保持其余文件不变。 |
3 | 清理并构建应用程序,以确保业务逻辑按要求工作。 |
4 | 最后,将应用程序以jar文件的形式部署到JBoss应用服务器上。如果JBoss应用服务器尚未启动,它将自动启动。 |
5 | 现在创建EJB客户端,一个基于控制台的应用程序,其方式与EJB - 创建应用程序章节中主题创建访问EJB的客户端中所述相同。 |
EJBComponent(EJB模块)
LibraryMessageBean.java
package com.tuturialspoint.messagebean; import com.tutorialspoint.entity.Book; import com.tutorialspoint.stateless.LibraryPersistentBeanRemote; import javax.annotation.Resource; import javax.ejb.ActivationConfigProperty; import javax.ejb.EJB; import javax.ejb.MessageDriven; import javax.ejb.MessageDrivenContext; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.ObjectMessage; @MessageDriven( name = "BookMessageHandler", activationConfig = { @ActivationConfigProperty( propertyName = "destinationType", propertyValue = "javax.jms.Queue"), @ActivationConfigProperty( propertyName = "destination", propertyValue ="/queue/BookQueue") } ) public class LibraryMessageBean implements MessageListener { @Resource private MessageDrivenContext mdctx; @EJB LibraryPersistentBeanRemote libraryBean; public LibraryMessageBean() { } public void onMessage(Message message) { ObjectMessage objectMessage = null; try { objectMessage = (ObjectMessage) message; Book book = (Book) objectMessage.getObject(); libraryBean.addBook(book); }catch (JMSException ex) { mdctx.setRollbackOnly(); } } }
EJBTester(EJB客户端)
EJBTester.java
package com.tutorialspoint.test; import com.tutorialspoint.entity.Book; import com.tutorialspoint.stateless.LibraryPersistentBeanRemote; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Properties; import javax.jms.ObjectMessage; import javax.jms.Queue; import javax.jms.QueueConnection; import javax.jms.QueueConnectionFactory; import javax.jms.QueueSender; import javax.jms.QueueSession; import javax.naming.InitialContext; import javax.naming.NamingException; public class EJBTester { BufferedReader brConsoleReader = null; Properties props; InitialContext ctx; { props = new Properties(); try { props.load(new FileInputStream("jndi.properties")); } catch (IOException ex) { ex.printStackTrace(); } try { ctx = new InitialContext(props); } catch (NamingException ex) { ex.printStackTrace(); } brConsoleReader = new BufferedReader(new InputStreamReader(System.in)); } public static void main(String[] args) { EJBTester ejbTester = new EJBTester(); ejbTester.testMessageBeanEjb(); } private void showGUI() { System.out.println("**********************"); System.out.println("Welcome to Book Store"); System.out.println("**********************"); System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: "); } private void testMessageBeanEjb() { try { int choice = 1; Queue queue = (Queue) ctx.lookup("/queue/BookQueue"); QueueConnectionFactory factory = (QueueConnectionFactory) ctx.lookup("ConnectionFactory"); QueueConnection connection = factory.createQueueConnection(); QueueSession session = connection.createQueueSession( false, QueueSession.AUTO_ACKNOWLEDGE); QueueSender sender = session.createSender(queue); while (choice != 2) { String bookName; showGUI(); String strChoice = brConsoleReader.readLine(); choice = Integer.parseInt(strChoice); if (choice == 1) { System.out.print("Enter book name: "); bookName = brConsoleReader.readLine(); Book book = new Book(); book.setName(bookName); ObjectMessage objectMessage = session.createObjectMessage(book); sender.send(objectMessage); } else if (choice == 2) { break; } } LibraryPersistentBeanRemote libraryBean = (LibraryPersistentBeanRemote) ctx.lookup("LibraryPersistentBean/remote"); List<Book> booksList = libraryBean.getBooks(); System.out.println("Book(s) entered so far: " + booksList.size()); int i = 0; for (Book book:booksList) { System.out.println((i+1)+". " + book.getName()); i++; } } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); }finally { try { if(brConsoleReader !=null) { brConsoleReader.close(); } } catch (IOException ex) { System.out.println(ex.getMessage()); } } } }
EJBTester执行以下任务:
从jndi.properties加载属性并初始化InitialContext对象。
在 testStatefulEjb() 方法中,使用名称 - "/queue/BookQueue" 执行 JNDI 查找以获取在 JBoss 中可用的队列的引用。然后使用队列会话创建发送者。
然后向用户显示一个图书馆商店用户界面,并要求他/她输入选择。
如果用户输入1,系统会要求输入书籍名称,发送者将书籍名称发送到队列。当JBoss容器在队列中接收到此消息时,它会调用我们消息驱动Bean的onMessage方法。然后,我们的消息驱动Bean使用有状态会话Bean的addBook()方法保存书籍。会话Bean通过EntityManager调用将书籍持久化到数据库中。
如果用户输入2,则使用名称“LibraryStatefulSessionBean/remote”进行另一个JNDI查找,以再次获取远程业务对象(有状态EJB),并进行书籍列表。
运行客户端以访问 EJB
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在 Netbeans 控制台中验证以下输出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn EJB ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 2 1. learn java 1. learn EJB BUILD SUCCESSFUL (total time: 15 seconds)
上面显示的输出表明我们的消息驱动Bean正在接收消息并将书籍存储在持久存储中,并且书籍是从数据库中检索的。
我们的消息驱动Bean使用注入到其中的LibraryPersistentBean,使用@EJB注解,如果发生异常,则使用MessageDrivenContext对象回滚事务。
EJB - 拦截器
EJB 3.0提供规范,使用带有@AroundInvoke注解的方法拦截业务方法调用。拦截器方法在ejbContainer调用其拦截的业务方法之前被调用。以下是拦截器方法的示例签名
@AroundInvoke public Object methodInterceptor(InvocationContext ctx) throws Exception { System.out.println("*** Intercepting call to LibraryBean method: " + ctx.getMethod().getName()); return ctx.proceed(); }
拦截器方法可以应用或绑定在三个级别。
默认 - 默认拦截器对部署中的每个Bean都调用。默认拦截器只能通过xml(ejb-jar.xml)应用。
类 - 类级别拦截器对Bean的每个方法都调用。类级别拦截器可以通过注解或通过xml(ejb-jar.xml)应用。
方法 - 方法级别拦截器对Bean的特定方法调用。方法级别拦截器可以通过注解或通过xml(ejb-jar.xml)应用。
我们在这里讨论类级别拦截器。
拦截器类
package com.tutorialspoint.interceptor; import javax.interceptor.AroundInvoke; import javax.interceptor.InvocationContext; public class BusinessInterceptor { @AroundInvoke public Object methodInterceptor(InvocationContext ctx) throws Exception { System.out.println("*** Intercepting call to LibraryBean method: " + ctx.getMethod().getName()); return ctx.proceed(); } }
远程接口
import javax.ejb.Remote; @Remote public interface LibraryBeanRemote { //add business method declarations }
被拦截的无状态EJB
@Interceptors ({BusinessInterceptor.class}) @Stateless public class LibraryBean implements LibraryBeanRemote { //implement business method }
示例应用程序
让我们创建一个测试EJB应用程序来测试被拦截的无状态EJB。
步骤 | 描述 |
---|---|
1 | 在EJB - 创建应用程序章节中说明的com.tutorialspoint.interceptor包下,创建一个名为EjbComponent的项目。您也可以使用EJB - 创建应用程序章节中创建的项目,以便在本节中了解被拦截的EJB概念。 |
2 | 在EJB - 创建应用程序章节中说明的com.tutorialspoint.interceptor包下创建LibraryBean.java和LibraryBeanRemote。保持其余文件不变。 |
3 | 清理并构建应用程序,以确保业务逻辑按要求工作。 |
4 | 最后,将应用程序以jar文件的形式部署到JBoss应用服务器上。如果JBoss应用服务器尚未启动,它将自动启动。 |
5 | 现在创建EJB客户端,一个基于控制台的应用程序,其方式与EJB - 创建应用程序章节中主题创建访问EJB的客户端中说明的相同。 |
EJBComponent(EJB模块)
LibraryBeanRemote.java
package com.tutorialspoint.interceptor; import java.util.List; import javax.ejb.Remote; @Remote public interface LibraryBeanRemote { void addBook(String bookName); List getBooks(); }
LibraryBean.java
package com.tutorialspoint.interceptor; import java.util.ArrayList; import java.util.List; import javax.ejb.Stateless; import javax.interceptor.Interceptors; @Interceptors ({BusinessInterceptor.class}) @Stateless public class LibraryBean implements LibraryBeanRemote { List<String> bookShelf; public LibraryBean() { bookShelf = new ArrayList<String>(); } public void addBook(String bookName) { bookShelf.add(bookName); } public List<String> getBooks() { return bookShelf; } }
一旦您在JBOSS上部署了EjbComponent项目,请注意jboss日志。
JBoss已自动为我们的会话Bean创建了一个JNDI条目 - LibraryBean/remote。
我们将使用此查找字符串获取类型为com.tutorialspoint.interceptor.LibraryBeanRemote的远程业务对象。
JBoss 应用程序服务器日志输出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryBean/remote - EJB3.x Default Remote Business Interface LibraryBean/remote-com.tutorialspoint.interceptor.LibraryBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.interceptor.LibraryBeanRemote ejbName: LibraryBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryBean/remote - EJB3.x Default Remote Business Interface LibraryBean/remote-com.tutorialspoint.interceptor.LibraryBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客户端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
这些属性用于初始化java命名服务的InitialContext对象。
InitialContext对象将用于查找无状态会话Bean。
EJBTester.java
package com.tutorialspoint.test; import com.tutorialspoint.stateful.LibraryBeanRemote; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Properties; import javax.naming.InitialContext; import javax.naming.NamingException; public class EJBTester { BufferedReader brConsoleReader = null; Properties props; InitialContext ctx; { props = new Properties(); try { props.load(new FileInputStream("jndi.properties")); } catch (IOException ex) { ex.printStackTrace(); } try { ctx = new InitialContext(props); } catch (NamingException ex) { ex.printStackTrace(); } brConsoleReader = new BufferedReader(new InputStreamReader(System.in)); } public static void main(String[] args) { EJBTester ejbTester = new EJBTester(); ejbTester.testInterceptedEjb(); } private void showGUI() { System.out.println("**********************"); System.out.println("Welcome to Book Store"); System.out.println("**********************"); System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: "); } private void testInterceptedEjb() { try { int choice = 1; LibraryBeanRemote libraryBean = LibraryBeanRemote)ctx.lookup("LibraryBean/remote"); while (choice != 2) { String bookName; showGUI(); String strChoice = brConsoleReader.readLine(); choice = Integer.parseInt(strChoice); if (choice == 1) { System.out.print("Enter book name: "); bookName = brConsoleReader.readLine(); Book book = new Book(); book.setName(bookName); libraryBean.addBook(book); } else if (choice == 2) { break; } } List<Book> booksList = libraryBean.getBooks(); System.out.println("Book(s) entered so far: " + booksList.size()); int i = 0; for (Book book:booksList) { System.out.println((i+1)+". " + book.getName()); i++; } } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); }finally { try { if(brConsoleReader !=null) { brConsoleReader.close(); } } catch (IOException ex) { System.out.println(ex.getMessage()); } } } }
EJBTester执行以下任务:
从jndi.properties加载属性并初始化InitialContext对象。
在testInterceptedEjb()方法中,使用名称“LibraryBean/remote”进行JNDI查找,以获取远程业务对象(无状态EJB)。
然后向用户显示图书馆商店用户界面,并提示用户输入选择。
如果用户输入1,系统会要求输入书籍名称,并使用无状态会话Bean的addBook()方法保存书籍。会话Bean将其存储在实例变量中。
如果用户输入2,系统将使用无状态会话Bean的getBooks()方法检索书籍并退出。
运行客户端以访问 EJB
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在 Netbeans 控制台中验证以下输出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Java ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. Learn Java BUILD SUCCESSFUL (total time: 13 seconds)
JBoss 应用程序服务器日志输出
在JBoss应用程序服务器日志输出中验证以下输出。
.... 09:55:40,741 INFO [STDOUT] *** Intercepting call to LibraryBean method: addBook 09:55:43,661 INFO [STDOUT] *** Intercepting call to LibraryBean method: getBooks
EJB - 可嵌入对象
EJB 3.0提供选项将JAVA POJO(普通旧Java对象)嵌入到实体Bean中,并允许将列名与嵌入式POJO类的方法映射。要嵌入的Java POJO必须用@Embeddable注释。
@Embeddable public class Publisher implements Serializable{ private String name; private String address; ... }
可以使用@Embedded注解嵌入上面的类。
@Entity public class Book implements Serializable{ private int id; private String name; private Publisher publisher; ... @Embedded @AttributeOverrides({ @AttributeOverride(name = "name", column = @Column(name = "PUBLISHER")), @AttributeOverride(name = "address", column = @Column(name = "PUBLISHER_ADDRESS")) }) public Publisher getPublisher() { return publisher; } ... }
示例应用程序
让我们创建一个测试EJB应用程序来测试EJB 3.0中的嵌入对象。
步骤 | 描述 |
---|---|
1 | 在EJB - 创建应用程序章节中说明的com.tutorialspoint.entity包下,创建一个名为EjbComponent的项目。请在本节中使用EJB - 持久性章节中创建的项目,以便了解EJB概念中的嵌入对象。 |
2 | 在EJB - 创建应用程序章节中说明的com.tutorialspoint.entity包下创建Publisher.java。保持其余文件不变。 |
3 | 在com.tutorialspoint.entity包下创建Book.java。使用EJB - 持久性章节作为参考。保持其余文件不变。 |
4 | 清理并构建应用程序,以确保业务逻辑按要求工作。 |
5 | 最后,将应用程序以jar文件的形式部署到JBoss应用程序服务器上。如果JBoss应用程序服务器尚未启动,它将自动启动。 |
6 | 现在创建EJB客户端,一个基于控制台的应用程序,其方式与EJB - 创建应用程序章节中主题创建访问EJB的客户端中所述相同。 |
创建/修改Book表
CREATE TABLE book ( id integer PRIMARY KEY, name varchar(50) ); Alter table book add publisher varchar(100); Alter table book add publisher_address varchar(200);
EJBComponent(EJB模块)
Publisher.java
package com.tutorialspoint.entity; import java.io.Serializable; import javax.persistence.Embeddable; @Embeddable public class Publisher implements Serializable{ private String name; private String address; public Publisher() {} public Publisher(String name, String address) { this.name = name; this.address = address; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String toString() { return name + "," + address; } }
Book.java
package com.tutorialspoint.entity; import com.tutorialspoint.callback.BookCallbackListener; import java.io.Serializable; import javax.persistence.AttributeOverride; import javax.persistence.AttributeOverrides; import javax.persistence.Column; import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.EntityListeners; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="book") public class Book implements Serializable{ private int id; private String name; private Publisher publisher; public Book() { } @Id @GeneratedValue(strategy= GenerationType.IDENTITY) @Column(name="id") public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Embedded @AttributeOverrides({ @AttributeOverride(name = "name", column = @Column(name = "PUBLISHER")), @AttributeOverride(name = "address", column = @Column(name = "PUBLISHER_ADDRESS")) }) public Publisher getPublisher() { return publisher; } public void setPublisher(Publisher publisher) { this.publisher = publisher; } }
LibraryPersistentBeanRemote.java
package com.tutorialspoint.stateless; import com.tutorialspoint.entity.Book; import java.util.List; import javax.ejb.Remote; @Remote public interface LibraryPersistentBeanRemote { void addBook(Book bookName); List<Book> getBooks(); }
LibraryPersistentBean.java
package com.tutorialspoint.stateless; import com.tutorialspoint.entity.Book; import java.util.List; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Stateless public class LibraryPersistentBean implements LibraryPersistentBeanRemote { public LibraryPersistentBean() { } @PersistenceContext(unitName="EjbComponentPU") private EntityManager entityManager; public void addBook(Book book) { entityManager.persist(book); } public List<Book> getBooks() { return entityManager.createQuery("From Book").getResultList(); } }
一旦您在JBOSS上部署了EjbComponent项目,请注意jboss日志。
JBoss已自动为我们的会话Bean创建了一个JNDI条目 - LibraryPersistentBean/remote。
我们将使用此查找字符串获取类型为com.tutorialspoint.interceptor.LibraryPersistentBeanRemote的远程业务对象。
JBoss 应用程序服务器日志输出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.interceptor.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryPersistentBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.interceptor.LibraryPersistentBeanRemote ejbName: LibraryPersistentBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.interceptor.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客户端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
这些属性用于初始化java命名服务的InitialContext对象。
InitialContext对象将用于查找无状态会话Bean。
EJBTester.java
package com.tutorialspoint.test; import com.tutorialspoint.stateful.LibraryBeanRemote; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Properties; import javax.naming.InitialContext; import javax.naming.NamingException; public class EJBTester { BufferedReader brConsoleReader = null; Properties props; InitialContext ctx; { props = new Properties(); try { props.load(new FileInputStream("jndi.properties")); } catch (IOException ex) { ex.printStackTrace(); } try { ctx = new InitialContext(props); } catch (NamingException ex) { ex.printStackTrace(); } brConsoleReader = new BufferedReader(new InputStreamReader(System.in)); } public static void main(String[] args) { EJBTester ejbTester = new EJBTester(); ejbTester.testEmbeddedObjects(); } private void showGUI() { System.out.println("**********************"); System.out.println("Welcome to Book Store"); System.out.println("**********************"); System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: "); } private void testEmbeddedObjects() { try { int choice = 1; LibraryPersistentBeanRemote libraryBean = (LibraryPersistentBeanRemote) ctx.lookup("LibraryPersistentBean/remote"); while (choice != 2) { String bookName; String publisherName; String publisherAddress; showGUI(); String strChoice = brConsoleReader.readLine(); choice = Integer.parseInt(strChoice); if (choice == 1) { System.out.print("Enter book name: "); bookName = brConsoleReader.readLine(); System.out.print("Enter publisher name: "); publisherName = brConsoleReader.readLine(); System.out.print("Enter publisher address: "); publisherAddress = brConsoleReader.readLine(); Book book = new Book(); book.setName(bookName); book.setPublisher (new Publisher(publisherName,publisherAddress)); libraryBean.addBook(book); } else if (choice == 2) { break; } } List<Book> booksList = libraryBean.getBooks(); System.out.println("Book(s) entered so far: " + booksList.size()); int i = 0; for (Book book:booksList) { System.out.println((i+1)+". " + book.getName()); System.out.println("Publication: "+book.getPublisher()); i++; } } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); }finally { try { if(brConsoleReader !=null) { brConsoleReader.close(); } } catch (IOException ex) { System.out.println(ex.getMessage()); } } } }
EJBTester执行以下任务:
从jndi.properties加载属性并初始化InitialContext对象。
在testInterceptedEjb()方法中,使用名称“LibraryPersistenceBean/remote”进行JNDI查找,以获取远程业务对象(无状态EJB)。
然后向用户显示一个图书馆商店用户界面,并要求他/她输入选择。
如果用户输入1,系统会要求输入书籍名称,并使用无状态会话Bean的addBook()方法保存书籍。会话Bean将书籍存储到数据库中。
如果用户输入2,系统将使用无状态会话Bean的getBooks()方法检索书籍并退出。
运行客户端以访问 EJB
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在 Netbeans 控制台中验证以下输出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: learn html5 Enter publisher name: SAMS Enter publisher address: DELHI ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. learn html5 Publication: SAMS,DELHI BUILD SUCCESSFUL (total time: 21 seconds)
EJB - Blob/Clob
EJB 3.0使用@Lob注解支持Blob和Clob类型。可以使用@Lob注解映射以下Java类型。
- java.sql.Blob
- java.sql.Clob
- byte[]
- String
- 可序列化对象
@Entity @Table(name="books") @EntityListeners(BookCallbackListener.class) public class Book implements Serializable{ ... private byte[] image; @Lob @Basic(fetch= FetchType.EAGER) public byte[] getImage() { return image; } ... }
示例应用程序
让我们创建一个测试EJB应用程序来测试EJB 3.0中的blob/clob支持。
步骤 | 描述 |
---|---|
1 | 在EJB - 创建应用程序章节中说明的com.tutorialspoint.entity包下,创建一个名为EjbComponent的项目。请在本节中使用EJB - 持久性章节中创建的项目,以便了解ejb概念中的clob/blob对象。 |
2 | 在com.tutorialspoint.entity包下创建Book.java。使用EJB - 持久性章节作为参考。保持其余文件不变。 |
3 | 清理并构建应用程序,以确保业务逻辑按要求工作。 |
4 | 最后,将应用程序以jar文件的形式部署到JBoss应用程序服务器上。如果JBoss应用程序服务器尚未启动,它将自动启动。 |
5 | 现在创建EJB客户端,一个基于控制台的应用程序,其方式与EJB - 创建应用程序章节中主题创建访问EJB的客户端中所述相同。 |
创建/修改Book表
CREATE TABLE book ( id integer PRIMARY KEY, name varchar(50) ); Alter table book add image bytea; Alter table book add xml text;
EJBComponent(EJB模块)
Book.java
package com.tutorialspoint.entity; import com.tutorialspoint.callback.BookCallbackListener; import java.io.Serializable; import javax.persistence.Basic; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EntityListeners; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Lob; import javax.persistence.Table; @Entity @Table(name="book") public class Book implements Serializable{ private int id; private String name; private byte[] image; private String xml; public Book() { } @Id @GeneratedValue(strategy= GenerationType.IDENTITY) @Column(name="id") public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Lob @Basic(fetch= FetchType.EAGER) public byte[] getImage() { return image; } public void setImage(byte[] image) { this.image = image; } @Lob @Basic(fetch= FetchType.EAGER) public String getXml() { return xml; } public void setXml(String xml) { this.xml = xml; } }
LibraryPersistentBeanRemote.java
package com.tutorialspoint.stateless; import com.tutorialspoint.entity.Book; import java.util.List; import javax.ejb.Remote; @Remote public interface LibraryPersistentBeanRemote { void addBook(Book bookName); List<Book> getBooks(); }
LibraryPersistentBean.java
package com.tutorialspoint.stateless; import com.tutorialspoint.entity.Book; import java.util.List; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Stateless public class LibraryPersistentBean implements LibraryPersistentBeanRemote { public LibraryPersistentBean() { } @PersistenceContext(unitName="EjbComponentPU") private EntityManager entityManager; public void addBook(Book book) { entityManager.persist(book); } public List<Book> getBooks() { return entityManager.createQuery("From Book").getResultList(); } }
一旦您在JBOSS上部署了EjbComponent项目,请注意jboss日志。
JBoss已自动为我们的会话Bean创建了一个JNDI条目 - LibraryPersistentBean/remote。
我们将使用此查找字符串获取类型为com.tutorialspoint.interceptor.LibraryPersistentBeanRemote的远程业务对象。
JBoss 应用程序服务器日志输出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.interceptor.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryPersistentBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.interceptor.LibraryPersistentBeanRemote ejbName: LibraryPersistentBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.interceptor.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客户端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
这些属性用于初始化java命名服务的InitialContext对象。
InitialContext对象将用于查找无状态会话Bean。
EJBTester.java
package com.tutorialspoint.test; import com.tutorialspoint.stateful.LibraryBeanRemote; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Properties; import javax.naming.InitialContext; import javax.naming.NamingException; public class EJBTester { BufferedReader brConsoleReader = null; Properties props; InitialContext ctx; { props = new Properties(); try { props.load(new FileInputStream("jndi.properties")); } catch (IOException ex) { ex.printStackTrace(); } try { ctx = new InitialContext(props); } catch (NamingException ex) { ex.printStackTrace(); } brConsoleReader = new BufferedReader(new InputStreamReader(System.in)); } public static void main(String[] args) { EJBTester ejbTester = new EJBTester(); ejbTester.testBlobClob(); } private void showGUI() { System.out.println("**********************"); System.out.println("Welcome to Book Store"); System.out.println("**********************"); System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: "); } private void testBlobClob() { try { int choice = 1; LibraryPersistentBeanRemote libraryBean = (LibraryPersistentBeanRemote) ctx.lookup("LibraryPersistentBean/remote"); while (choice != 2) { String bookName; String publisherName; String publisherAddress; showGUI(); String strChoice = brConsoleReader.readLine(); choice = Integer.parseInt(strChoice); if (choice == 1) { System.out.print("Enter book name: "); bookName = brConsoleReader.readLine(); String xml = "<book><name>"+bookName+"</name></book>"; Book book = new Book(); book.setName(bookName); byte[] imageBytes = {0x32, 0x32,0x32, 0x32,0x32, 0x32,0x32, 0x32, 0x32, 0x32,0x32, 0x32,0x32, 0x32,0x32, 0x32, 0x32, 0x32,0x32, 0x32,0x32, 0x32,0x32, 0x32 }; book.setImage(imageBytes); book.setXml(xml); libraryBean.addBook(book); } else if (choice == 2) { break; } } List<Book> booksList = libraryBean.getBooks(); System.out.println("Book(s) entered so far: " + booksList.size()); int i = 0; for (Book book:booksList) { System.out.println((i+1)+". " + book.getName()); byte[] imageByts = book.getImage(); if(imageByts != null) { System.out.print("image bytes: ["); for(int j = 0; j < imageByts.length ; j++) { System.out.print("0x" + String.format("%x", imageByts[j]) +" "); } System.out.println("]"); } System.out.println(book.getXml()); i++; } } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); }finally { try { if(brConsoleReader !=null) { brConsoleReader.close(); } } catch (IOException ex) { System.out.println(ex.getMessage()); } } } }
EJBTester执行以下任务。
从jndi.properties加载属性并初始化InitialContext对象。
在testInterceptedEjb()方法中,使用名称“LibraryPersistenceBean/remote”进行JNDI查找,以获取远程业务对象(无状态EJB)。
然后向用户显示一个图书馆商店用户界面,并要求他/她输入选择。
如果用户输入1,系统会要求输入书籍名称,并使用无状态会话Bean的addBook()方法保存书籍。会话Bean将书籍存储到数据库中。
如果用户输入2,系统将使用无状态会话Bean的getBooks()方法检索书籍并退出。
运行客户端以访问 EJB
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在 Netbeans 控制台中验证以下输出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: learn testing ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. learn testing image bytes: [ 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 ] <book><name>learn testing</name></book> BUILD SUCCESSFUL (total time: 20 seconds)
EJB - 事务
事务是一组工作项,遵循ACID属性。ACID代表原子性、一致性、隔离性和持久性。
原子性 - 如果任何工作项失败,则整个单元将被视为失败。成功意味着所有项目都成功执行。
一致性 - 事务必须使系统保持一致状态。
隔离性 - 每个事务独立于任何其他事务执行。
持久性 - 如果事务已执行或提交,则应在系统故障中存活。
EJB容器/服务器是事务服务器,负责处理事务上下文传播和分布式事务。事务可以由容器管理,也可以由Bean代码中的自定义代码处理。
容器管理的事务 - 在这种类型中,容器管理事务状态。
Bean管理的事务 - 在这种类型中,开发人员管理事务状态的生命周期。
容器管理的事务
EJB 3.0指定了事务的以下属性,EJB容器实现了这些属性 -
REQUIRED - 指示业务方法必须在事务中执行,否则将为该方法启动一个新事务。
REQUIRES_NEW - 指示为业务方法启动一个新事务。
SUPPORTS - 指示业务方法将作为事务的一部分执行。
NOT_SUPPORTED - 指示业务方法不应作为事务的一部分执行。
MANDATORY - 指示业务方法将作为事务的一部分执行,否则将抛出异常。
NEVER - 指示如果业务方法作为事务的一部分执行,则将抛出异常。
示例
package com.tutorialspoint.txn.required; import javax.ejb.* @Stateless @TransactionManagement(TransactionManagementType.CONTAINER) public class UserDetailBean implements UserDetailRemote { private UserDetail; @TransactionAttribute(TransactionAttributeType.REQUIRED) public void createUserDetail() { //create user details object } }
createUserDetail()业务方法使用Required注解设置为Required。
package com.tutorialspoint.txn.required; import javax.ejb.* @Stateless public class UserSessionBean implements UserRemote { private User; @EJB private UserDetailRemote userDetail; public void createUser() { //create user //... //create user details userDetail.createUserDetail(); } }
createUser()业务方法使用createUserDetail()。如果在createUser()调用期间发生异常并且未创建User对象,则也不会创建UserDetail对象。
Bean管理的事务
在Bean管理的事务中,可以通过在应用程序级别处理异常来管理事务。
以下是需要考虑的关键点 -
开始 - 何时在业务方法中启动事务。
成功 - 确定事务要提交时的成功场景。
失败 - 确定事务要回滚时的失败场景。
示例
package com.tutorialspoint.txn.bmt; import javax.annotation.Resource; import javax.ejb.Stateless; import javax.ejb.TransactionManagement; import javax.ejb.TransactionManagementType; import javax.transaction.UserTransaction; @Stateless @TransactionManagement(value=TransactionManagementType.BEAN) public class AccountBean implements AccountBeanLocal { @Resource private UserTransaction userTransaction; public void transferFund(Account fromAccount, double fund , Account toAccount) throws Exception{ try{ userTransaction.begin(); confirmAccountDetail(fromAccount); withdrawAmount(fromAccount,fund); confirmAccountDetail(toAccount); depositAmount(toAccount,fund); userTransaction.commit(); }catch (InvalidAccountException exception) { userTransaction.rollback(); }catch (InsufficientFundException exception) { userTransaction.rollback(); }catch (PaymentException exception) { userTransaction.rollback(); } } private void confirmAccountDetail(Account account) throws InvalidAccountException { } private void withdrawAmount() throws InsufficientFundException { } private void depositAmount() throws PaymentException{ } }
在此示例中,我们使用UserTransaction接口使用userTransaction.begin()方法调用来标记事务的开始。我们使用userTransaction.commit()方法标记事务的完成,如果在事务期间发生任何异常,则使用userTransaction.rollback()方法调用回滚整个事务。
EJB - 安全性
安全性是任何企业级应用程序的主要关注点。它包括识别访问应用程序的用户或系统。根据身份识别,它允许或拒绝访问应用程序中的资源。EJB容器管理标准安全问题,或者可以对其进行自定义以处理任何特定安全问题。
安全的重要术语
身份验证 - 这是确保访问系统或应用程序的用户已验证为真实的流程。
授权 - 这是确保经过身份验证的用户具有访问系统资源的正确权限级别的流程。
用户 - 用户代表访问应用程序的客户端或系统。
用户组 - 用户可能是具有某些权限的组的一部分,例如管理员组。
用户角色 - 角色定义用户拥有的权限级别或访问系统资源的权限。
容器管理的安全
EJB 3.0指定了安全性的以下属性/注解,EJB容器实现了这些属性。
DeclareRoles - 指示类将接受声明的角色。注解应用于类级别。
RolesAllowed - 指示方法可以由指定角色的用户访问。可以应用于类级别,这会导致类所有方法都可以由指定角色的用户访问。
PermitAll - 指示业务方法对所有人可见。它可以应用于类级别以及方法级别。
DenyAll - 指示业务方法对类或方法级别指定的任何用户都不可访问。
示例
package com.tutorialspoint.security.required; import javax.ejb.* @Stateless @DeclareRoles({"student" "librarian"}) public class LibraryBean implements LibraryRemote { @RolesAllowed({"librarian"}) public void delete(Book book) { //delete book } @PermitAll public void viewBook(Book book) { //view book } @DenyAll public void deleteAll() { //delete all books } }
安全配置
在配置文件中映射角色和用户组。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sun-ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 EJB 3.0//EN" "http://www.sun.com/software/appserver/dtds/sun-ejb-jar_3_0-0.dtd"> <ejb-jar> <security-role-mapping> <role-name>student</role-name> <group-name>student-group</group-name> </security-role-mapping> <security-role-mapping> <role-name>librarian</role-name> <group-name>librarian-group</group-name> </security-role-mapping> <enterprise-beans/> </ejb-jar>
EJB - JNDI 绑定
JNDI代表Java命名和目录接口。它是一组API和服务接口。基于Java的应用程序使用JNDI进行命名和目录服务。在EJB的上下文中,有两个术语。
绑定 - 这指的是为EJB对象分配一个名称,该名称可以在以后使用。
查找 - 这指的是查找和获取EJB的对象。
在Jboss中,会话Bean默认情况下以以下格式绑定到JNDI中。
本地 - EJB-name/local
远程 - EJB-name/remote
如果EJB与<application-name>.ear文件捆绑在一起,则默认格式如下 -
本地 - application-name/ejb-name/local
远程 - application-name/ejb-name/remote
默认绑定的示例
请参阅EJB - 创建应用程序章节的JBoss控制台输出。
JBoss 应用程序服务器日志输出
... 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibrarySessionBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibrarySessionBean ejbName: LibrarySessionBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibrarySessionBean/remote - EJB3.x Default Remote Business Interface LibrarySessionBean/remote-com.tutorialspoint.stateless.LibrarySessionBeanRemote - EJB3.x Remote Business Interface ...
自定义绑定
以下注解可用于自定义默认JNDI绑定 -
本地 - org.jboss.ejb3.LocalBinding
远程 - org.jboss.ejb3.RemoteBindings
更新LibrarySessionBean.java。请参阅EJB - 创建应用程序章节。
LibrarySessionBean
package com.tutorialspoint.stateless; import java.util.ArrayList; import java.util.List; import javax.ejb.Stateless; @Stateless @LocalBinding(jndiBinding="tutorialsPoint/librarySession") public class LibrarySessionBean implements LibrarySessionBeanLocal { List<String> bookShelf; public LibrarySessionBean() { bookShelf = new ArrayList<String>(); } public void addBook(String bookName) { bookShelf.add(bookName); } public List<String> getBooks() { return bookShelf; } }
LibrarySessionBeanLocal
package com.tutorialspoint.stateless; import java.util.List; import javax.ejb.Local; @Local public interface LibrarySessionBeanLocal { void addBook(String bookName); List getBooks(); }
构建项目,将应用程序部署到Jboss,并在Jboss控制台中验证以下输出 -
... 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibrarySessionBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibrarySessionBean ejbName: LibrarySessionBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: tutorialsPoint/librarySession - EJB3.x Default Local Business Interface tutorialsPoint/librarySession-com.tutorialspoint.stateless.LibrarySessionBeanLocal - EJB3.x Local Business Interface ...
EJB - 实体关系
EJB 3.0提供选项定义数据库实体关系/映射,如一对一、一对多、多对一和多对多关系。
以下是相关的注解 -
一对一 − 对象之间存在一对一的关系。例如,乘客一次只能使用一张票出行。
一对多 − 对象之间存在一对多的关系。例如,一位父亲可以有多个孩子。
多对一 − 对象之间存在多对一的关系。例如,多个孩子共有一个母亲。
多对多 − 对象之间存在多对多的关系。例如,一本书可以有多个作者,一个作者可以写多本书。
这里我们将演示多对多映射的使用。为了表示多对多的关系,需要以下三个表:
Book − 书籍表,包含书籍的记录。
Author − 作者表,包含作者的记录。
Book_Author − 书籍作者表,包含上述书籍表和作者表的关联关系。
创建表
在默认数据库postgres中创建book、author和book_author表。
CREATE TABLE book ( book_id integer, name varchar(50) ); CREATE TABLE author ( author_id integer, name varchar(50) ); CREATE TABLE book_author ( book_id integer, author_id integer );
创建实体类
@Entity @Table(name="author") public class Author implements Serializable{ private int id; private String name; ... } @Entity @Table(name="book") public class Book implements Serializable{ private int id; private String title; private Set<Author> authors; ... }
在Book实体中使用ManyToMany注解。
@Entity public class Book implements Serializable{ ... @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE} , fetch = FetchType.EAGER) @JoinTable(table = @Table(name = "book_author"), joinColumns = {@JoinColumn(name = "book_id")}, inverseJoinColumns = {@JoinColumn(name = "author_id")}) public Set<Author> getAuthors() { return authors; } ... }
示例应用程序
让我们创建一个测试EJB应用程序,以测试EJB 3.0中实体关系对象。
步骤 | 描述 |
---|---|
1 | 在EJB - 创建应用程序章节中说明的com.tutorialspoint.entity包下,创建一个名为EjbComponent的项目。请在本节中使用EJB - 持久性章节中创建的项目,以便了解EJB概念中的嵌入对象。 |
2 | 在com.tutorialspoint.entity包下创建Author.java文件,如EJB - 创建应用程序章节中所述。保持其他文件不变。 |
3 | 在com.tutorialspoint.entity包下创建Book.java。使用EJB - 持久性章节作为参考。保持其余文件不变。 |
4 | 清理并构建应用程序,以确保业务逻辑按要求工作。 |
5 | 最后,将应用程序以jar文件的形式部署到JBoss应用服务器上。如果JBoss应用服务器尚未启动,它将自动启动。 |
6 | 现在创建EJB客户端,一个基于控制台的应用程序,其方式与EJB - 创建应用程序章节中主题创建访问EJB的客户端中所述相同。 |
EJBComponent(EJB模块)
Author.java
package com.tutorialspoint.entity; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="author") public class Author implements Serializable{ private int id; private String name; public Author() {} public Author(int id, String name) { this.id = id; this.name = name; } @Id @GeneratedValue(strategy= GenerationType.IDENTITY) @Column(name="author_id") public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return id + "," + name; } }
Book.java
package com.tutorialspoint.entity; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; @Entity @Table(name="book") public class Book implements Serializable{ private int id; private String name; private Set<Author> authors; public Book() { } @Id @GeneratedValue(strategy= GenerationType.IDENTITY) @Column(name="book_id") public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void setAuthors(Set<Author> authors) { this.authors = authors; } @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE} , fetch = FetchType.EAGER) @JoinTable(table = @Table(name = "book_author"), joinColumns = {@JoinColumn(name = "book_id")}, inverseJoinColumns = {@JoinColumn(name = "author_id")}) public Set<Author> getAuthors() { return authors; } }
LibraryPersistentBeanRemote.java
package com.tutorialspoint.stateless; import com.tutorialspoint.entity.Book; import java.util.List; import javax.ejb.Remote; @Remote public interface LibraryPersistentBeanRemote { void addBook(Book bookName); List<Book> getBooks(); }
LibraryPersistentBean.java
package com.tutorialspoint.stateless; import com.tutorialspoint.entity.Book; import java.util.List; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Stateless public class LibraryPersistentBean implements LibraryPersistentBeanRemote { public LibraryPersistentBean() { } @PersistenceContext(unitName="EjbComponentPU") private EntityManager entityManager; public void addBook(Book book) { entityManager.persist(book); } public List<Book> getBooks() { return entityManager.createQuery("From Book").getResultList(); } }
一旦您在JBOSS上部署了EjbComponent项目,请注意jboss日志。
JBoss已自动为我们的会话Bean创建了一个JNDI条目 - LibraryPersistentBean/remote。
我们将使用此查找字符串获取类型为com.tutorialspoint.interceptor.LibraryPersistentBeanRemote的远程业务对象。
JBoss 应用程序服务器日志输出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.interceptor.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryPersistentBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.interceptor.LibraryPersistentBeanRemote ejbName: LibraryPersistentBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.interceptor.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客户端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
这些属性用于初始化java命名服务的InitialContext对象。
InitialContext对象将用于查找无状态会话Bean。
EJBTester.java
package com.tutorialspoint.test; import com.tutorialspoint.stateful.LibraryBeanRemote; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.*; import javax.naming.InitialContext; import javax.naming.NamingException; public class EJBTester { BufferedReader brConsoleReader = null; Properties props; InitialContext ctx; { props = new Properties(); try { props.load(new FileInputStream("jndi.properties")); } catch (IOException ex) { ex.printStackTrace(); } try { ctx = new InitialContext(props); } catch (NamingException ex) { ex.printStackTrace(); } brConsoleReader = new BufferedReader(new InputStreamReader(System.in)); } public static void main(String[] args) { EJBTester ejbTester = new EJBTester(); ejbTester.testEmbeddedObjects(); } private void showGUI() { System.out.println("**********************"); System.out.println("Welcome to Book Store"); System.out.println("**********************"); System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: "); } private void testEmbeddedObjects() { try { int choice = 1; LibraryPersistentBeanRemote libraryBean = (LibraryPersistentBeanRemote) ctx.lookup("LibraryPersistentBean/remote"); while (choice != 2) { String bookName; String authorName; showGUI(); String strChoice = brConsoleReader.readLine(); choice = Integer.parseInt(strChoice); if (choice == 1) { System.out.print("Enter book name: "); bookName = brConsoleReader.readLine(); System.out.print("Enter author name: "); authorName = brConsoleReader.readLine(); Book book = new Book(); book.setName(bookName); Author author = new Author(); author.setName(authorName); Set<Author> authors = new HashSet<Author>(); authors.add(author); book.setAuthors(authors); libraryBean.addBook(book); } else if (choice == 2) { break; } } List<Book> booksList = libraryBean.getBooks(); System.out.println("Book(s) entered so far: " + booksList.size()); int i = 0; for (Book book:booksList) { System.out.println((i+1)+". " + book.getName()); System.out.print("Author: "); Author[] authors = (Author[])books.getAuthors().toArray(); for(int j=0;j<authors.length;j++) { System.out.println(authors[j]); } i++; } } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); }finally { try { if(brConsoleReader !=null) { brConsoleReader.close(); } } catch (IOException ex) { System.out.println(ex.getMessage()); } } } }
EJBTester执行以下任务:
从jndi.properties加载属性并初始化InitialContext对象。
在testInterceptedEjb()方法中,使用名称“LibraryPersistenceBean/remote”进行JNDI查找,以获取远程业务对象(无状态EJB)。
然后向用户显示一个图书馆商店用户界面,并要求他/她输入选择。
如果用户输入1,系统会要求输入书籍名称,并使用无状态会话Bean的addBook()方法保存书籍。会话Bean将书籍存储到数据库中。
如果用户输入2,系统将使用无状态会话Bean的getBooks()方法检索书籍并退出。
运行客户端以访问 EJB
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在 Netbeans 控制台中验证以下输出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: learn html5 Enter Author name: Robert ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. learn html5 Author: Robert BUILD SUCCESSFUL (total time: 21 seconds)
EJB - 访问数据库
在EJB 3.0中,持久化机制用于访问数据库,其中容器管理数据库相关操作。开发人员可以直接在EJB业务方法中使用JDBC API调用访问数据库。
为了演示EJB中的数据库访问,我们需要执行以下任务:
步骤1 − 在数据库中创建一个表。
步骤2 − 创建一个包含业务方法的无状态EJB。
步骤3 − 更新无状态EJB。添加方法,通过实体管理器向数据库添加记录和获取记录。
步骤4 − 一个基于控制台的应用程序客户端将访问无状态EJB,以将数据持久化到数据库中。
创建表
在默认数据库postgres中创建一个名为books的表。
CREATE TABLE books ( id integer PRIMARY KEY, name varchar(50) );
创建模型类
public class Book implements Serializable{ private int id; private String name; public Book() { } public int getId() { return id; } ... }
创建无状态EJB
@Stateless public class LibraryPersistentBean implements LibraryPersistentBeanRemote { public void addBook(Book book) { //persist book using jdbc calls } public List<Book> getBooks() { //get books using jdbc calls } ... }
构建EJB模块后,我们需要一个客户端来访问无状态Bean,我们将在下一节中创建。
示例应用程序
让我们创建一个测试EJB应用程序,以测试EJB数据库访问机制。
步骤 | 描述 |
---|---|
1 | 在com.tutorialspoint.entity包下创建一个名为EjbComponent的项目,如EJB - 创建应用程序章节中所述。您也可以使用EJB - 创建应用程序章节中创建的项目,以便理解EJB数据访问的概念。 |
2 | 在包com.tutorialspoint.entity下创建Book.java,并按如下所示修改它。 |
3 | 创建LibraryPersistentBean.java和LibraryPersistentBeanRemote,如EJB - 创建应用程序章节中所述,并按如下所示修改它们。 |
4 | 清理并构建应用程序,以确保业务逻辑按要求工作。 |
5 | 最后,将应用程序以jar文件的形式部署到JBoss应用服务器上。如果JBoss应用服务器尚未启动,它将自动启动。 |
6 | 现在创建EJB客户端,一个基于控制台的应用程序,其方式与EJB - 创建应用程序章节中主题创建访问EJB的客户端中所述相同。按如下所示修改它。 |
EJBComponent(EJB模块)
Book.java
package com.tutorialspoint.entity; import java.io.Serializable; public class Book implements Serializable{ private int id; private String name; public Book() { } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
LibraryPersistentBeanRemote.java
package com.tutorialspoint.stateless; import com.tutorialspoint.entity.Book; import java.util.List; import javax.ejb.Remote; @Remote public interface LibraryPersistentBeanRemote { void addBook(Book bookName); List<Book> getBooks(); }
LibraryPersistentBean.java
package com.tutorialspoint.stateless; import com.tutorialspoint.entity.Book; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import javax.ejb.Stateless; @Stateless public class LibraryPersistentBean implements LibraryPersistentBeanRemote { public LibraryPersistentBean() { } public void addBook(Book book) { Connection con = null; String url = "jdbc:postgresql://127.0.0.1:5432/postgres"; String driver = "org.postgresql.driver"; String userName = "sa"; String password = "sa"; List<Book> books = new ArrayList<Book>(); try { Class.forName(driver).newInstance(); con = DriverManager.getConnection(url , userName, password); PreparedStatement st = con.prepareStatement("insert into book(name) values(?)"); st.setString(1,book.getName()); int result = st.executeUpdate(); } catch (SQLException ex) { ex.printStackTrace(); } catch (InstantiationException ex) { ex.printStackTrace(); } catch (IllegalAccessException ex) { ex.printStackTrace(); } catch (ClassNotFoundException ex) { ex.printStackTrace(); } } public List<Book> getBooks() { Connection con = null; String url = "jdbc:postgresql://127.0.0.1:5432/postgres"; String driver = "org.postgresql.driver"; String userName = "sa"; String password = "sa"; List<Book> books = new ArrayList<Book>(); try { Class.forName(driver).newInstance(); con = DriverManager.getConnection(url , userName, password); Statement st = con.createStatement(); ResultSet rs = st.executeQuery("select * from book"); Book book; while (rs.next()) { book = new Book(); book.setId(rs.getInt(1)); book.setName(rs.getString(2)); books.add(book); } } catch (SQLException ex) { ex.printStackTrace(); } catch (InstantiationException ex) { ex.printStackTrace(); } catch (IllegalAccessException ex) { ex.printStackTrace(); } catch (ClassNotFoundException ex) { ex.printStackTrace(); } return books; } }
一旦您在JBOSS上部署了EjbComponent项目,请注意jboss日志。
JBoss已自动为我们的会话Bean创建了一个JNDI条目 - LibraryPersistentBean/remote。
我们将使用此查找字符串获取类型为com.tutorialspoint.stateless.LibraryPersistentBeanRemote的远程业务对象。
JBoss 应用程序服务器日志输出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.stateless.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryPersistentBeanRemote,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibraryPersistentBeanRemote ejbName: LibraryPersistentBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.stateless.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客户端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
这些属性用于初始化java命名服务的InitialContext对象。
InitialContext对象将用于查找无状态会话Bean。
EJBTester.java
package com.tutorialspoint.test; import com.tutorialspoint.stateless.LibraryPersistentBeanRemote; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Properties; import javax.naming.InitialContext; import javax.naming.NamingException; public class EJBTester { BufferedReader brConsoleReader = null; Properties props; InitialContext ctx; { props = new Properties(); try { props.load(new FileInputStream("jndi.properties")); } catch (IOException ex) { ex.printStackTrace(); } try { ctx = new InitialContext(props); } catch (NamingException ex) { ex.printStackTrace(); } brConsoleReader = new BufferedReader(new InputStreamReader(System.in)); } public static void main(String[] args) { EJBTester ejbTester = new EJBTester(); ejbTester.testEntityEjb(); } private void showGUI() { System.out.println("**********************"); System.out.println("Welcome to Book Store"); System.out.println("**********************"); System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: "); } private void testEntityEjb() { try { int choice = 1; LibraryPersistentBeanRemote libraryBean = LibraryPersistentBeanRemote) ctx.lookup("LibraryPersistentBean/remote"); while (choice != 2) { String bookName; showGUI(); String strChoice = brConsoleReader.readLine(); choice = Integer.parseInt(strChoice); if (choice == 1) { System.out.print("Enter book name: "); bookName = brConsoleReader.readLine(); Book book = new Book(); book.setName(bookName); libraryBean.addBook(book); } else if (choice == 2) { break; } } List<Book> booksList = libraryBean.getBooks(); System.out.println("Book(s) entered so far: " + booksList.size()); int i = 0; for (Book book:booksList) { System.out.println((i+1)+". " + book.getName()); i++; } } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); }finally { try { if(brConsoleReader !=null) { brConsoleReader.close(); } } catch (IOException ex) { System.out.println(ex.getMessage()); } } } }
EJBTester执行以下任务:
从jndi.properties加载属性并初始化InitialContext对象。
在testStatefulEjb()方法中,使用名称“LibraryStatelessSessionBean/remote”进行JNDI查找,以获取远程业务对象(有状态EJB)。
然后向用户显示图书馆商店用户界面,并提示用户输入选择。
如果用户输入1,系统将提示输入书籍名称,并使用无状态会话Bean的addBook()方法保存书籍。会话Bean通过EntityManager调用将书籍持久化到数据库中。
如果用户输入2,系统将使用无状态会话Bean的getBooks()方法检索书籍并退出。
然后,再次使用名称“LibraryStatelessSessionBean/remote”进行JNDI查找,以获取远程业务对象(有状态EJB),并列出书籍。
运行客户端以访问 EJB
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在 Netbeans 控制台中验证以下输出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Java ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. learn java BUILD SUCCESSFUL (total time: 15 seconds)
EJB - 查询语言
EJB查询语言非常方便编写自定义查询,而无需担心底层数据库的细节。它与HQL(Hibernate查询语言)非常相似,通常被称为EJBQL。
为了演示EJB中EJBQL的使用,我们将执行以下任务:
步骤1 - 在数据库中创建表。
步骤2 − 创建一个包含业务方法的无状态EJB。
步骤3 − 更新无状态EJB。添加方法,通过实体管理器向数据库添加记录和获取记录。
步骤4 − 一个基于控制台的应用程序客户端将访问无状态EJB,以将数据持久化到数据库中。
创建表
在默认数据库postgres中创建一个名为books的表。
CREATE TABLE books ( id integer PRIMARY KEY, name varchar(50) );
创建模型类
public class Book implements Serializable{ private int id; private String name; public Book() { } public int getId() { return id; } ... }
创建无状态EJB
@Stateless public class LibraryPersistentBean implements LibraryPersistentBeanRemote { public void addBook(Book book) { //persist book using entity manager } public List<Book> getBooks() { //get books using entity manager } ... }
构建EJB模块后,我们需要一个客户端来访问无状态Bean,我们将在下一节中创建。
示例应用程序
让我们创建一个测试EJB应用程序,以测试EJB数据库访问机制。
步骤 | 描述 |
---|---|
1 | 在com.tutorialspoint.entity包下创建一个名为EjbComponent的项目,如EJB - 创建应用程序章节中所述。您也可以使用EJB - 创建应用程序章节中创建的项目,以便理解EJB数据访问的概念。 |
2 | 在包com.tutorialspoint.entity下创建Book.java,并按如下所示修改它。 |
3 | 创建LibraryPersistentBean.java和LibraryPersistentBeanRemote,如EJB - 创建应用程序章节中所述,并按如下所示修改它们。 |
4 | 清理并构建应用程序,以确保业务逻辑按要求工作。 |
5 | 最后,将应用程序以jar文件的形式部署到JBoss应用服务器上。如果JBoss应用服务器尚未启动,它将自动启动。 |
6 | 现在创建EJB客户端,一个基于控制台的应用程序,其方式与EJB - 创建应用程序章节中主题创建访问EJB的客户端中所述相同。按如下所示修改它。 |
EJBComponent(EJB模块)
Book.java
package com.tutorialspoint.entity; import java.io.Serializable; public class Book implements Serializable{ private int id; private String name; public Book() { } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
LibraryPersistentBeanRemote.java
package com.tutorialspoint.stateless; import com.tutorialspoint.entity.Book; import java.util.List; import javax.ejb.Remote; @Remote public interface LibraryPersistentBeanRemote { void addBook(Book bookName); List<Book> getBooks(); }
LibraryPersistentBean.java
package com.tutorialspoint.stateless; import com.tutorialspoint.entity.Book; import java.util.List; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; @Stateless public class LibraryPersistentBean implements LibraryPersistentBeanRemote { public LibraryPersistentBean() { } @PersistenceContext(unitName="EntityEjbPU") private EntityManager entityManager; public void addBook(Book book) { entityManager.persist(book); } public List<Book> getBooks() { //create an ejbql expression String ejbQL = "From Book b where b.name like ?1"; //create query Query query = entityManager.createQuery(ejbQL); //substitute parameter. query.setParameter(1, "%test%"); //execute the query return query.getResultList(); } }
一旦您在JBOSS上部署了EjbComponent项目,请注意jboss日志。
JBoss已自动为我们的会话Bean创建了一个JNDI条目 - LibraryPersistentBean/remote。
我们将使用此查找字符串获取类型为com.tutorialspoint.stateless.LibraryPersistentBeanRemote的远程业务对象。
JBoss 应用程序服务器日志输出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.stateless.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryPersistentBeanRemote,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibraryPersistentBeanRemote ejbName: LibraryPersistentBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.stateless.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客户端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
这些属性用于初始化java命名服务的InitialContext对象。
InitialContext对象将用于查找无状态会话Bean。
EJBTester.java
package com.tutorialspoint.test; import com.tutorialspoint.stateless.LibraryPersistentBeanRemote; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Properties; import javax.naming.InitialContext; import javax.naming.NamingException; public class EJBTester { BufferedReader brConsoleReader = null; Properties props; InitialContext ctx; { props = new Properties(); try { props.load(new FileInputStream("jndi.properties")); } catch (IOException ex) { ex.printStackTrace(); } try { ctx = new InitialContext(props); } catch (NamingException ex) { ex.printStackTrace(); } brConsoleReader = new BufferedReader(new InputStreamReader(System.in)); } public static void main(String[] args) { EJBTester ejbTester = new EJBTester(); ejbTester.testEntityEjb(); } private void showGUI() { System.out.println("**********************"); System.out.println("Welcome to Book Store"); System.out.println("**********************"); System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: "); } private void testEntityEjb() { try { int choice = 1; LibraryPersistentBeanRemote libraryBean = LibraryPersistentBeanRemote) ctx.lookup("LibraryPersistentBean/remote"); while (choice != 2) { String bookName; showGUI(); String strChoice = brConsoleReader.readLine(); choice = Integer.parseInt(strChoice); if (choice == 1) { System.out.print("Enter book name: "); bookName = brConsoleReader.readLine(); Book book = new Book(); book.setName(bookName); libraryBean.addBook(book); } else if (choice == 2) { break; } } List<Book> booksList = libraryBean.getBooks(); System.out.println("Book(s) entered so far: " + booksList.size()); int i = 0; for (Book book:booksList) { System.out.println((i+1)+". " + book.getName()); i++; } } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); }finally { try { if(brConsoleReader !=null) { brConsoleReader.close(); } } catch (IOException ex) { System.out.println(ex.getMessage()); } } } }
EJBTester执行以下任务:
从jndi.properties加载属性并初始化InitialContext对象。
在testStatefulEjb()方法中,使用名称“LibraryStatelessSessionBean/remote”进行JNDI查找,以获取远程业务对象(有状态EJB)。
然后向用户显示图书馆商店用户界面,并提示用户输入选择。
如果用户输入1,系统将提示输入书籍名称,并使用无状态会话Bean的addBook()方法保存书籍。会话Bean通过EntityManager调用将书籍持久化到数据库中。
如果用户输入2,系统将使用无状态会话Bean的getBooks()方法检索书籍并退出。
然后,再次使用名称“LibraryStatelessSessionBean/remote”进行JNDI查找,以获取远程业务对象(有状态EJB),并列出书籍。
运行客户端以访问 EJB
在项目浏览器中找到 EJBTester.java。右键单击 EJBTester 类并选择运行文件。
在 Netbeans 控制台中验证以下输出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Testing ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. learn Testing BUILD SUCCESSFUL (total time: 15 seconds)
EJB - 异常处理
EJB是企业应用程序的一部分,这些应用程序通常基于分布式环境。因此,除了可能发生的普通异常之外,还可能发生诸如通信失败、安全权限、服务器宕机等异常。
EJB容器以两种方式考虑异常:
应用程序异常 − 如果违反业务规则或在执行业务逻辑时发生异常。
系统异常 − 任何不是由业务逻辑或业务代码引起的异常。RuntimeException、RemoteException都是SystemException。例如,EJB查找期间发生错误。RuntimeException、RemoteException都是SystemException。
EJB容器如何处理异常?
当发生应用程序异常时,EJB容器会拦截异常,但将其原样返回给客户端。它不会回滚事务,除非在代码中通过EJBContext.setRollBackOnly()方法指定。在应用程序异常的情况下,EJB容器不会包装异常。
当发生系统异常时,EJB容器会拦截异常,回滚事务并开始清理任务。它将异常包装到RemoteException中,并将其抛给客户端。
处理应用程序异常
应用程序异常通常在会话EJB方法中抛出,因为这些方法负责执行业务逻辑。应用程序异常应在业务方法的throws子句中声明,并在业务逻辑失败时抛出。
@Stateless public class LibraryPersistentBean implements LibraryPersistentBeanRemote { ... public List<Book> getBooks() throws NoBookAvailableException { List<Book> books = entityManager.createQuery("From Books").getResultList(); if(books.size == 0) throw NoBookAvailableException ("No Book available in library."); return books; } ... }
处理系统异常
系统异常可能随时发生,例如命名查找失败、获取数据时发生SQL错误。在这种情况下,此类异常应包装在EJBException中,并抛回给客户端。
@Stateless public class LibraryPersistentBean implements LibraryPersistentBeanRemote { ... public List<Book> getBooks() { try { List<Book> books = entityManager.createQuery("From Books").getResultList(); } catch (CreateException ce) { throw (EJBException) new EJBException(ce).initCause(ce); } catch (SqlException se) { throw (EJBException) new EJBException(se).initCause(se); } return books; } ... }
在客户端,处理EJBException。
public class EJBTester { private void testEntityEjb() { ... try{ LibraryPersistentBeanRemote libraryBean = LibraryPersistentBeanRemote)ctx.lookup("LibraryPersistentBean/remote"); List<Book> booksList = libraryBean.getBooks(); } catch(EJBException e) { Exception ne = (Exception) e.getCause(); if(ne.getClass().getName().equals("SqlException")) { System.out.println("Database error: "+ e.getMessage()); } } ... } }
EJB - Web 服务
EJB 3.0提供了一个选项,可以将会话EJB公开为Web服务。@WebService注解用于将类标记为Web服务端点,@WebMethod用于将方法公开为客户端的Web方法。
@Stateless @WebService(serviceName="LibraryService") public class LibraryPersistentBean implements LibraryPersistentBeanRemote { ... @WebMethod(operationName="getBooks") public List<Book> getBooks() { return entityManager.createQuery("From Books").getResultList(); } ... }
示例应用程序
让我们创建一个测试EJB应用程序来测试EJB 3.0中的blob/clob支持。
步骤 | 描述 |
---|---|
1 | 在com.tutorialspoint.entity包下创建一个名为EjbComponent的项目,如EJB - 创建应用程序章节中所述。请使用EJB - 持久化章节中创建的项目,以便理解EJB概念中的clob/blob对象。 |
2 | 在com.tutorialspoint.stateless包下创建LibraryPersistentBean.java。使用EJB - 持久化章节作为参考。保持其他文件不变。 |
3 | 清理并构建应用程序,以确保业务逻辑按要求工作。 |
4 | 最后,将应用程序以jar文件的形式部署到JBoss应用服务器上。如果JBoss应用服务器尚未启动,它将自动启动。 |
LibraryPersistentBean.java
package com.tutorialspoint.stateless; import com.tutorialspoint.entity.Book; import java.util.List; import javax.ejb.Stateless; import javax.jws.WebMethod; import javax.jws.WebService; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Stateless @WebService(serviceName="LibraryService") public class LibraryPersistentBean implements LibraryPersistentBeanRemote { public LibraryPersistentBean() { } @PersistenceContext(unitName="EjbComponentPU") private EntityManager entityManager; public void addBook(Book book) { entityManager.persist(book); } @WebMethod(operationName="getBooks") public List <Book> getBooks() { return entityManager.createQuery("From Book").getResultList(); } }
JBoss 应用程序服务器日志输出
10:51:37,271 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibraryPersistentBean ejbName: LibraryPersistentBean 10:51:37,287 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.stateless.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface 10:51:37,349 INFO [EJBContainer] STARTED EJB: com.tuturialspoint.messagebean.LibraryMessageBean ejbName: BookMessageHandler 10:51:37,443 INFO [DefaultEndpointRegistry] register: jboss.ws:context=EjbComponent,endpoint=LibraryPersistentBean 10:51:38,191 INFO [WSDLFilePublisher] WSDL published to: file:/D:/Jboss-5.0.1/server/default/data/wsdl/EjbComponent.jar/ LibraryService3853081455302946642.wsdl
创建客户端以访问EJB作为Web服务
在NetBeans IDE中,选择文件 > 新建项目 >。在类别下选择项目类型Java,项目类型为Java应用程序。单击下一步 >按钮。输入项目名称和位置。单击完成 >按钮。我们选择名称为EJBWebServiceClient。
在项目资源管理器窗口中,右键单击项目名称。选择新建 > Web服务客户端。
使用添加项目按钮在编译选项卡中添加前面创建的EJB组件项目的LibraryPersistentBean,以及WSDL和客户端位置。
单击完成按钮。在项目资源管理器中验证以下结构。
创建EJBWebServiceClient.java
package ejbwebserviceclient; public class EJBWebServiceClient { public static void main(String[] args) { } }
选择Web服务getBooks Web方法,如下图所示,并将其拖动到EJBWebServiceClient的代码窗口。
您将看到类似于以下所示的输出。
更新EJBWebServiceClient代码以使用此方法。
package ejbwebserviceclient; public class EJBWebServiceClient { public static void main(String[] args) { for(com.tutorialspoint.stateless.Book book:getBooks()) { System.out.println(book.getName()); } } private static java.util.List <com.tutorialspoint.stateless.Book> getBooks() { com.tutorialspoint.stateless.LibraryService service = new com.tutorialspoint.stateless.LibraryService(); com.tutorialspoint.stateless.LibraryPersistentBean port = service.getLibraryPersistentBeanPort(); return port.getBooks(); } }
运行客户端
在项目资源管理器窗口中,右键单击项目名称。选择运行。Netbeans将构建客户端并运行它。验证以下输出。
ant -f D:\\SVN\\EJBWebServiceClient run init: Deleting: D:\SVN\EJBWebServiceClient\build\built-jar.properties deps-jar: Updating property file: D:\SVN\EJBWebServiceClient\build\built-jar.properties wsimport-init: wsimport-client-LibraryPersistentBean: files are up to date classLoader = java.net.URLClassLoader@4ce46c SharedSecrets.getJavaNetAccess()=java.net.URLClassLoader$7@182cdac wsimport-client-generate: Compiling 1 source file to D:\SVN\EJBWebServiceClient\build\classes compile: run: learn java Learn Spring learn JSF Learn HTML Learn JBoss Learn EJB Learn Hibernate Learn IBatis Times Now learn html5 Learn images Learn Testing Forbes test1 BUILD SUCCESSFUL (total time: 1 second)
EJB - 打包应用程序
使用EJB 3.0打包应用程序的要求类似于J2EE平台的要求。EJB组件打包到模块中作为jar文件,并打包到应用程序企业归档中作为ear文件。
任何企业应用程序主要有三个组件:
jar − Java应用程序归档,包含EJB模块、EJB客户端模块和实用程序模块。
war − Web应用程序归档,包含Web模块。
ear − 企业应用程序归档,包含jar和war模块。
在NetBeans中,创建、开发、打包和部署J2EE应用程序非常容易。
在NetBeans IDE中,选择文件 > 新建项目 >。在类别下选择项目类型Java EE,项目类型为企业应用程序。单击下一步 >按钮。输入项目名称和位置。单击完成 >按钮。我们选择名称为EnterpriseApplicaton。
选择服务器和设置。保持创建EJB模块和创建Web应用程序模块选中,并使用提供的默认名称。单击完成按钮。NetBeans将在项目窗口中创建以下结构。
在项目资源管理器中,右键单击项目企业应用程序,然后选择构建。
ant -f D:\\SVN\\EnterpriseApplication dist pre-init: init-private: init-userdir: init-user: init-project: do-init: post-init: init-check: init: deps-jar: deps-j2ee-archive: EnterpriseApplication-ejb.init: EnterpriseApplication-ejb.deps-jar: EnterpriseApplication-ejb.compile: EnterpriseApplication-ejb.library-inclusion-in-manifest: Building jar: D:\SVN\EnterpriseApplication\EnterpriseApplication-ejb\dist\EnterpriseApplication-ejb.jar EnterpriseApplication-ejb.dist-ear: EnterpriseApplication-war.init: EnterpriseApplication-war.deps-module-jar: EnterpriseApplication-war.deps-ear-jar: EnterpriseApplication-ejb.init: EnterpriseApplication-ejb.deps-jar: EnterpriseApplication-ejb.compile: EnterpriseApplication-ejb.library-inclusion-in-manifest: EnterpriseApplication-ejb.dist-ear: EnterpriseApplication-war.deps-jar: EnterpriseApplication-war.library-inclusion-in-archive: EnterpriseApplication-war.library-inclusion-in-manifest: EnterpriseApplication-war.compile: EnterpriseApplication-war.compile-jsps: EnterpriseApplication-war.do-ear-dist: Building jar: D:\SVN\EnterpriseApplication\EnterpriseApplication-war\dist\EnterpriseApplication-war.war EnterpriseApplication-war.dist-ear: pre-pre-compile: pre-compile: Copying 1 file to D:\SVN\EnterpriseApplication\build Copying 1 file to D:\SVN\EnterpriseApplication\build do-compile: post-compile: compile: pre-dist: do-dist-without-manifest: do-dist-with-manifest: Building jar: D:\SVN\EnterpriseApplication\dist\EnterpriseApplication.ear post-dist: dist: BUILD SUCCESSFUL (total time: 1 second)
在这里您可以看到,Netbeans首先准备Jar,然后是War,最后是包含Jar和War文件的ear文件。每个jar、war和ear文件都包含一个meta-inf文件夹,以根据J2EE规范存储元数据。