Ejb 简明教程
EJB - Overview
EJB 表示 Enterprise Java Beans 。EJB 是 J2EE 平台的重要组成部分。J2EE 平台具有基于组件的架构,为企业级应用程序提供多层、分布和高度事务的功能。
EJB 提供了一种架构来开发和部署基于组件的企业应用程序,同时考虑健壮性、高可扩展性和高性能。EJB 应用程序可以部署在符合 J2EE 1.3 标准规范的任何应用程序服务器上。
我们将在本教程中详细讨论 EJB 3.0。
Types
EJB 主要分为三类;下表列出了其中的名称及简要说明:
S.No |
Type & Description |
1 |
Session Bean 会话 Bean 为单个会话存储特定用户的数据。它可以是 stateful 或 stateless 。与实体 Bean 相比,它使用较少的资源。当用户会话终止时,会话 Bean 即被销毁。 |
2 |
Entity Bean * Entity beans* 表示持久性数据存储。可通过实体 Bean 将用户数据保存到数据库中,之后可以从实体 Bean 中的数据库中检索数据。 |
3 |
Message Driven Bean * Message driven beans* 用在 JMS(Java 消息服务)的上下文中。消息驱动 Bean 可以从外部实体消费 JMS 消息并根据情况采取相应操作。 |
EJB - Environment Setup
EJB 是一个适用于 Java 的框架,所以最重要的要求是,在您的计算机中安装一个 *J*ava *D*evelopment *K*it (JDK)。
System Requirement
JDK |
1.5 or above. |
Memory |
no minimum requirement. |
Disk Space |
no minimum requirement. |
Operating System |
no minimum requirement. |
Step 1 - Verify Java Installation in Your System
现在,打开控制台,执行以下 java 命令。
OS |
Task |
Command |
Windows |
Open Command Console |
c:> java -version |
Linux |
Open Command Terminal |
$ java -version |
Mac |
Open Terminal |
machine:~ joseph$ java -version |
让我们验证所有操作系统的输出 −
OS |
Output |
Windows |
java 版本 “1.6.0_21”Java™ SE 运行时环境(构建 1.6.0_21-b11)Java HotSpot™ 64 位服务器虚拟机(构建 23.21-b01,混合模式) |
Linux |
java 版本 “1.6.0_21”Java™ SE 运行时环境(构建 1.6.0_21-b11)Java HotSpot™ 64 位服务器虚拟机(构建 23.21-b01,混合模式) |
Mac |
java 版本 “1.6.0_21”Java™ SE 运行时环境(构建 1.6.0_21-b11)Java HotSpot™ 64 位服务器虚拟机(构建 23.21-b01,混合模式) |
如果您没有安装 Java,请从 www.oracle.com 中安装 Java 软件开发包 (SDK),我们假设 Java 1.6.0_21 是针对本教程安装的版本。
Step 2 – Set JAVA Environment
设置 JAVA_HOME 环境变量,以指定 Java 安装在系统中的基本目录位置。例如,
OS |
Output |
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 编译器位置追加到系统路径中。
OS |
Output |
Windows |
将字符串 ;C:\Program Files\Java\jdk1.6.0_21\bin 追加到系统变量 Path 的末尾。 |
Linux |
export PATH=$PATH:$JAVA_HOME/bin/ |
Mac |
not required |
使用上面解释的 java -version 命令验证 Java 安装。
Step 3 – Download and Install NetBeans IDE
从 netbeans.org 下载 NetBeans IDE 的最新版本。在撰写本教程时,我下载了 NetBeans 7.3,它使用 www.oracle.com 链接,并捆绑了 JDK 1.7。
OS |
Installer name |
Windows |
Netbeans 7.3 |
Linux |
Netbeans 7.3 |
Mac |
Netbeans 7.3 |
Step 4 – Setup JBoss Application Server
您可以从 www.jboss.org 下载 JBoss 服务器的最新版本。根据平台下载该软件包。将 Jboss 提取到机器上的任何位置。
OS |
File name |
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 |
Step 5 - Configure JEE Plugins to Netbeans
使用“工具”>“插件”,打开插件窗口。打开“可用插件”选项卡,然后在“Java Web and EE”类别下选择“Java EE Base”和“EJB and EAR”。单击安装按钮,NetBeans 会下载并安装相应插件。使用“已安装”选项卡,验证插件安装(如下面给出的图片所示)。
Step 6 - Configure JBoss Server in Netbeans
转到“服务”选项卡,然后右键单击服务器以添加新服务器。
添加服务器实例向导将打开。选择 JBoss,然后在下一步中输入相关详细信息,以便在 NetBeans 中配置服务器。
一旦一切都配置好,您将看到以下屏幕。
Step 7 - Install Database Server (PostGreSql)
从 www.postgresql.org 下载最新版本的 PostGreSql 数据库服务器。在撰写本教程时,我下载了 PostGreSql 9.2
OS |
Installer name |
Windows |
PostGreSql 9.2 |
Linux |
PostGreSql 9.2 |
Mac |
PostGreSql 9.2 |
EJB - Create Application
要创建一个简单的 EJB 模块,我们将使用 NetBeans,“新建项目”向导。在下面给出的示例中,我们将创建一个名为 Component 的 EJB 模块项目。
Create Project
在 NetBeans IDE 中,选择 File > New Project > 。您将看到以下屏幕。
在类别 Java EE 下选择项目类型,项目类型为 EJB Module 。单击 Next > 按钮。您将看到以下屏幕。
输入项目名称和位置。单击 Next > 按钮。您将看到以下屏幕。
将服务器选择为 JBoss Application Server 。单击 Finish 按钮。您将看到 NetBeans 创建的以下项目。
Create a Sample EJB
要创建一个简单的 EJB,我们将使用 NetBeans“新建”向导。在下面给出的示例中,我们将创建一个无状态 EJB 类 librarySessionBean,位于 EjbComponent 项目中。
选择项目资源管理器窗口中的项目 EjbComponent,并右键单击它。选择, New > Session Bean 。您将看到 New Session Bean 向导。
输入会话 bean 名称和包名称。单击 Finish 按钮。您将看到 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;
}
}
Build the Project
-
在项目资源管理器窗口中选择 EjbComponent 项目。
-
右键单击它以打开上下文菜单。
-
Select clean and build.
您将在 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)
Start the Application Server
-
在服务窗口的服务器下选择 JBoss 应用程序服务器。
-
右键单击它以打开上下文菜单。
-
Select start.
您将在 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
Deploy the Project
-
在项目资源管理器窗口中选择 EjbComponent 项目。
-
右键单击它以打开上下文菜单。
-
Select Deploy.
您将在 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 Application Server Log Output
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
Create Client to Access EJB
-
在 NetBeans IDE 中,选择 File > New Project > 。
-
在类别 Java 下选择项目类型,项目类型为 Java Application 。单击下一步 > 按钮。
-
输入项目名称和位置。单击 Finish > 按钮。我们选择 EjbTester 作为名称。
-
在项目资源管理器窗口中右键单击项目名称。选择 properties 。
-
在 compile 选项卡中使用 Add Project 按钮,添加之前创建的 EJB 组件项目到库下。
-
在 compile 选项卡中使用 Add jar/folder 按钮添加 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());
}
}
}
}
Run Client to Access EJB
在项目浏览器中找到EJBTester.java。右键单击EJBTester类并选择 run file 。
在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 - Stateless Bean
无状态会话 Bean 是一种企业的 Bean,它通常用于执行独立操作。一个无状态会话 Bean 根据其名称不会有任何关联的客户端状态,但它可能会保留其实例状态。EJB 容器通常会创建几个无状态 Bean 对象的池,并使用这些对象来处理客户端请求。由于池的原因,实例变量值无法保证在查找/方法调用中相同。
Steps to Create a Stateless EJB
以下是创建无状态 EJB 所需的步骤 −
-
创建一个公开业务方法的远程/本地接口。
-
该接口将由 EJB 客户端应用程序使用。
-
如果 EJB 客户端处于与要部署 EJB 会话 Bean 的相同环境中,则使用 @Local 注解。
-
如果 EJB 客户端处于与要部署 EJB 会话 Bean 的不同环境中,则使用 @Remote 注解。
-
创建一个无状态会话 Bean,实现上述接口。
-
使用 @Stateless 注解标明它为无状态 Bean。EJB 容器会自动创建此注解要求的相关配置或界面,以便在部署期间对其进行读取。
Example Application
让我们创建一个测试 EJB 应用程序来测试无状态 EJB。
Step |
Description |
1 |
按照 EJB - 创建应用程序章节中所述,在 com.tutorialspoint.stateless 包下创建一个名为 EjbComponent 的项目。您还可以使用 EJB - 创建应用程序章节中创建的项目,以便理解无状态 EJB 概念。 |
2 |
如在 EJB - 创建应用程序章节中所述,创建 LibrarySessionBean.java 和 LibrarySessionBeanRemote。保持其他文件不变。 |
3 |
清理并构建应用程序以确保业务逻辑按需求工作。 |
4 |
最后,以 jar 文件的形式将应用程序部署在 JBoss 应用程序服务器上。如果 JBoss 应用程序服务器尚未启动,它会自动启动。 |
5 |
现在创建 EJB 客户端,这是一个控制台应用程序,创建方法与 EJB - 章节中主题 Create Client to access EJB 下所述相同。 |
EJBComponent (EJB Module)
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 Application Server Log Output
...
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 Client)
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)并列出书籍。
Run Client to Access EJB
在项目浏览器中找到EJBTester.java。右键单击EJBTester类并选择 run file 。
在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)
Run Client Again to Access EJB
在项目浏览器中找到EJBTester.java。右键单击EJBTester类并选择 run file 。
在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 - Stateful Bean
有状态会话 bean 是企业 bean 的一种类型,它与客户端保持会话状态。有状态会话 bean 根据其名称将关联客户端状态保留在其实例变量中。EJB 容器创建独立的有状态会话 bean 来处理客户端的每个请求。请求范围结束时,有状态会话 bean 将被销毁。
Steps to Create Stateful EJB
以下是有状态 EJB 的创建步骤:
-
创建一个公开业务方法的远程/本地接口。
-
该接口将由 EJB 客户端应用程序使用。
-
如果 EJB 客户端与 EJB 会话 bean 需要部署的 EJB 客户端处于同一环境,则使用 @Local 注释。
-
如果 EJB 客户端与 EJB 会话 bean 需要部署的 EJB 客户端属于不同的环境,则使用 @Remote 注释。
-
创建实现上述接口的有状态会话 bean。
-
使用 @Stateful 注释表示有状态 bean。EJB 容器在部署期间通过读取此注释自动创建所需的配置或接口。
Example Application
让我们创建一个测试 EJB 应用程序来测试有状态 EJB。
Step |
Description |
1 |
在包 com.tutorialspoint.stateful 下,创建一个名为 EjbComponent 的项目,如 EJB 中所述 - 创建应用程序章节。您还可以使用在 EJB - 创建应用程序章节中创建的项目来了解有状态 EJB 概念。 |
2 |
创建 LibraryStatefulSessionBean.java 和 LibraryStatefulSessionBeanRemote,如 EJB 中所述 - 创建应用程序章节。保持其他文件不变。 |
3 |
清理并构建应用程序以确保业务逻辑按需求工作。 |
4 |
最后,以 jar 文件的形式将应用程序部署在 JBoss 应用程序服务器上。如果 JBoss 应用程序服务器尚未启动,它会自动启动。 |
5 |
现在创建 EJB 客户端,这是一个控制台应用程序,创建方法与 EJB - 章节中主题 Create Client to access EJB 下所述相同。 |
EJBComponent (EJB Module)
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 Application Server Log Output
...
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 Client)
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 查找,以获取远程业务对象(状态会话 bean)。
-
然后向用户显示一个图书库用户界面,并询问他/她输入一个选择。
-
如果用户输入 1,系统询问书籍名称并使用状态会话 bean addBook() 方法保存书籍。会话 bean 正在其实例变量中存储书籍。
-
如果用户输入 2,系统使用状态会话 bean getBooks() 方法检索书籍并退出。
-
然后再次使用名称“LibraryStatefulSessionBean/remote”执行另一个 jndi 查找,以获取远程业务对象(状态 EJB)和书籍列表。
Run Client to Access EJB
在项目浏览器中找到EJBTester.java。右键单击EJBTester类并选择 run file 。
在 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)
Run Client Again to Access EJB
在项目浏览器中找到EJBTester.java。右键单击EJBTester类并选择 run file 。
在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 实例。
-
Stateful EJB 对象仅为单个会话保留值。正如在第二次运行中,我们没有获得任何书籍值。
EJB - Persistence
在 EJB 2.0 中使用的 EJB 3.0 实体 bean 在很大程度上已被持久性机制取代。现在实体 bean 是一个简单的 POJO,具有与表的映射。
以下是持久性 API 中的关键参与者 −
-
Entity − 表示数据存储记录的持久对象。它是可序列化的。
-
EntityManager − 持久性接口,用于在持久对象(实体)上执行诸如添加/删除/更新/查找的数据操作。它还有助于使用 Query 接口执行查询。
.
-
Persistence unit (persistence.xml) − 持久性单元描述持久性机制的属性。
.
-
Data Source (*ds.xml) − 数据源描述与数据存储相关的属性,例如连接 URL。用户名、密码等。
要演示 EJB 持久性机制,我们需要执行以下任务 −
-
Step 1 − 在数据库中创建表。
-
Step 2 − 创建与表对应的实体类。
-
Step 3 − 创建数据源和持久性单元。
-
Step 4 − 创建具有 EntityManager 实例的无状态 EJB。
-
Step 5 − 更新无状态 EJB。添加方法通过实体管理器向数据库添加记录和从数据库获取记录。
-
Step 6 − 一个基于控制台的应用程序客户端将访问无状态 EJB,以将数据保留到数据库中。
Create Table
在默认数据库 postgres 中创建一个表 books 。
CREATE TABLE books (
id integer PRIMARY KEY,
name varchar(50)
);
Create Entity class
//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;
}
...
}
Create DataSource and Persistence Unit
DataSource (jboss-ds.xml)
<?xml version = "1.0" encoding = "UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>PostgresDS</jndi-name>
<connection-url>jdbc:postgresql://localhost: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 Unit (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>
Create Stateless EJB Having EntityManager Instance
@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,我们将在下一节中创建。
Example Application
让我们创建一个测试 EJB 应用程序,以测试 EJB 持久机制。
Step |
Description |
1 |
正如 EJB- 创建应用程序章节中说明的那样,使用包 com.tutorialspoint.entity 下的名为 EjbComponent 的项目, 创建一个项目。还可以使用在 EJB- 创建应用程序章节中创建的项目,以便本章理解 EJB 持久化概念。 |
2 |
在包 com.tutorialspoint.entity 中创建 Book.java,然后按照如下所示修改。 |
3 |
正如在 EJB- 创建应用程序章节中说明的那样,创建 LibraryPersistentBean.java 和 LibraryPersistentBeanRemote,并对其进行修改如下所示。 |
4 |
在*EjbComponent > 安装*文件夹中创建 jboss-ds.xml,在*EjbComponent > src > conf*文件夹中创建 persistence.xml。可以从 Netbeans 的文件选项卡中看到这些文件夹。对这些文件进行修改,如上所示。 |
5 |
清理并构建应用程序以确保业务逻辑按需求工作。 |
6 |
最后,以 jar 文件的形式将应用程序部署在 JBoss 应用程序服务器上。如果 JBoss 应用程序服务器尚未启动,它会自动启动。 |
7 |
现在按照 EJB - 创建应用程序章节的主题 Create Client to access EJB 中的说明,以相同的方式创建 EJB 客户端(控制台应用程序)。按照如下所示修改。 |
EJBComponent (EJB Module)
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 Application Server Log Output
...
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 Client)
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 查找,以获取远程业务对象(状态会话 bean)。
-
然后向用户显示库存储用户界面,并要求其输入选择。
-
如果用户输入 1,系统会要求输入书名,并使用无状态会话 bean addBook() 方法保存这本书。会话 Bean 正在通过 EntityManager 调用在数据库中保留这本书。
-
如果用户输入 2,系统使用状态会话 bean getBooks() 方法检索书籍并退出。
-
然后使用名称为“LibraryStatelessSessionBean/remote”执行另一个 jndi 查找,以再次获取远程业务对象(无状态 EJB),并列出书目。
Run Client to Access EJB
在项目浏览器中找到EJBTester.java。右键单击EJBTester类并选择 run file 。
在 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)
Run Client Again to Access EJB
在访问 EJB 之前重新启动 JBoss。
在项目浏览器中找到EJBTester.java。右键单击EJBTester类并选择 run file 。
在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 - Message Driven Beans
消息驱动 bean 是一种企业 bean,当它从队列或主题收到消息时,由 EJB 容器调用。消息驱动 bean 是一个无状态 bean,用于异步执行任务。
为了演示如何使用消息驱动 bean,我们将利用 EJB 持久性章节,我们需要执行以下任务-
-
Step 1 − 在数据库中创建表(参阅 EJB 持久性章节)。
-
Step 2 − 创建与表对应的实体类(参阅 EJB 持久性章节)。
-
Step 3 − 创建数据源和持久单元(参阅 EJB 持久性章节)。
-
Step 4 − 创建一个拥有 EntityManager 实例的无状态 EJB(参阅 EJB 持久性章节)。
-
Step 5 − 更新无状态 ejb.Add 方法,以便通过实体管理器添加记录并从数据库中获取记录(参阅 EJB 持久性章节)。
-
Step 6 − 在 JBoss default 应用程序目录中创建一个名为 BookQueue 的队列。
-
Step 7 − 基于控制台的应用程序客户端将向此队列发送消息。
-
Step 8 − 创建消息驱动的 Bean,它将使用无状态 Bean 来使客户端数据持久化。
-
Step 9 − jboss 的 EJB 容器将调用上述消息驱动的 Bean,并将客户端将发送到的消息传递给它。
Create Queue
如果不位于 *<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
...
Create Message Driven 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 作为资源。
-
无状态 Bean LibraryPersistentBeanRemote 为了持久化目的被注入此 bean。
构建 EjbComponent 项目,并将其部署在 JBoss 上。在构建和部署 EJB 模块后,我们需要一个客户端来向 jboss 队列发送消息。
Example Application
让我们创建一个测试 EJB 应用程序来测试消息驱动的 Bean。
Step |
Description |
1 |
正如 EJB- 创建应用程序章节中说明的那样,使用包 com.tutorialspoint.entity 下的名为 EjbComponent 的项目, 创建一个项目。还可以使用在 EJB- 创建应用程序章节中创建的项目,以便本章理解 EJB 持久化概念。 |
2 |
在 EJB-Persistence 章节中创建的 com.tutorialspoint.entity 包下创建 Book.java。 |
3 |
在 EJB-Persistence 章节中创建的 com.tutorialspoint.entity 包下创建 LibraryPersistentBean.java 和 LibraryPersistentBeanRemote。 |
4 |
在 EJB-Persistence 章节中创建的 *EjbComponent > setup * 文件夹中创建 jboss-ds.xml,在 *EjbComponent > src > conf * 文件夹中创建 persistence.xml。这些文件夹可以在 Netbeans 的文件选项卡中看到。 |
5 |
在 com.tutorialspoint.messagebean 包下创建 LibraryMessageBean.java,并按照如下所示进行修改。 |
6 |
如上所述,在 Jboss 中创建 BookQueue 队列。 |
7 |
清理并构建应用程序以确保业务逻辑按需求工作。 |
8 |
最后,以 jar 文件的形式将应用程序部署在 JBoss 应用程序服务器上。如果 JBoss 应用程序服务器尚未启动,它会自动启动。 |
9 |
现在按照 EJB - 创建应用程序章节的主题 Create Client to access EJB 中的说明,以相同的方式创建 EJB 客户端(控制台应用程序)。按照如下所示修改。 |
EJBComponent (EJB Module)
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 Client)
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() 方法中,jndi 查找使用名称 - "/queue/BookQueue" 来获得 Jboss 中可用的队列的 treference。然后使用队列会话创建发送者。
-
然后向用户显示库存储用户界面,并要求其输入选择。
-
如果用户输入 1,系统会要求输入书名,并将书名发送到队列。当 JBoss 容器接收到队列中的此消息时,它会调用我们消息驱动的 bean 的 onMessage 方法。我们的消息驱动 bean 然后使用有状态会话 bean addBook() 方法保存图书。会话 Bean 通过 EntityManager 调用将图书持久保存在数据库中。
-
如果用户输入 2,那么会使用名称 - "LibraryStatefulSessionBean/remote" 再进行另一个 jndi 查找,以再次获取远程业务对象(有状态 EJB)并完成图书的列出。
Run Client to Access EJB
在项目浏览器中找到EJBTester.java。右键单击EJBTester类并选择 run file 。
在 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 - Annotations
注释是在 Java 5.0 中引入的。具有注释的目的是在类的源代码中附加到类或类的元数据的其他信息。在 EJB 3.0 中,注释用于描述 EJB 类中的配置元数据。通过这种方式,EJB 3.0 消除了在配置 XML 文件中描述配置数据的需要。
EJB 容器使用编译器工具通过读取这些注释来生成所需的工件,如接口、部署描述符。以下是常用注释的列表。
Sr.no |
Name |
Description |
1 |
javax.ejb.Stateless |
指定给定的 EJB 类是有状态会话 bean。 Attributes name − 用于指定会话 bean 的名称。 mappedName − 用于指定会话 bean 的 JNDI 名称。 description − 用于提供会话 bean 的说明。 |
2 |
javax.ejb.Stateful |
指定给定的 EJB 类是有状态会话 bean。 Attributes name − 用于指定会话 bean 的名称。 mappedName − 用于指定会话 bean 的 JNDI 名称。 description − 用于提供会话 bean 的说明。 |
3 |
javax.ejb.MessageDrivenBean |
指定给定的 EJB 类是消息驱动 bean。 Attributes name − 用于指定消息驱动 bean 的名称。 messageListenerInterface − 用于指定消息驱动 bean 的消息侦听器接口。 activationConfig − 用于在消息驱动 bean 的操作环境中指定消息驱动 bean 的配置详细信息。 mappedName − 用于指定会话 bean 的 JNDI 名称。 description − 用于提供会话 bean 的说明。 |
4 |
javax.ejb.EJB |
用于将依赖项作为 EJB 实例指定或注入到另一个 EJB 中。 Attributes name − 用于指定名称,该名称将用于在环境中查找引用的 bean。 beanInterface − 用于指定引用的 bean 的接口类型。 beanName − 用于提供引用的 bean 的名称。 mappedName − 用于指定引用的 bean 的 JNDI 名称。 description − 用于提供引用的 bean 的说明。 |
5 |
javax.ejb.Local |
用于指定会话 bean 的本地接口。该本地接口说明会话 bean(可以是无状态或有状态)的业务方法。此接口用于将业务方法公开放给本地客户端,这些客户端与 EJB 在同一部署/应用程序中运行。 Attributes value − 用于指定本地接口列表作为接口数组。 |
6 |
javax.ejb.Remote |
用于指定会话 bean 的远程接口。该远程接口说明会话 bean(可以是无状态或有状态)的业务方法。此接口用于将业务方法公开放给远程客户端,这些客户端与 EJB 在不同的部署/应用程序中运行。 Attributes value − 用于指定远程接口列表作为接口数组。 |
7 |
javax.ejb.Activation ConfigProperty |
用于指定消息驱动 bean 所需的属性。例如,端点、目标、消息选择器等。此注释作为 javax.ejb.MessageDrivenBean 注释的 activationConfig 属性的参数传递。 Attributes *propertyName * − 属性的名称。*propertyValue * − 属性的值。 |
8 |
javax.ejb.PostActivate |
用于指定 EJB 生命周期的回调方法。当 EJB 容器刚刚激活/重新激活 bean 实例时,将调用此方法。此接口用于将业务方法公开放给本地客户端,这些客户端与 EJB 在同一部署/应用程序中运行。 |
EJB - Callbacks
回调是一种可以拦截企业 bean 生命周期的方法。EJB 3.0 规范指定了回调处理程序方法将被创建的回调。EJB 容器调用这些回调。我们可以在 EJB 类本身或单独的类中定义回调方法。EJB 3.0 为回调提供了许多注释。
以下是无状态 bean 的回调注释列表 −
Annotation |
Description |
@PostConstruct |
当首次创建 bean 时调用。 |
@PreDestroy |
当 bean 从 bean 池中移除或被销毁时调用。 |
以下是状态 bean 的回调注释列表 −
Annotation |
Description |
@PostConstruct |
当首次创建 bean 时调用。 |
@PreDestroy |
当 bean 从 bean 池中移除或被销毁时调用。 |
@PostActivate |
在加载 bean 以便使用时调用。 |
@PrePassivate |
在将 bean 放回 Bean 池时调用。 |
以下为消息驱动 Bean 的回调注释列表−
Annotation |
Description |
@PostConstruct |
当首次创建 bean 时调用。 |
@PreDestroy |
当 bean 从 bean 池中移除或被销毁时调用。 |
以下为实体 Bean 的回调注释列表−
Annotation |
Description |
@PrePersist |
在数据库中创建实体时调用。 |
@PostPersist |
在数据库中创建实体后调用。 |
@PreRemove |
从数据库中删除实体时调用。 |
@PostRemove |
从数据库中删除实体后调用。 |
@PreUpdate |
在数据库中更新实体之前调用。 |
@PostLoad |
从数据库中获取记录并加载到实体时调用。 |
Example Application
我们创建一个测试 EJB 应用程序来测试 EJB 中的各种回调。
Step |
Description |
1 |
根据 EJB 中的说明,使用名称为 EjbComponent 创建一个项目,包为 com.tutorialspoint.stateless - 创建应用程序章节。你还可以使用在此章节中创建的 EJB - 持久性章节中的项目,以将各种回调添加到 EJB。 |
2 |
如在 EJB - 创建应用程序章节中所述,创建 LibrarySessionBean.java 和 LibrarySessionBeanRemote。保持其他文件不变。 |
3 |
使用在 EJB - 持久性章节中创建的 Bean。按如下所示添加回调方法。保持其他文件不变。 |
4 |
在包 com.tutorialspoint.callback 下创建 java 类 BookCallbackListener。此类将演示回调方法的划分。 |
5 |
清理并构建应用程序以确保业务逻辑按需求工作。 |
6 |
最后,以 jar 文件的形式将应用程序部署在 JBoss 应用程序服务器上。如果 JBoss 应用程序服务器尚未启动,它会自动启动。 |
7 |
现在创建 EJB 客户端,这是一个控制台应用程序,创建方法与 EJB - 章节中主题 Create Client to access EJB 下所述相同。 |
EJBComponent (EJB Module)
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 Application Server Log Output
...
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 Client)
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() 方法检索书籍并退出。
Run Client to Access EJB
在项目浏览器中找到EJBTester.java。右键单击EJBTester类并选择 run file 。
在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 Application Server Log Output
你可以在 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 - Timer Service
计时器服务是一种机制,可以使用它构建计划的应用程序。例如,每月 1 号生成工资单。EJB 3.0 规范指定了 @Timeout 注释,此注释有助于在无状态或消息驱动 Bean 中对 EJB 服务进行编程。EJB 容器调用由 @Timeout 注释的方法。
EJB Timer Service 是由 EJB 容器提供的服务,此服务有助于创建计时器并在计时器到期时计划回调。
Steps to Create Timer
在 Bean 中使用 @Resource 注解注入 SessionContext −
@Stateless
public class TimerSessionBean {
@Resource
private SessionContext context;
...
}
使用 SessionContext 对象获取 TimerService 并创建定时器。以毫秒为单位传递时间和消息。
public void createTimer(long duration) {
context.getTimerService().createTimer(duration, "Hello World!");
}
Steps to Use Timer
对方法使用 @Timeout 注解。返回类型应为 void 并传递 Timer 类型的一个参数。我们在第一次执行后取消了定时器,否则它将在固定间隔后持续运行。
@Timeout
public void timeOutHandler(Timer timer) {
System.out.println("timeoutHandler : " + timer.getInfo());
timer.cancel();
}
Example Application
让我们创建一个测试 EJB 应用程序来测试 EJB 中的定时器服务。
Step |
Description |
1 |
按照 EJB - 创建应用程序章节中的说明,在包 com.tutorialspoint.timer 中创建一个名为 EjbComponent 的项目。 |
2 |
按照 EJB - 创建应用程序章节中的说明,创建 TimerSessionBean.java 和 TimerSessionBeanRemote。保持其他文件不变。 |
3 |
清理并构建应用程序以确保业务逻辑按需求工作。 |
4 |
最后,以 jar 文件的形式将应用程序部署在 JBoss 应用程序服务器上。如果 JBoss 应用程序服务器尚未启动,它会自动启动。 |
5 |
现在创建 EJB 客户端,这是一个控制台应用程序,创建方法与 EJB - 章节中主题 Create Client to access EJB 下所述相同。 |
EJBComponent (EJB Module)
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 Application Server Log Output
...
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 Client)
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 - Dependency Injection
EJB 3.0 规范提供了注解,可以应用于字段或 setter 方法来注入依赖项。EJB 容器使用全局 JNDI 注册表来定位依赖项。EJB 3.0 中用于依赖项注入的注解如下。
-
@EJB − 用于注入其他 EJB 引用。
-
@Resource − 用于注入数据源或单例服务,如 sessionContext、timerService 等。
Steps to Use @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;
}
...
}
Steps to use @Resource
@Resource 通常用于注入 EJB 容器提供的单例。
public class LibraryMessageBean implements MessageListener {
@Resource
private MessageDrivenContext mdctx;
...
}
Example Application
让我们创建一个测试 EJB 应用程序来测试 EJB 中的依赖项注入服务。
Step |
Description |
1 |
按照 EJB - 创建应用程序章节中的说明,在包 com.tutorialspoint.timer 中创建一个名为 EjbComponent 的项目。 |
2 |
使用在 EJB - 消息驱动 Bean 章节中创建的 Bean。保持其他文件不变。 |
3 |
清理并构建应用程序以确保业务逻辑按需求工作。 |
4 |
最后,以 jar 文件的形式将应用程序部署在 JBoss 应用程序服务器上。如果 JBoss 应用程序服务器尚未启动,它会自动启动。 |
5 |
现在创建 EJB 客户端,这是一个控制台应用程序,创建方法与 EJB - 章节中主题 Create Client to access EJB 下所述相同。 |
EJBComponent (EJB Module)
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 Client)
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() 方法中,jndi 查找使用名称“/queue/BookQueue”完成,以获取 Jboss 中可用的队列引用。然后使用队列会话创建发送者。
-
然后向用户显示一个图书库用户界面,并询问他/她输入一个选择。
-
如果用户输入 1,系统会询问书名,然后发送者将书名发送到队列。当 JBoss 容器在队列中收到此消息时,它将调用我们的消息驱动的 Bean 的 onMessage 方法。然后我们的消息驱动的 Bean 使用有状态会话 Bean 的 addBook() 方法保存图书。会话 Bean 通过 EntityManager 调用将图书保存在数据库中。
-
如果用户输入 2,则使用名称“LibraryStatefulSessionBean/remote”执行另一个 jndi 查找,以再次获取远程业务对象(有状态 EJB)并列出图书。
Run Client to Access EJB
在项目浏览器中找到EJBTester.java。右键单击EJBTester类并选择 run file 。
在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 使用使用 @EJB 注解注入的 LibraryPersistentBean,并且在异常情况下,使用 MessageDrivenContext 对象回滚事务。
EJB - Interceptors
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();
}
拦截器方法可以应用或绑定在三个级别。
-
Default -部署中每个bean将调用默认的拦截器。默认的拦截器只能通过xml(ejb-jar.xml)来应用。
-
Class -类级别的拦截器将在bean的每个方法中调用。类级别的拦截器可以通过注解或通过xml(ejb-jar.xml)来应用。
-
Method -方法级别的拦截器将为bean的特定方法调用。方法级别的拦截器可以通过注解或通过xml(ejb-jar.xml)来应用。
我们在此讨论类级别的拦截器。
Interceptor Class
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();
}
}
Remote Interface
import javax.ejb.Remote;
@Remote
public interface LibraryBeanRemote {
//add business method declarations
}
Intercepted Stateless EJB
@Interceptors ({BusinessInterceptor.class})
@Stateless
public class LibraryBean implements LibraryBeanRemote {
//implement business method
}
Example Application
让我们创建一个测试EJB应用程序来测试截取的无状态EJB。
Step |
Description |
1 |
在程序包com.tutorialspoint.interceptor中使用名称EjbComponent创建一个项目,正如在EJB-创建应用程序章节中所解释的那样。您还可以使用EJB-创建应用程序章节中创建的项目,以了解拦截的EJB概念。 |
2 |
按照 EJB - 创建应用程序章节中所述,在 com.tutorialspoint.interceptor 包下创建 LibraryBean.java 和 LibraryBeanRemote。保持其他文件不变。 |
3 |
清理并构建应用程序以确保业务逻辑按需求工作。 |
4 |
最后,以 jar 文件的形式将应用程序部署在 JBoss 应用程序服务器上。如果 JBoss 应用程序服务器尚未启动,它会自动启动。 |
5 |
现在,按照 EJB - 创建应用程序章节下主题 Create Client to access EJB 中所述,创建 ejb 客户端,这是一个基于控制台的应用程序。 |
EJBComponent (EJB Module)
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 Application Server Log Output
...
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 Client)
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()方法检索图书并退出。
Run Client to Access EJB
在项目浏览器中找到EJBTester.java。右键单击EJBTester类并选择 run file 。
在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)
EJB - Embeddable Objects
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;
}
...
}
Example Application
让我们创建一个测试 EJB 应用程序来测试 EJB 3.0 中的嵌入对象。
Step |
Description |
1 |
在包 com.tutorialspoint.entity 下创建一个名为 EjbComponent 的项目,如 EJB - 章节中所述。请使用在 EJB - 持久性章节中创建的项目,以便本章可以了解 EJB 概念中的嵌入对象。 |
2 |
在包 com.tutorialspoint.entity 下创建 Publisher.java,如 EJB - 章节中所述。保持其他文件不变。 |
3 |
在包 com.tutorialspoint.entity 下创建 Book.java。使用 EJB - 持久性章节作为参考。保持其他文件不变。 |
4 |
清理并构建应用程序以确保业务逻辑按需求工作。 |
5 |
最后,以 jar 文件的形式在 JBoss 应用程序服务器上部署应用程序。如果尚未启动 JBoss 应用程序服务器,它将自动启动。 |
6 |
现在创建 EJB 客户端,这是一个控制台应用程序,创建方法与 EJB - 章节中主题 Create Client to access EJB 下所述相同。 |
EJBComponent (EJB Module)
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 Application Server Log Output
...
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 Client)
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()方法中,jndi查找与名称“LibraryPersistenceBean/remote”相对应,用于获取远程业务对象(无状态EJB)。
-
然后向用户显示一个图书库用户界面,并询问他/她输入一个选择。
-
如果用户输入1,系统将询问图书名称,并使用无状态会话bean的addBook()方法保存图书。会话bean将图书存储到数据库中。
-
如果用户输入2,系统将使用无状态会话bean的getBooks()方法检索图书并退出。
Run Client to Access EJB
在项目浏览器中找到EJBTester.java。右键单击EJBTester类并选择 run file 。
在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 - Blobs/Clobs
EJB 3.0 使用 @Lob 注解提供对 Blob 和 Clob 类型的支持。可以使用 @Lob 注解映射以下 java 类型。
-
java.sql.Blob
-
java.sql.Clob
-
byte[]
-
String
-
Serializable Object
@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;
}
...
}
Example Application
让我们创建一个测试 EJB 应用程序来测试 EJB 3.0 中的 blob/clob 支持。
Step |
Description |
1 |
在一个包为 com.tutorialspoint.entity 中的项目“EjbComponent”下创建项目,如 EJB - 创建应用程序章节中所述。请将 EJB - 持久性章节中创建的项目用作此章节的项目,以了解 ejb 概念中的 clob/blob 对象。 |
2 |
在包 com.tutorialspoint.entity 下创建 Book.java。使用 EJB - 持久性章节作为参考。保持其他文件不变。 |
3 |
清理并构建应用程序以确保业务逻辑按需求工作。 |
4 |
最后,以 jar 文件的形式在 JBoss 应用程序服务器上部署应用程序。如果尚未启动 JBoss 应用程序服务器,它将自动启动。 |
5 |
现在创建 EJB 客户端,这是一个控制台应用程序,创建方法与 EJB - 章节中主题 Create Client to access EJB 下所述相同。 |
EJBComponent (EJB Module)
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 Application Server Log Output
...
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 Client)
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() 方法中,jndi 查找使用名称“LibraryPersistenceBean/remote”完成,以获取远程业务对象(无状态 EJB)。
-
然后向用户显示一个图书库用户界面,并询问他/她输入一个选择。
-
如果用户输入 1,系统会询问书名,然后使用无状态会话 Bean addBook() 方法保存图书。会话 Bean 会将图书存储在数据库中。
-
如果用户输入2,系统将使用无状态会话bean的getBooks()方法检索图书并退出。
Run Client to Access EJB
在项目浏览器中找到EJBTester.java。右键单击EJBTester类并选择 run file 。
在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 - Transactions
事务是一个工作项的单一单元,它遵循 ACID 属性。ACID 代表原子性、一致性、隔离性和持久性。
-
Atomic - 如果任何工作项失败,则整个单元将被视为失败。成功意味着所有项目都执行成功。
-
Consistent - 事务必须使系统保持一致的状态。
-
Isolated - 每个事务独立于任何其他事务执行。
-
Durable - 如果事务已执行或提交,它应当能经受系统故障。
EJB 容器/服务器是事务服务器,并处理事务上下文传播和分布式事务。事务可以由容器管理,也可以由 Bean 代码中的自定义代码处理。
-
Container Managed Transactions - 在此类型中,容器会管理事务状态。
-
Bean Managed Transactions - 在此类型中,开发人员会管理事务状态的生命周期。
Container Managed Transactions
EJB 3.0 已指定了 EJB 容器实施的以下事务属性 −
-
REQUIRED − 表示必须在事务中执行业务方法,否则将为该方法启动新事务。
-
REQUIRES_NEW − 表示要为业务方法启动新事务。
-
SUPPORTS − 表示业务方法将作为事务的一部分执行。
-
NOT_SUPPORTED − 表示不应将业务方法作为事务的一部分执行。
-
MANDATORY − 表示业务方法将作为事务的一部分执行,否则将抛出异常。
-
NEVER − 表示如果业务方法作为事务的一部分执行,那么将抛出异常。
Example
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 Managed Transactions
在 Bean 管理的事务中,可以通过处理应用程序级别的异常来管理事务。
以下是需要考虑的关键点 −
-
Start − 何时在业务方法中启动事务。
-
Sucess − 在提交事务时识别成功场景。
-
Failed − 在回滚事务时识别失败场景。
Example
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 - Security
安全性是任何企业级应用程序的主要关注点。它包括识别访问应用程序的用户或系统。基于识别,它允许或拒绝对应用程序内资源的访问。EJB 容器管理标准安全问题,也可以对其进行定制以处理任何特定安全问题。
Important Terms of Security
-
Authentication − 这是确保验证访问系统或应用程序的用户是真实的处理。
-
Authorization − 这是确保经过验证的用户具有访问系统资源的正确权限级别的处理。
-
User − 用户表示访问应用程序的客户端或系统。
-
User Groups − 用户可以属于具有特定权限的组,例如管理员组。
-
User Roles − 角色定义用户具有的权限级别或访问系统资源的权限。
Container Managed Security
EJB 3.0 已指定 EJB 容器实现的以下安全属性/注释。
-
DeclareRoles − 指出类将接受声明的角色。注释应用于类级别。
-
RolesAllowed − 指出方法可被指定角色的用户访问。可以应用于类级别,从而所有类方法都可以由指定角色的用户访问。
-
PermitAll − 指出所有用户都可以访问业务方法。它可以应用于类以及方法级别。
-
DenyAll − 指出任何用户都无法访问在类或方法级别指定的业务方法。
Example
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
}
}
Security Configuration
将角色和用户组映射到配置文件中。
<?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 Bindings
JNDI 代表 Java 命名和目录接口。它是一组 API 和服务接口。基于 Java 的应用程序将 JNDI 用于命名和目录服务。在 EJB 的背景下,有两个术语。
-
Binding − 指为 EJB 对象分配一个名称,稍后可以使用该名称。
-
Lookup − 指查找并获取 EJB 对象。
在 Jboss 中,默认情况下,会话 bean 以以下格式绑定在 JNDI 中。
-
local − EJB-name/local
-
remote − EJB-name/remote
如果 EJB 与 <application-name>.ear 文件捆绑在一起,则默认格式如下 −
-
local − application-name/ejb-name/local
-
remote − application-name/ejb-name/remote
Example of Default Binding
请参阅 EJB - 创建应用程序章节的 JBoss 控制台输出。
JBoss Application Server Log Output
...
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
...
Customized Binding
可以使用以下注释来自定义默认 JNDI 绑定 −
-
local − org.jboss.ejb3.LocalBinding
-
remote − 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 - Entity Relationships
EJB 3.0 提供了定义数据库实体关系/映射(例如一对一、一对多、多对一和多对多关系)的选项。
以下是相关注释 −
-
One-to-One − 对象之间具有单对单关系。例如,乘客一次只能使用一张车票乘坐火车。
-
One-to-Many − 对象具有单对多关系。例如,一位父亲可以有多个孩子。
-
Many-to-One − 对象具有多对一关系。例如,多个孩子只有一个母亲。
-
Many-to-Many − 对象具有多对多关系。例如,一本书可以有多个作者,一个作者可以写多本书。
此处我们将演示 ManyToMany 映射的使用。要表示 ManyToMany 关系,需要以下三张表 −
-
Book − Book 表,有图书记录。
-
Author − Author 表,有作者记录。
-
Book_Author − Book Author 表,连接上述 Book 表和 Author 表。
Create Tables
在默认数据库 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
);
Create Entity Classes
@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;
}
...
}
Example Application
让我们创建一个测试 EJB 应用程序,以在 EJB 3.0 中测试实体关系对象。
Step |
Description |
1 |
在包 com.tutorialspoint.entity 下创建一个名为 EjbComponent 的项目,如 EJB - 章节中所述。请使用在 EJB - 持久性章节中创建的项目,以便本章可以了解 EJB 概念中的嵌入对象。 |
2 |
如 EJB - Create Application 章节所述,在包 com.tutorialspoint.entity 下创建 Author.java。保持其他文件不变。 |
3 |
在包 com.tutorialspoint.entity 下创建 Book.java。使用 EJB - 持久性章节作为参考。保持其他文件不变。 |
4 |
清理并构建应用程序以确保业务逻辑按需求工作。 |
5 |
最后,以 jar 文件的形式将应用程序部署在 JBoss 应用程序服务器上。如果 JBoss 应用程序服务器尚未启动,它会自动启动。 |
6 |
现在创建 EJB 客户端,这是一个控制台应用程序,创建方法与 EJB - 章节中主题 Create Client to access EJB 下所述相同。 |
EJBComponent (EJB Module)
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 Application Server Log Output
...
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 Client)
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() 方法中,jndi 查找使用名称“LibraryPersistenceBean/remote”完成,以获取远程业务对象(无状态 EJB)。
-
然后向用户显示一个图书库用户界面,并询问他/她输入一个选择。
-
如果用户输入 1,系统会询问书名,然后使用无状态会话 Bean addBook() 方法保存图书。会话 Bean 会将图书存储在数据库中。
-
如果用户输入2,系统将使用无状态会话bean的getBooks()方法检索图书并退出。
Run Client to Access EJB
在项目浏览器中找到EJBTester.java。右键单击EJBTester类并选择 run file 。
在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 - Access Database
在 EJB 3.0 中,持久机制用于访问数据库,其中容器管理与数据库相关的操作。开发人员可以使用 JDBC API 直接在 EJB 业务方法中访问数据库。
要演示 EJB 中的数据库访问,我们需要执行以下任务 −
-
Step 1 − 在数据库中创建一个表。
-
Step 2 − 创建一个具有业务的自无状态 EJB。
-
Step 3 − 更新无状态 EJB。通过实体管理器添加方法以添加记录并从数据库获取记录。
-
Step 4 − 基于控制台的应用程序客户端将访问无状态 EJB 以在数据库中持久保存数据。
Create Table
在默认数据库 postgres 中创建一个表 books 。
CREATE TABLE books (
id integer PRIMARY KEY,
name varchar(50)
);
Create a Model Class
public class Book implements Serializable{
private int id;
private String name;
public Book() {
}
public int getId() {
return id;
}
...
}
Create Stateless 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,我们将在下一节中创建。
Example Application
让我们创建一个测试 EJB 应用程序来测试 EJB 数据库访问机制。
Step |
Description |
1 |
按照 EJB - 创建应用程序章节中的说明,使用名称 EjbComponent 在包 com.tutorialspoint.entity 中创建一个项目。您也可以使用在 EJB - 创建应用程序章节中创建的项目,以便理解本章节中的 EJB 数据访问概念。 |
2 |
在包 com.tutorialspoint.entity 中创建 Book.java,然后按照如下所示修改。 |
3 |
按照 EJB - 创建应用程序章节中的说明创建 LibraryPersistentBean.java 和 LibraryPersistentBeanRemote,然后按照如下所示修改。 |
4 |
清理并构建应用程序以确保业务逻辑按需求工作。 |
5 |
最后,以 jar 文件的形式将应用程序部署在 JBoss 应用程序服务器上。如果 JBoss 应用程序服务器尚未启动,它会自动启动。 |
6 |
现在按照 EJB - 创建应用程序章节的主题 Create Client to access EJB 中的说明,以相同的方式创建 EJB 客户端(控制台应用程序)。按照如下所示修改。 |
EJBComponent (EJB Module)
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://localhost: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://localhost: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 Application Server Log Output
...
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 Client)
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() 方法中,JNDI 查找与名称 - "LibraryStatelessSessionBean/remote" 共同完成,以获取远程业务对象(状态 EJB)。
-
然后,向用户显示图书馆商店的用户界面,并要求他/她输入选择。
-
如果用户输入 1,系统会询问书籍名称并使用无状态会话 bean addBook() 方法保存书籍。会话 Bean 正在通过 EntityManager 调用将该书籍保存到数据库中。
-
如果用户输入2,系统将使用无状态会话bean的getBooks()方法检索图书并退出。
-
然后再次使用名称 - "LibraryStatelessSessionBean/remote" 执行另一个 JNDI 查找以获取远程业务对象(状态 EJB),并列出书籍。
Run Client to Access EJB
在项目浏览器中找到EJBTester.java。右键单击EJBTester类并选择 run file 。
在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 - Query Language
EJB Query Language 在编写自定义查询时非常方便,无需担心底层数据库详细信息。它与 HQL(Hibernate 查询语言)非常相似,通常被称为 EJBQL。
为了在 EJB 中演示 EJBQL,我们将执行以下任务 −
-
Step 1 − 在数据库中创建表。
-
Step 2 − 创建一个具有业务的自无状态 EJB。
-
Step 3 − 更新无状态 EJB。添加方法,以通过实体管理器添加记录并从数据库获取记录。
-
Step 4 − 基于控制台的应用程序客户端将访问无状态 EJB 以在数据库中持久保存数据。
Create Table
在默认数据库 postgres 中创建一个表 books 。
CREATE TABLE books (
id integer PRIMARY KEY,
name varchar(50)
);
Create a Model Class
public class Book implements Serializable{
private int id;
private String name;
public Book() {
}
public int getId() {
return id;
}
...
}
Create Stateless 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,我们将在下一节中创建。
Example Application
让我们创建一个测试 EJB 应用程序来测试 EJB 数据库访问机制。
Step |
Description |
1 |
按照 EJB - 创建应用程序章节中的说明,使用名称 EjbComponent 在包 com.tutorialspoint.entity 中创建一个项目。您也可以使用在 EJB - 创建应用程序章节中创建的项目,以便理解本章节中的 EJB 数据访问概念。 |
2 |
在包 com.tutorialspoint.entity 中创建 Book.java,然后按照如下所示修改。 |
3 |
按照 EJB - 创建应用程序章节中的说明创建 LibraryPersistentBean.java 和 LibraryPersistentBeanRemote,然后按照如下所示修改。 |
4 |
清理并构建应用程序以确保业务逻辑按需求工作。 |
5 |
最后,以 jar 文件的形式将应用程序部署在 JBoss 应用程序服务器上。如果 JBoss 应用程序服务器尚未启动,它会自动启动。 |
6 |
现在按照 EJB - 创建应用程序章节的主题 Create Client to access EJB 中的说明,以相同的方式创建 EJB 客户端(控制台应用程序)。按照如下所示修改。 |
EJBComponent (EJB Module)
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 Application Server Log Output
...
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 Client)
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),并列出图书。
Run Client to Access EJB
在项目浏览器中找到EJBTester.java。右键单击EJBTester类并选择 run file 。
在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 - Exception Handling
EJB 是企业应用程序的一部分,这些应用程序通常基于分布式环境。因此,除了可能发生的正常异常情况外,还可能存在诸如通信故障、安全权限、服务器关闭等异常情况。
EJB 容器以两种方式考虑异常 −
-
Application Exception − 如果执行业务逻辑时违反了业务规则或发生异常。
-
System Exception − 由非业务逻辑或业务代码导致的任何异常。RuntimeException、RemoteException 是 SystemException。例如,EJB 查找过程中的错误。RuntimeException、RemoteException 是 SystemException。
How Does EJB Container Handle Exceptions?
当 Application Exception 发生时,EJB 容器拦截异常,但原样将其返回给客户端。除非 EJBContext.setRollBackOnly() 方法在代码中指定,否则它不会回滚事务。EJB 容器不会在出现应用程序异常的情况下封装异常。
当 System Exception 发生时,EJB 容器会拦截异常,回滚事务并启动清除任务。它将异常打包到 RemoteException 中,并将其抛给客户端。
Handling Application Exception
应用程序异常通常在 Session EJB 方式中抛出,因为这些方式负责执行业务逻辑。应该在 business 方式的 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;
}
...
}
Handling System Exception
系统异常可以在任何时候发生,比如在命名查询失败时,或者在获取数据时发生 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 Services
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();
}
...
}
Example Application
让我们创建一个测试 EJB 应用程序来测试 EJB 3.0 中的 blob/clob 支持。
Step |
Description |
1 |
在 EJB - Create Application 章节中所解释的 com.tutorialspoint.entity 包中,使用名称 EjbComponent 创建一个项目。请使用在 EJB - Persistence 章节中创建的项目,以便本章了解 EJB 概念中的 clob/blob 对象。 |
2 |
在 com.tutorialspoint.stateless 包下创建 LibraryPersistentBean.java。使用 EJB - Persistence 章节作为参考。保持其余的文件不变。 |
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 Application Server Log Output
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
Create Client to Access EJB as Web Service
在 NetBeans IDE 中,选择 ,File > New Project > 。在Category下选择工程类型, Java ,工程类型作为 Java Application 。单击 Next > 按钮。输入工程名称和位置。单击 Finish > 按钮。我们已经选择 EJBWebServiceClient 这个名称。
在工程资源管理器窗口中右键单击工程名称。选择 New > WebService Client 。
使用 compile 选项卡中的 Add Project 按钮,在 WSDL 和客户端位置下添加先前创建的 EJB 组件项目的 LibraryPersistentBean。
单击“Finish”按钮。验证工程资源管理器中的以下结构。
Create EJBWebServiceClient.java
package ejbwebserviceclient;
public class EJBWebServiceClient {
public static void main(String[] args) {
}
}
选择 Web Service getBooks 方法,如下图所示,并将其拖拽到 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();
}
}
Run the Client
在工程资源管理器窗口中右键单击工程名称。选择 Run 。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 - Packaging Applications
使用 EJB 3.0 打包应用程序的要求类似于 J2EE 平台的要求。EJB 组件打包为 jar 文件形式的模块,并打包为 ear 文件形式的应用程序企业存档。
任何企业应用程序主要有三个组件 −
-
jar − Java Application aRchive,包含 EJB 模块、EJB 客户端模块和实用程序模块。
-
war − Web Application aRchive,包含 Web 模块。
-
ear − Enterprise Application aRchive,包含 jar 和 war 模块。
在 NetBeans 中,创建、开发、打包和部署 J2EE 应用程序非常容易。
在 NetBeans IDE 中,选择 File > New Project > 。在“Category”下选择工程类型, Java EE ,工程类型作为 Enterprise Application 。单击 Next > 按钮。输入工程名称和位置。单击 Finish > 按钮。我们已经选择 EnterpriseApplicaton 这个名称。
选择服务器和设置。保持 Create EJB Module 和 Create Web Application Module 被选中,并为其提供默认名称。单击“Finish”按钮。NetBeans 将在工程窗口中创建以下结构。
右键单击工程资源管理器中的工程 Enterprise Application ,然后选择“Build”。
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 规范提供元数据。