- EJB 教程
- EJB - 首頁
- EJB - 概述
- EJB - 環境設定
- EJB - 建立應用程式
- EJB - 無狀態 Bean
- EJB - 有狀態 Bean
- EJB - 永續性
- EJB - 訊息驅動 Bean
- EJB - 註解
- EJB - 回撥
- EJB - 定時器服務
- EJB - 依賴注入
- EJB - 攔截器
- EJB - 可嵌入物件
- EJB - Blob/Clob
- EJB - 事務
- EJB - 安全性
- EJB - JNDI 繫結
- EJB - 實體關係
- EJB - 訪問資料庫
- EJB - 查詢語言
- EJB - 異常處理
- EJB - Web 服務
- EJB - 打包應用程式
- EJB 有用資源
- EJB - 快速指南
- EJB - 有用資源
- EJB - 討論
EJB - 快速指南
EJB - 概述
EJB 代表 **企業** **Java** **Bean**。EJB 是 J2EE 平臺的重要組成部分。J2EE 平臺具有基於元件的架構,為企業級應用程式提供多層、分散式和高事務特性。
EJB 提供了一種架構來開發和部署基於元件的企業應用程式,同時考慮了健壯性、高可擴充套件性和高效能。EJB 應用程式可以部署在任何符合 J2EE 1.3 標準規範的應用程式伺服器上。
在本教程中,我們將詳細討論 EJB 3.0。
型別
EJB 主要分為三類;下表列出了它們的名字及其簡要描述:
| 序號 | 型別和描述 |
|---|---|
| 1 |
會話 Bean 會話 Bean 為特定使用者的單個會話儲存資料。它可以是 **有狀態的** 或 **無狀態的**。與實體 Bean 相比,它佔用較少的資源。會話 Bean 在使用者會話終止後會被銷燬。 |
| 2 |
實體 Bean 實體 Bean 代表持久資料儲存。使用者資料可以透過實體 Bean 儲存到資料庫,稍後可以從實體 Bean 中的資料庫中檢索。 |
| 3 |
訊息驅動 Bean 訊息驅動 Bean 用於 JMS(Java 訊息服務)的上下文中。訊息驅動 Bean 可以從外部實體消費 JMS 訊息並相應地執行操作。 |
優勢
以下是 EJB 的重要優勢:
簡化大型企業級應用程式的開發。
應用程式伺服器/EJB 容器提供大多數系統級服務,如事務處理、日誌記錄、負載平衡、永續性機制、異常處理等等。開發人員只需要關注應用程式的業務邏輯。
EJB 容器管理 EJB 例項的生命週期,因此開發人員無需擔心何時建立/刪除 EJB 物件。
EJB - 環境設定
EJB 是 Java 的框架,因此第一個要求是在您的機器上安裝 **J**ava **D**evelopment **K**it(JDK)。
系統要求
| JDK | 1.5 或更高版本。 |
|---|---|
| 記憶體 | 沒有最低要求。 |
| 磁碟空間 | 沒有最低要求。 |
| 作業系統 | 沒有最低要求。 |
步驟 1 - 驗證系統中 Java 的安裝
現在開啟控制檯並執行以下 **java** 命令。
| 作業系統 | 任務 | 命令 |
|---|---|---|
| Windows | 開啟命令控制檯 | c:\> java -version |
| Linux | 開啟命令終端 | $ java -version |
| Mac | 開啟終端 | machine:~ joseph$ java -version |
讓我們驗證所有作業系統的輸出:
| 作業系統 | 輸出 |
|---|---|
| Windows |
java version "1.6.0_21" Java(TM) SE Runtime Environment (build 1.6.0_21-b11) Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode) |
| Linux |
java version "1.6.0_21" Java(TM) SE Runtime Environment (build 1.6.0_21-b11) Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode) |
| Mac | java version "1.6.0_21" Java(TM) SE Runtime Environment (build 1.6.0_21-b11) Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode) |
如果您沒有安裝 Java,請從 www.oracle.com 安裝 Java 軟體開發工具包 (SDK)。在本教程中,我們假設 Java 1.6.0_21 為已安裝版本。
步驟 2 – 設定 JAVA 環境
設定 **JAVA_HOME** 環境變數以指向 Java 在系統中安裝的基本目錄位置。例如,
| 作業系統 | 輸出 |
|---|---|
| Windows | 將環境變數 JAVA_HOME 設定為 C:\Program Files\Java\jdk1.6.0_21 |
| Linux | export JAVA_HOME=/usr/local/java-current |
| Mac | export JAVA_HOME=/Library/Java/Home |
將 Java 編譯器位置新增到系統路徑。
| 作業系統 | 輸出 |
|---|---|
| Windows | 將字串 ;C:\Program Files\Java\jdk1.6.0_21\bin 附加到系統變數 Path 的末尾。 |
| Linux | export PATH=$PATH:$JAVA_HOME/bin/ |
| Mac | 不需要 |
使用上面解釋的 **java -version** 命令驗證 Java 安裝。
步驟 3 – 下載並安裝 NetBeans IDE
從 netbeans.org 下載最新版本的 NetBeans IDE。在撰寫本教程時,我下載了Netbeans 7.3,它使用以下連結捆綁了 JDK 1.7 www.oracle.com
| 作業系統 | 安裝程式名稱 |
|---|---|
| Windows | Netbeans 7.3 |
| Linux | Netbeans 7.3 |
| Mac | Netbeans 7.3 |
步驟 4 – 設定 JBoss 應用程式伺服器
您可以從 www.jboss.org 下載最新版本的 JBoss 伺服器。根據平臺下載存檔。將 Jboss 解壓縮到機器上的任何位置。
| 作業系統 | 檔名 |
|---|---|
| Windows | jboss-5.1.0.GA-jdk6.zip |
| Linux | jboss-5.1.0.GA-src.tar.gz |
| Mac | jboss-5.1.0.GA-src.tar.gz |
步驟 5 - 將 JEE 外掛配置到 Netbeans
使用“工具”>“外掛”開啟外掛視窗。開啟“可用外掛”選項卡,然後在“Java Web 和 EE”類別下選擇“Java EE 基礎”和“EJB 和 EAR”。單擊安裝按鈕。Netbeans 將下載並安裝相應的外掛。使用“已安裝”選項卡驗證外掛安裝(如下面的影像所示)。
步驟 6 - 在 Netbeans 中配置 JBoss 伺服器
轉到“服務”選項卡,然後右鍵單擊伺服器以新增新伺服器。
將開啟“新增伺服器例項”嚮導。選擇 JBoss,並在下一步中輸入相關詳細資訊以在 netbeans 中配置伺服器。
配置完成後,您將看到以下螢幕。
步驟 7 - 安裝資料庫伺服器 (PostGreSql)
從 www.postgresql.org 下載最新版本的 PostGreSql 資料庫伺服器。在撰寫本教程時,我下載了PostGreSql 9.2
| 作業系統 | 安裝程式名稱 |
|---|---|
| Windows | PostGreSql 9.2 |
| Linux | PostGreSql 9.2 |
| Mac | PostGreSql 9.2 |
EJB - 建立應用程式
要建立一個簡單的 EJB 模組,我們將使用 NetBeans,“新建專案”嚮導。在下面給出的示例中,我們將建立一個名為 Component 的 EJB 模組專案。
建立專案
在 NetBeans IDE 中,選擇檔案 > 新建專案 >。您將看到以下螢幕
在類別Java EE下選擇專案型別,專案型別為EJB 模組。單擊下一步 >按鈕。您將看到以下螢幕。
輸入專案名稱和位置。單擊下一步 >按鈕。您將看到以下螢幕。
選擇伺服器為JBoss 應用程式伺服器。單擊完成按鈕。您將看到 NetBeans 建立的以下專案。
建立一個示例 EJB
要建立一個簡單的 EJB,我們將使用 NetBeans“新建”嚮導。在下面給出的示例中,我們將建立一個名為 librarySessionBean 的無狀態 EJB 類,位於 EjbComponent 專案下。
在專案瀏覽器視窗中選擇專案 EjbComponent 並右鍵單擊它。選擇新建 > 會話 Bean。您將看到新建會話 Bean嚮導。
輸入會話 Bean 名稱和包名稱。單擊完成按鈕。您將看到 NetBeans 建立的以下 EJB 類。
LibrarySessionBean - 無狀態會話 Bean
LibrarySessionBeanLocal - 會話 Bean 的本地介面
我將本地介面更改為遠端介面,因為我們將在基於控制檯的應用程式中訪問我們的 EJB。遠端/本地介面用於公開 EJB 必須實現的業務方法。
LibrarySessionBeanLocal 重新命名為 LibrarySessionBeanRemote,並且 LibrarySessionBean 實現 LibrarySessionBeanRemote 介面。
LibrarySessionBeanRemote
package com.tutorialspoint.stateless;
import java.util.List;
import javax.ejb.Remote;
@Remote
public interface LibrarySessionBeanRemote {
void addBook(String bookName);
List getBooks();
}
LibrarySessionBean
package com.tutorialspoint.stateless;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.Stateless;
@Stateless
public class LibrarySessionBean implements LibrarySessionBeanRemote {
List<String> bookShelf;
public LibrarySessionBean() {
bookShelf = new ArrayList<String>();
}
public void addBook(String bookName) {
bookShelf.add(bookName);
}
public List<String> getBooks() {
return bookShelf;
}
}
構建專案
- 在專案瀏覽器視窗中選擇 EjbComponent 專案。
- 右鍵單擊它以開啟上下文選單。
- 選擇清理並構建。
您將在 NetBeans 控制檯輸出中看到以下輸出。
ant -f C:\\EJB\\EjbComponent clean dist init: undeploy-clean: deps-clean: Deleting directory C:\EJB\EjbComponent\build Deleting directory C:\EJB\EjbComponent\dist clean: init: deps-jar: Created dir: C:\EJB\EjbComponent\build\classes Copying 3 files to C:\EJB\EjbComponent\build\classes\META-INF Created dir: C:\EJB\EjbComponent\build\empty Created dir: C:\EJB\EjbComponent\build\generated-sources\ap-source-output Compiling 2 source files to C:\EJB\EjbComponent\build\classes warning: [options] bootstrap class path not set in conjunction with -source 1.6 Note: C:\EJB\EjbComponent\src\java\com\tutorialspoint\stateless \LibraryPersistentBean.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. 1 warning compile: library-inclusion-in-archive: Created dir: C:\EJB\EjbComponent\dist Building jar: C:\EJB\EjbComponent\dist\EjbComponent.jar dist: BUILD SUCCESSFUL (total time: 3 seconds)
啟動應用程式伺服器
- 在“服務”視窗的“伺服器”下選擇 JBoss 應用程式伺服器。
- 右鍵單擊它以開啟上下文選單。
- 選擇啟動。
您將在 NetBeans 中看到以下輸出,JBoss 應用程式伺服器下的輸出。
Calling C:\jboss-5.1.0.GA\bin\run.conf.bat ========================================================================= JBoss Bootstrap Environment JBOSS_HOME: C:\jboss-5.1.0.GA JAVA: C:\Program Files (x86)\Java\jdk1.6.0_21\bin\java JAVA_OPTS: -Dprogram.name=run.bat -Xms128m -Xmx512m -server CLASSPATH: C:\jboss-5.1.0.GA\bin\run.jar ========================================================================= 16:25:50,062 INFO [ServerImpl] Starting JBoss (Microcontainer)... 16:25:50,062 INFO [ServerImpl] Release ID: JBoss [The Oracle] 5.1.0.GA (build: SVNTag=JBoss_5_1_0_GA date=200905221634) ... 16:26:40,420 INFO [TomcatDeployment] deploy, ctxPath=/admin-console 16:26:40,485 INFO [config] Initializing Mojarra (1.2_12-b01-FCS) for context '/admin-console' 16:26:42,362 INFO [TomcatDeployment] deploy, ctxPath=/ 16:26:42,406 INFO [TomcatDeployment] deploy, ctxPath=/jmx-console 16:26:42,471 INFO [Http11Protocol] Starting Coyote HTTP/1.1 on http-127.0.0.1-8080 16:26:42,487 INFO [AjpProtocol] Starting Coyote AJP/1.3 on ajp-127.0.0.1-8009 16:26:42,493 INFO [ServerImpl] JBoss (Microcontainer) [5.1.0.GA (build: SVNTag=JBoss_5_1_0_GA date=200905221634)] Started in 52s:427ms
部署專案
- 在專案瀏覽器視窗中選擇 EjbComponent 專案。
- 右鍵單擊它以開啟上下文選單。
- 選擇部署。
您將在 NetBeans 控制檯輸出中看到以下輸出。
ant -f C:\\EJB\\EjbComponent -DforceRedeploy=true -Ddirectory.deployment.supported=false -Dnb.wait.for.caches=true run init: deps-jar: compile: library-inclusion-in-archive: Building jar: C:\EJB\EjbComponent\dist\EjbComponent.jar dist-directory-deploy: pre-run-deploy: Checking data source definitions for missing JDBC drivers... Distributing C:\EJB\EjbComponent\dist\EjbComponent.jar to [org.jboss.deployment.spi.LocalhostTarget@1e4f84ee] Deploying C:\EJB\EjbComponent\dist\EjbComponent.jar Application Deployed Operation start started Operation start completed post-run-deploy: run-deploy: run: BUILD SUCCESSFUL (total time: 2 seconds)
JBoss 應用程式伺服器日誌輸出
16:30:00,963 INFO [DeployHandler] Begin start, [EjbComponent.jar]
...
16:30:01,233 INFO [Ejb3DependenciesDeployer] Encountered deployment AbstractVFSDeploymentContext@12038795{vfszip:/C:/jboss-5.1.0.GA/server/default/deploy/EjbComponent.jar/}
...
16:30:01,281 INFO [JBossASKernel] jndi:LibrarySessionBean/remote-com.tutorialspoint.stateless.LibrarySessionBeanRemote
16:30:01,281 INFO [JBossASKernel] Class:com.tutorialspoint.stateless.LibrarySessionBeanRemote
16:30:01,281 INFO [JBossASKernel] jndi:LibrarySessionBean/remote
16:30:01,281 INFO [JBossASKernel] Added bean(jboss.j2ee:jar=EjbComponent.jar,name=
LibrarySessionBean,service=EJB3) to KernelDeployment of: EjbComponent.jar
16:30:01,282 INFO [JBossASKernel] installing bean: jboss.j2ee:jar=EjbComponent.jar,name=BookMessageHandler,service=EJB3
16:30:01,282 INFO [JBossASKernel] with dependencies:
16:30:01,282 INFO [JBossASKernel] and demands:
16:30:01,282 INFO [JBossASKernel] jboss.ejb:service=EJBTimerService
...
16:30:01,283 INFO [EJB3EndpointDeployer] Deploy
AbstractBeanMetaData@5497cb{name=jboss.j2ee:jar=EjbComponent.jar,
name=LibrarySessionBean, service=EJB3_endpoint bean=org.jboss.ejb3.endpoint.deployers.impl.EndpointImpl properties=[container] constructor=null autowireCandidate=true}
...
16:30:01,394 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibrarySessionBean,service=EJB3
16:30:01,395 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibrarySessionBean ejbName: LibrarySessionBean
16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI:
LibrarySessionBean/remote - EJB3.x Default Remote Business Interface
LibrarySessionBean/remote-com.tutorialspoint.stateless.LibrarySessionBeanRemote - EJB3.x Remote Business Interface
16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibrarySessionBean,service=EJB3
16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibrarySessionBean ejbName: LibrarySessionBean
16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI:
LibrarySessionBean/remote - EJB3.x Default Remote Business Interface
LibrarySessionBean/remote-com.tutorialspoint.stateless.LibrarySessionBeanRemote - EJB3.x Remote Business Interface
建立客戶端以訪問 EJB
在 NetBeans IDE 中,選擇檔案 > 新建專案 >。
在類別Java下選擇專案型別,專案型別為Java 應用程式。單擊下一步 > 按鈕
輸入專案名稱和位置。單擊完成 >按鈕。我們已選擇名稱為 EjbTester。
右鍵單擊專案瀏覽器視窗中的專案名稱。選擇屬性。
使用新增專案按鈕在編譯選項卡中新增前面建立的 EJB 元件專案。
使用新增 jar/資料夾按鈕在編譯選項卡中新增 jboss 庫。Jboss 庫可以位於 <jboss 安裝資料夾>> client 資料夾中。
在專案(例如 EjbTester)下建立 jndi.properties。
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
在其中建立包 com.tutorialspoint.test 和 EJBTester.java 類。
EJBTester.java
package com.tutorialspoint.test;
import com.tutorialspoint.stateless.LibrarySessionBeanRemote;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class EJBTester {
BufferedReader brConsoleReader = null;
Properties props;
InitialContext ctx;
{
props = new Properties();
try {
props.load(new FileInputStream("jndi.properties"));
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ctx = new InitialContext(props);
} catch (NamingException ex) {
ex.printStackTrace();
}
brConsoleReader =
new BufferedReader(new InputStreamReader(System.in));
}
public static void main(String[] args) {
EJBTester ejbTester = new EJBTester();
ejbTester.testStatelessEjb();
}
private void showGUI() {
System.out.println("**********************");
System.out.println("Welcome to Book Store");
System.out.println("**********************");
System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: ");
}
private void testStatelessEjb() {
try {
int choice = 1;
LibrarySessionBeanRemote libraryBean =
(LibrarySessionBeanRemote)ctx.lookup("LibrarySessionBean/remote");
while (choice != 2) {
String bookName;
showGUI();
String strChoice = brConsoleReader.readLine();
choice = Integer.parseInt(strChoice);
if (choice == 1) {
System.out.print("Enter book name: ");
bookName = brConsoleReader.readLine();
libraryBean.addBook(bookName);
}else if (choice == 2) {
break;
}
}
List<String> booksList = libraryBean.getBooks();
System.out.println("Book(s) entered so far: " + booksList.size());
for (int i = 0; i < booksList.size(); ++i) {
System.out.println((i+1)+". " + booksList.get(i));
}
LibrarySessionBeanRemote libraryBean1 =
(LibrarySessionBeanRemote)ctx.lookup("LibrarySessionBean/remote");
List<String> booksList1 = libraryBean1.getBooks();
System.out.println(
"***Using second lookup to get library stateless object***");
System.out.println(
"Book(s) entered so far: " + booksList1.size());
for (int i = 0; i < booksList1.size(); ++i) {
System.out.println((i+1)+". " + booksList1.get(i));
}
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
} finally {
try {
if(brConsoleReader !=null) {
brConsoleReader.close();
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}
}
執行客戶端以訪問 EJB
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在 Netbeans 控制檯中驗證以下輸出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Java ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. Learn Java ***Using second lookup to get library stateless object*** Book(s) entered so far: 0 BUILD SUCCESSFUL (total time: 13 seconds)
在接下來的章節中,我們將涵蓋此完整 EJB 應用程式的多個方面。
EJB - 無狀態 Bean
無狀態會話Bean是一種企業Bean,通常用於執行獨立的操作。顧名思義,無狀態會話Bean沒有任何與客戶端關聯的狀態,但它可以保留其例項狀態。EJB容器通常會建立一些無狀態Bean物件的池,並使用這些物件來處理客戶端的請求。由於池的存在,例項變數的值在查詢/方法呼叫之間不能保證相同。
建立無狀態EJB的步驟
建立無狀態EJB需要以下步驟:
建立公開業務方法的遠端/本地介面。
此介面將由EJB客戶端應用程式使用。
如果EJB客戶端與要部署EJB會話Bean的環境相同,則使用@Local註解。
如果EJB客戶端與要部署EJB會話Bean的環境不同,則使用@Remote註解。
建立實現上述介面的無狀態會話Bean。
使用@Stateless註解表示它是一個無狀態Bean。EJB容器在部署期間透過讀取此註解自動建立相關配置或介面。
遠端介面
import javax.ejb.Remote;
@Remote
public interface LibrarySessionBeanRemote {
//add business method declarations
}
無狀態EJB
@Stateless
public class LibrarySessionBean implements LibrarySessionBeanRemote {
//implement business method
}
示例應用程式
讓我們建立一個測試EJB應用程式來測試無狀態EJB。
| 步驟 | 描述 |
|---|---|
| 1 | 在包com.tutorialspoint.stateless下建立一個名為EjbComponent的專案,如EJB - 建立應用程式章節中所述。您也可以使用EJB - 建立應用程式章節中建立的專案,以便在本節中理解無狀態EJB的概念。 |
| 2 | 建立LibrarySessionBean.java和LibrarySessionBeanRemote,如EJB - 建立應用程式章節中所述。保持其餘檔案不變。 |
| 3 | 清理並構建應用程式,以確保業務邏輯按要求工作。 |
| 4 | 最後,將應用程式以jar檔案的形式部署到JBoss應用伺服器上。如果JBoss應用伺服器尚未啟動,它將自動啟動。 |
| 5 | 現在建立EJB客戶端,一個基於控制檯的應用程式,其方式與EJB - 建立應用程式章節中主題建立訪問EJB的客戶端中所述相同。 |
EJBComponent(EJB模組)
LibrarySessionBeanRemote.java
package com.tutorialspoint.stateless;
import java.util.List;
import javax.ejb.Remote;
@Remote
public interface LibrarySessionBeanRemote {
void addBook(String bookName);
List getBooks();
}
LibrarySessionBean.java
package com.tutorialspoint.stateless;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.Stateless;
@Stateless
public class LibrarySessionBean implements LibrarySessionBeanRemote {
List<String> bookShelf;
public LibrarySessionBean() {
bookShelf = new ArrayList<String>();
}
public void addBook(String bookName) {
bookShelf.add(bookName);
}
public List<String> getBooks() {
return bookShelf;
}
}
一旦您在JBOSS上部署了EjbComponent專案,請注意jboss日誌。
JBoss已自動為我們的會話Bean建立了一個JNDI條目 - LibrarySessionBean/remote。
我們將使用此查詢字串獲取型別為com.tutorialspoint.stateless.LibrarySessionBeanRemote的遠端業務物件。
JBoss 應用程式伺服器日誌輸出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibrarySessionBean/remote - EJB3.x Default Remote Business Interface LibrarySessionBean/remote-com.tutorialspoint.stateless.LibrarySessionBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibrarySessionBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibrarySessionBeanRemote ejbName: LibrarySessionBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibrarySessionBean/remote - EJB3.x Default Remote Business Interface LibrarySessionBean/remote-com.tutorialspoint.stateless.LibrarySessionBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客戶端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
這些屬性用於初始化java命名服務的InitialContext物件。
InitialContext物件將用於查詢無狀態會話Bean。
EJBTester.java
package com.tutorialspoint.test;
import com.tutorialspoint.stateful.LibrarySessionBeanRemote;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class EJBTester {
BufferedReader brConsoleReader = null;
Properties props;
InitialContext ctx;
{
props = new Properties();
try {
props.load(new FileInputStream("jndi.properties"));
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ctx = new InitialContext(props);
} catch (NamingException ex) {
ex.printStackTrace();
}
brConsoleReader =
new BufferedReader(new InputStreamReader(System.in));
}
public static void main(String[] args) {
EJBTester ejbTester = new EJBTester();
ejbTester.testStatelessEjb();
}
private void showGUI() {
System.out.println("**********************");
System.out.println("Welcome to Book Store");
System.out.println("**********************");
System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: ");
}
private void testStatelessEjb() {
try {
int choice = 1;
LibrarySessionBeanRemote libraryBean =
LibrarySessionBeanRemote)ctx.lookup("LibrarySessionBean/remote");
while (choice != 2) {
String bookName;
showGUI();
String strChoice = brConsoleReader.readLine();
choice = Integer.parseInt(strChoice);
if (choice == 1) {
System.out.print("Enter book name: ");
bookName = brConsoleReader.readLine();
Book book = new Book();
book.setName(bookName);
libraryBean.addBook(book);
} else if (choice == 2) {
break;
}
}
List<Book> booksList = libraryBean.getBooks();
System.out.println("Book(s) entered so far: " + booksList.size());
int i = 0;
for (Book book:booksList) {
System.out.println((i+1)+". " + book.getName());
i++;
}
LibrarySessionBeanRemote libraryBean1 =
(LibrarySessionBeanRemote)ctx.lookup("LibrarySessionBean/remote");
List<String> booksList1 = libraryBean1.getBooks();
System.out.println(
"***Using second lookup to get library stateless object***");
System.out.println(
"Book(s) entered so far: " + booksList1.size());
for (int i = 0; i < booksList1.size(); ++i) {
System.out.println((i+1)+". " + booksList1.get(i));
}
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}finally {
try {
if(brConsoleReader !=null) {
brConsoleReader.close();
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}
}
EJBTester執行以下任務:
從jndi.properties載入屬性並初始化InitialContext物件。
在testStatelessEjb()方法中,使用名稱"LibrarySessionBean/remote"進行jndi查詢以獲取遠端業務物件(無狀態ejb)。
然後向用戶顯示一個圖書館商店使用者介面,並要求他/她輸入選擇。
如果使用者輸入1,系統會要求輸入書名,並使用無狀態會話Bean的addBook()方法儲存書籍。會話Bean將其儲存在例項變數中。
如果使用者輸入2,系統將使用無狀態會話Bean的getBooks()方法檢索書籍並退出。
然後使用名稱"LibrarySessionBean/remote"再次進行jndi查詢以獲取遠端業務物件(無狀態EJB),並列出書籍。
執行客戶端以訪問 EJB
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在 Netbeans 控制檯中驗證以下輸出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Java ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. Learn Java ***Using second lookup to get library stateless object*** Book(s) entered so far: 0 BUILD SUCCESSFUL (total time: 13 seconds)
再次執行客戶端以訪問EJB
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在 Netbeans 控制檯中驗證以下輸出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 0 ***Using second lookup to get library stateless object*** Book(s) entered so far: 1 1. Learn Java BUILD SUCCESSFUL (total time: 12 seconds)
上面顯示的輸出可能會有所不同,具體取決於JBoss維護了多少個無狀態EJB物件。
如果維護單個無狀態EJB物件,則在每次查詢後您可能會看到相同的書籍列表。
EJB容器可能會為每次查詢返回相同的無狀態EJB物件。
無狀態EJB Bean會保留例項變數的值,直到伺服器重新啟動。
EJB - 有狀態 Bean
有狀態會話Bean是一種企業Bean,它保留與客戶端的會話狀態。顧名思義,有狀態會話Bean在其例項變數中保留關聯的客戶端狀態。EJB容器為每個客戶端請求建立一個單獨的有狀態會話Bean。一旦請求範圍結束,有狀態會話Bean就會被銷燬。
建立有狀態EJB的步驟
建立有狀態EJB需要以下步驟:
建立公開業務方法的遠端/本地介面。
此介面將由EJB客戶端應用程式使用。
如果EJB客戶端與要部署EJB會話Bean的環境相同,則使用@Local註解。
如果EJB客戶端與要部署EJB會話Bean的環境不同,則使用@Remote註解。
建立實現上述介面的有狀態會話Bean。
使用@Stateful註解表示它是一個有狀態Bean。EJB容器在部署期間透過讀取此註解自動建立相關配置或介面。
遠端介面
import javax.ejb.Remote;
@Remote
public interface LibraryStatefulSessionBeanRemote {
//add business method declarations
}
有狀態EJB
@Stateful
public class LibraryStatefulSessionBean implements LibraryStatefulSessionBeanRemote {
//implement business method
}
示例應用程式
讓我們建立一個測試EJB應用程式來測試有狀態EJB。
| 步驟 | 描述 |
|---|---|
| 1 | 在包com.tutorialspoint.stateful下建立一個名為EjbComponent的專案,如EJB - 建立應用程式章節中所述。您也可以使用EJB - 建立應用程式章節中建立的專案,以便在本節中理解有狀態EJB的概念。 |
| 2 | 建立LibraryStatefulSessionBean.java和LibraryStatefulSessionBeanRemote,如EJB - 建立應用程式章節中所述。保持其餘檔案不變。 |
| 3 | 清理並構建應用程式,以確保業務邏輯按要求工作。 |
| 4 | 最後,將應用程式以jar檔案的形式部署到JBoss應用伺服器上。如果JBoss應用伺服器尚未啟動,它將自動啟動。 |
| 5 | 現在建立EJB客戶端,一個基於控制檯的應用程式,其方式與EJB - 建立應用程式章節中主題建立訪問EJB的客戶端中所述相同。 |
EJBComponent(EJB模組)
LibraryStatefulSessionBeanRemote.java
package com.tutorialspoint.stateful;
import java.util.List;
import javax.ejb.Remote;
@Remote
public interface LibraryStatefulSessionBeanRemote {
void addBook(String bookName);
List getBooks();
}
LibraryStatefulSessionBean.java
package com.tutorialspoint.stateful;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.Stateful;
@Stateful
public class LibraryStatefulSessionBean implements LibraryStatefulSessionBeanRemote {
List<String> bookShelf;
public LibraryStatefulSessionBean() {
bookShelf = new ArrayList<String>();
}
public void addBook(String bookName) {
bookShelf.add(bookName);
}
public List<String> getBooks() {
return bookShelf;
}
}
一旦您在JBOSS上部署了EjbComponent專案,請注意jboss日誌。
JBoss已自動為我們的會話Bean建立了一個JNDI條目 - LibraryStatefulSessionBean/remote。
我們將使用此查詢字串獲取型別為com.tutorialspoint.stateful.LibraryStatefulSessionBeanRemote的遠端業務物件。
JBoss 應用程式伺服器日誌輸出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryStatefulSessionBean/remote - EJB3.x Default Remote Business Interface LibraryStatefulSessionBean/remote-com.tutorialspoint.stateful.LibraryStatefulSessionBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryStatefulSessionBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateful.LibraryStatefulSessionBeanRemote ejbName: LibraryStatefulSessionBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryStatefulSessionBean/remote - EJB3.x Default Remote Business Interface LibraryStatefulSessionBean/remote-com.tutorialspoint.stateful.LibraryStatefulSessionBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客戶端)
jndi.properties
java.naming.factory.initial = org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs = org.jboss.naming:org.jnp.interfaces java.naming.provider.url = localhost
這些屬性用於初始化java命名服務的InitialContext物件。
InitialContext物件將用於查詢有狀態會話Bean。
EJBTester.java
package com.tutorialspoint.test;
import com.tutorialspoint.stateful.LibraryStatefulSessionBeanRemote;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class EJBTester {
BufferedReader brConsoleReader = null;
Properties props;
InitialContext ctx;
{
props = new Properties();
try {
props.load(new FileInputStream("jndi.properties"));
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ctx = new InitialContext(props);
} catch (NamingException ex) {
ex.printStackTrace();
}
brConsoleReader =
new BufferedReader(new InputStreamReader(System.in));
}
public static void main(String[] args) {
EJBTester ejbTester = new EJBTester();
ejbTester.testStatelessEjb();
}
private void showGUI() {
System.out.println("**********************");
System.out.println("Welcome to Book Store");
System.out.println("**********************");
System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: ");
}
private void testStatelessEjb() {
try {
int choice = 1;
LibraryStatefulSessionBeanRemote libraryBean =
LibraryStatefulSessionBeanRemote)ctx.lookup("LibraryStatefulSessionBean/remote");
while (choice != 2) {
String bookName;
showGUI();
String strChoice = brConsoleReader.readLine();
choice = Integer.parseInt(strChoice);
if (choice == 1) {
System.out.print("Enter book name: ");
bookName = brConsoleReader.readLine();
Book book = new Book();
book.setName(bookName);
libraryBean.addBook(book);
} else if (choice == 2) {
break;
}
}
List<Book> booksList = libraryBean.getBooks();
System.out.println("Book(s) entered so far: " + booksList.size());
int i = 0;
for (Book book:booksList) {
System.out.println((i+1)+". " + book.getName());
i++;
}
LibraryStatefulSessionBeanRemote libraryBean1 =
(LibraryStatefulSessionBeanRemote)ctx.lookup("LibraryStatefulSessionBean/remote");
List<String> booksList1 = libraryBean1.getBooks();
System.out.println(
"***Using second lookup to get library stateful object***");
System.out.println(
"Book(s) entered so far: " + booksList1.size());
for (int i = 0; i < booksList1.size(); ++i) {
System.out.println((i+1)+". " + booksList1.get(i));
}
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}finally {
try {
if(brConsoleReader !=null) {
brConsoleReader.close();
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}
}
EJBTester執行以下任務:
從jndi.properties載入屬性並初始化InitialContext物件。
在testStatefulEjb()方法中,使用名稱"LibraryStatefulSessionBean/remote"進行jndi查詢以獲取遠端業務物件(有狀態ejb)。
然後向用戶顯示一個圖書館商店使用者介面,並要求他/她輸入選擇。
如果使用者輸入1,系統會要求輸入書名,並使用有狀態會話Bean的addBook()方法儲存書籍。會話Bean將其儲存在例項變數中。
如果使用者輸入2,系統將使用有狀態會話Bean的getBooks()方法檢索書籍並退出。
然後使用名稱"LibraryStatefulSessionBean/remote"再次進行jndi查詢以獲取遠端業務物件(有狀態EJB),並列出書籍。
執行客戶端以訪問 EJB
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在Netbeans控制檯中驗證以下輸出:
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Java ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. Learn Java ***Using second lookup to get library stateful object*** Book(s) entered so far: 0 BUILD SUCCESSFUL (total time: 13 seconds)
再次執行客戶端以訪問EJB
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在 Netbeans 控制檯中驗證以下輸出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 0 ***Using second lookup to get library stateful object*** Book(s) entered so far: 0 BUILD SUCCESSFUL (total time: 12 seconds)
上面顯示的輸出表明,對於每次查詢,都會返回不同的有狀態EJB例項。
有狀態EJB物件僅保留單個會話的值。在第二次執行中,我們沒有獲取任何書籍的值。
EJB - 永續性
在EJB 3.0中,EJB 2.0中使用的實體Bean很大程度上被持久化機制所取代。現在,實體Bean是一個簡單的POJO,與表具有對映關係。
持久化API中的關鍵參與者如下:
實體 - 表示資料儲存記錄的持久化物件。最好是可序列化的。
實體管理器 - 持久化介面,用於對持久化物件(實體)執行新增/刪除/更新/查詢等資料操作。它還有助於使用查詢介面執行查詢。
持久化單元 (persistence.xml) - 持久化單元描述持久化機制的屬性。
資料來源 (*ds.xml) - 資料來源描述資料儲存相關的屬性,如連線URL、使用者名稱、密碼等。
為了演示EJB持久化機制,我們需要執行以下任務:
步驟1 - 在資料庫中建立表。
步驟2 - 建立與表對應的實體類。
步驟3 - 建立資料來源和持久化單元。
步驟4 - 建立具有實體管理器例項的無狀態EJB。
步驟5 - 更新無狀態EJB。新增方法以透過實體管理器從資料庫新增和獲取記錄。
步驟6 - 基於控制檯的應用程式客戶端將訪問無狀態EJB以將資料持久化到資料庫中。
建立表
在預設資料庫postgres中建立一個名為books的表。
CREATE TABLE books ( id integer PRIMARY KEY, name varchar(50) );
建立實體類
//mark it entity using Entity annotation
//map table name using Table annotation
@Entity
@Table(name="books")
public class Book implements Serializable{
private int id;
private String name;
public Book() {
}
//mark id as primary key with autogenerated value
//map database column id with id field
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="id")
public int getId() {
return id;
}
...
}
建立資料來源和持久化單元
資料來源 (jboss-ds.xml)
<?xml version = "1.0" encoding = "UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>PostgresDS</jndi-name>
<connection-url>jdbc:postgresql://:5432/postgres</connection-url>
<driver-class>org.postgresql.driver</driver-class>
<user-name>sa</user-name>
<password>sa</password>
<min-pool-size>5</min-pool-size>
<max-pool-size>20</max-pool-size>
<idle-timeout-minutes>5</idle-timeout-minutes>
</local-tx-datasource>
</datasources>
持久化單元 (persistence.xml)
<persistence version = "1.0" xmlns = "http://java.sun.com/xml/ns/persistence" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name = "EjbComponentPU" transaction-type = "JTA">
<jta-data-source>java:/PostgresDS</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties/>
</persistence-unit>
<persistence-unit name = "EjbComponentPU2" transaction-type = "JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/PostgresDS</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
建立具有實體管理器例項的無狀態EJB
@Stateless
public class LibraryPersistentBean implements LibraryPersistentBeanRemote {
//pass persistence unit to entityManager.
@PersistenceContext(unitName="EjbComponentPU")
private EntityManager entityManager;
public void addBook(Book book) {
entityManager.persist(book);
}
public List<Book> getBooks() {
return entityManager.createQuery("From Books").getResultList();
}
...
}
構建EJB模組後,我們需要一個客戶端來訪問無狀態Bean,我們將在下一節中建立。
示例應用程式
讓我們建立一個測試EJB應用程式來測試EJB持久化機制。
| 步驟 | 描述 |
|---|---|
| 1 | 在包com.tutorialspoint.entity下建立一個名為EjbComponent的專案,如EJB - 建立應用程式章節中所述。您也可以使用EJB - 建立應用程式章節中建立的專案,以便在本節中理解EJB持久化概念。 |
| 2 | 在包com.tutorialspoint.entity下建立Book.java,並按如下所示修改它。 |
| 3 | 建立LibraryPersistentBean.java和LibraryPersistentBeanRemote,如EJB - 建立應用程式章節中所述,並按如下所示修改它們。 |
| 4 | 在EjbComponent > setup資料夾中建立jboss-ds.xml,在EjbComponent > src > conf資料夾中建立persistence.xml。這些資料夾可以在Netbeans的檔案選項卡中看到。按上面顯示的方式修改這些檔案。 |
| 5 | 清理並構建應用程式,以確保業務邏輯按要求工作。 |
| 6 | 最後,將應用程式以jar檔案的形式部署到JBoss應用伺服器上。如果JBoss應用伺服器尚未啟動,它將自動啟動。 |
| 7 | 現在建立EJB客戶端,一個基於控制檯的應用程式,其方式與EJB - 建立應用程式章節中主題建立訪問EJB的客戶端中所述相同。按如下所示修改它。 |
EJBComponent(EJB模組)
Book.java
package com.tutorialspoint.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="books")
public class Book implements Serializable{
private int id;
private String name;
public Book() {
}
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
LibraryPersistentBeanRemote.java
package com.tutorialspoint.stateless;
import com.tutorialspoint.entity.Book;
import java.util.List;
import javax.ejb.Remote;
@Remote
public interface LibraryPersistentBeanRemote {
void addBook(Book bookName);
List<Book> getBooks();
}
LibraryPersistentBean.java
package com.tutorialspoint.stateless;
import com.tutorialspoint.entity.Book;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Stateless
public class LibraryPersistentBean implements LibraryPersistentBeanRemote {
public LibraryPersistentBean() {
}
@PersistenceContext(unitName="EjbComponentPU")
private EntityManager entityManager;
public void addBook(Book book) {
entityManager.persist(book);
}
public List<Book> getBooks() {
return entityManager.createQuery("From Book").getResultList();
}
}
一旦您在JBOSS上部署了EjbComponent專案,請注意jboss日誌。
JBoss已自動為我們的會話Bean建立了一個JNDI條目 - LibraryPersistentBean/remote。
我們將使用此查詢字串獲取型別為com.tutorialspoint.stateless.LibraryPersistentBeanRemote的遠端業務物件。
JBoss 應用程式伺服器日誌輸出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.stateless.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryPersistentBeanRemote,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibraryPersistentBeanRemote ejbName: LibraryPersistentBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.stateless.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客戶端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
這些屬性用於初始化java命名服務的InitialContext物件。
InitialContext物件將用於查詢無狀態會話Bean。
EJBTester.java
package com.tutorialspoint.test;
import com.tutorialspoint.stateless.LibraryPersistentBeanRemote;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class EJBTester {
BufferedReader brConsoleReader = null;
Properties props;
InitialContext ctx;
{
props = new Properties();
try {
props.load(new FileInputStream("jndi.properties"));
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ctx = new InitialContext(props);
} catch (NamingException ex) {
ex.printStackTrace();
}
brConsoleReader =
new BufferedReader(new InputStreamReader(System.in));
}
public static void main(String[] args) {
EJBTester ejbTester = new EJBTester();
ejbTester.testEntityEjb();
}
private void showGUI() {
System.out.println("**********************");
System.out.println("Welcome to Book Store");
System.out.println("**********************");
System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: ");
}
private void testEntityEjb() {
try {
int choice = 1;
LibraryPersistentBeanRemote libraryBean =
LibraryPersistentBeanRemote)ctx.lookup("LibraryPersistentBean/remote");
while (choice != 2) {
String bookName;
showGUI();
String strChoice = brConsoleReader.readLine();
choice = Integer.parseInt(strChoice);
if (choice == 1) {
System.out.print("Enter book name: ");
bookName = brConsoleReader.readLine();
Book book = new Book();
book.setName(bookName);
libraryBean.addBook(book);
} else if (choice == 2) {
break;
}
}
List<Book> booksList = libraryBean.getBooks();
System.out.println("Book(s) entered so far: " + booksList.size());
int i = 0;
for (Book book:booksList) {
System.out.println((i+1)+". " + book.getName());
i++;
}
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}finally {
try {
if(brConsoleReader !=null) {
brConsoleReader.close();
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}
}
EJBTester執行以下任務。
從jndi.properties載入屬性並初始化InitialContext物件。
在testStatefulEjb()方法中,使用名稱"LibraryStatefulSessionBean/remote"進行jndi查詢以獲取遠端業務物件(有狀態ejb)。
然後向用戶顯示一個圖書館商店使用者介面,並要求他/她輸入選擇。
如果使用者輸入1,系統會要求輸入書名,並使用無狀態會話Bean的addBook()方法儲存書籍。會話Bean透過EntityManager呼叫將書籍持久化到資料庫中。
如果使用者輸入2,系統將使用有狀態會話Bean的getBooks()方法檢索書籍並退出。
然後使用名稱"LibraryStatelessSessionBean/remote"再次進行jndi查詢以獲取遠端業務物件(無狀態EJB),並列出書籍。
執行客戶端以訪問 EJB
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在Netbeans控制檯中驗證以下輸出:
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Java ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. learn java BUILD SUCCESSFUL (total time: 15 seconds)
再次執行客戶端以訪問EJB
在訪問EJB之前重新啟動JBoss。
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在 Netbeans 控制檯中驗證以下輸出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Spring ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 2 1. learn java 2. Learn Spring BUILD SUCCESSFUL (total time: 15 seconds)
上面顯示的輸出表明,書籍正在儲存到持久化儲存中,並從資料庫中檢索。
EJB - 訊息驅動 Bean
訊息驅動Bean是一種企業Bean,當它從佇列或主題接收訊息時,由EJB容器呼叫。訊息驅動Bean是一個無狀態Bean,用於非同步執行任務。
為了演示訊息驅動Bean的使用,我們將使用EJB持久化章節,並且我們需要執行以下任務:
步驟1 - 在資料庫中建立表(請參閱EJB-持久化章節)。
步驟2 - 建立與表對應的實體類(請參閱EJB-持久化章節)。
步驟3 - 建立資料來源和持久化單元(請參閱EJB-持久化章節)。
步驟4 - 建立具有實體管理器例項的無狀態EJB(請參閱EJB-持久化章節)。
步驟5 - 更新無狀態ejb。新增方法以透過實體管理器從資料庫新增和獲取記錄(請參閱EJB-持久化章節)。
步驟6 - 在JBossdefault應用程式目錄中建立一個名為BookQueue的佇列。
步驟7 - 基於控制檯的應用程式客戶端將向此佇列傳送訊息。
步驟 8 − 建立一個訊息驅動 Bean,它將使用無狀態 Bean 持久化客戶端資料。
步驟 9 − JBoss 的 EJB 容器將呼叫以上訊息驅動 Bean,並將客戶端傳送的訊息傳遞給它。
建立佇列
如果在<JBoss 安裝資料夾> > server > default > deploy 資料夾中不存在,則建立一個名為 jbossmq-destinations-service.xml 的檔案。
這裡我們建立一個名為 BookQueue 的佇列 -
jbossmq-destinations-service.xml
<mbean code="org.jboss.mq.server.jmx.Queue"
name="jboss.mq.destination:service=Queue,name=BookQueue">
<depends optional-attribute-name="DestinationManager">
jboss.mq:service=DestinationManager
</depends>
</mbean>
啟動 JBoss 後,您將在 JBoss 日誌中看到類似的條目。
... 10:37:06,167 INFO [QueueService] Queue[/queue/BookQueue] started, fullSize=200000, pageSize=2000, downCacheSize=2000 ...
建立訊息驅動 Bean
@MessageDriven(
name = "BookMessageHandler",
activationConfig = {
@ActivationConfigProperty( propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty( propertyName = "destination",
propertyValue ="/queue/BookQueue")
}
)
public class LibraryMessageBean implements MessageListener {
@Resource
private MessageDrivenContext mdctx;
@EJB
LibraryPersistentBeanRemote libraryBean;
public LibraryMessageBean() {
}
public void onMessage(Message message) {
}
}
LibraryMessageBean 使用 @MessageDriven 註解進行註釋,以將其標記為訊息驅動 Bean。
其屬性定義為 destinationType - Queue 和 destination - /queue/BookQueue。
它實現了 MessageListener 介面,該介面公開 onMessage 方法。
它具有 MessgeDrivenContext 作為資源。
LibraryPersistentBeanRemote 無狀態 Bean 在此 Bean 中注入,用於持久化目的。
構建 EjbComponent 專案並在 JBoss 上部署它。構建和部署 EJB 模組後,我們需要一個客戶端向 JBoss 佇列傳送訊息。
示例應用程式
讓我們建立一個測試 EJB 應用程式來測試訊息驅動 Bean。
| 步驟 | 描述 |
|---|---|
| 1 | 在包com.tutorialspoint.entity下建立一個名為EjbComponent的專案,如EJB - 建立應用程式章節中所述。您也可以使用EJB - 建立應用程式章節中建立的專案,以便在本節中理解EJB持久化概念。 |
| 2 | 在 EJB-Persistence 章節中建立的包 com.tutorialspoint.entity 下建立 Book.java。 |
| 3 | 建立 LibraryPersistentBean.java 和 LibraryPersistentBeanRemote,如 EJB-Persistence 章節中所建立。 |
| 4 | 在EjbComponent > setup 資料夾中建立 jboss-ds.xml,在EjbComponent > src > conf 資料夾中建立 persistence.xml。這些資料夾可以在 Netbeans 的檔案選項卡中看到,如 EJB-Persistence 章節中所建立。 |
| 5 | 在包 com.tutorialspoint.messagebean 下建立 LibraryMessageBean.java,並根據以下所示進行修改。 |
| 6 | 如上所述在 JBoss 中建立 BookQueue 佇列。 |
| 7 | 清理並構建應用程式,以確保業務邏輯按要求工作。 |
| 8 | 最後,將應用程式以jar檔案的形式部署到JBoss應用伺服器上。如果JBoss應用伺服器尚未啟動,它將自動啟動。 |
| 9 | 現在建立EJB客戶端,一個基於控制檯的應用程式,其方式與EJB - 建立應用程式章節中主題建立訪問EJB的客戶端中所述相同。按如下所示修改它。 |
EJBComponent(EJB模組)
LibraryMessageBean.java
package com.tutorialspoint.messagebean;
import com.tutorialspoint.entity.Book;
import com.tutorialspoint.stateless.LibraryPersistentBeanRemote;
import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.EJB;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
@MessageDriven(
name = "BookMessageHandler",
activationConfig = {
@ActivationConfigProperty( propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty( propertyName = "destination",
propertyValue ="/queue/BookQueue")
}
)
public class LibraryMessageBean implements MessageListener {
@Resource
private MessageDrivenContext mdctx;
@EJB
LibraryPersistentBeanRemote libraryBean;
public LibraryMessageBean() {
}
public void onMessage(Message message) {
ObjectMessage objectMessage = null;
try {
objectMessage = (ObjectMessage) message;
Book book = (Book) objectMessage.getObject();
libraryBean.addBook(book);
} catch (JMSException ex) {
mdctx.setRollbackOnly();
}
}
}
EJBTester(EJB客戶端)
EJBTester.java
package com.tutorialspoint.test;
import com.tutorialspoint.entity.Book;
import com.tutorialspoint.stateless.LibraryPersistentBeanRemote;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Properties;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class EJBTester {
BufferedReader brConsoleReader = null;
Properties props;
InitialContext ctx;
{
props = new Properties();
try {
props.load(new FileInputStream("jndi.properties"));
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ctx = new InitialContext(props);
} catch (NamingException ex) {
ex.printStackTrace();
}
brConsoleReader =
new BufferedReader(new InputStreamReader(System.in));
}
public static void main(String[] args) {
EJBTester ejbTester = new EJBTester();
ejbTester.testMessageBeanEjb();
}
private void showGUI() {
System.out.println("**********************");
System.out.println("Welcome to Book Store");
System.out.println("**********************");
System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: ");
}
private void testMessageBeanEjb() {
try {
int choice = 1;
Queue queue = (Queue) ctx.lookup("/queue/BookQueue");
QueueConnectionFactory factory =
(QueueConnectionFactory) ctx.lookup("ConnectionFactory");
QueueConnection connection = factory.createQueueConnection();
QueueSession session =
connection.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);
QueueSender sender = session.createSender(queue);
while (choice != 2) {
String bookName;
showGUI();
String strChoice = brConsoleReader.readLine();
choice = Integer.parseInt(strChoice);
if (choice == 1) {
System.out.print("Enter book name: ");
bookName = brConsoleReader.readLine();
Book book = new Book();
book.setName(bookName);
ObjectMessage objectMessage =
session.createObjectMessage(book);
sender.send(objectMessage);
} else if (choice == 2) {
break;
}
}
LibraryPersistentBeanRemote libraryBean =
(LibraryPersistentBeanRemote)
ctx.lookup("LibraryPersistentBean/remote");
List<Book> booksList = libraryBean.getBooks();
System.out.println("Book(s) entered so far: " + booksList.size());
int i = 0;
for (Book book:booksList) {
System.out.println((i+1)+". " + book.getName());
i++;
}
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}finally {
try {
if(brConsoleReader !=null) {
brConsoleReader.close();
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}
}
EJBTester執行以下任務:
從jndi.properties載入屬性並初始化InitialContext物件。
在 testStatefulEjb() 方法中,使用名稱 - "/queue/BookQueue" 執行 JNDI 查詢以獲取在 JBoss 中可用的佇列的引用。然後使用佇列會話建立傳送者。
然後向用戶顯示一個圖書館商店使用者介面,並要求他/她輸入選擇。
如果使用者輸入 1,系統將提示輸入書籍名稱,傳送者將書籍名稱傳送到佇列。當 JBoss 容器在佇列中收到此訊息時,它將呼叫我們訊息驅動 Bean 的 onMessage 方法。然後,我們的訊息驅動 Bean 使用有狀態會話 Bean 的 addBook() 方法儲存書籍。會話 Bean 透過 EntityManager 呼叫將書籍持久化到資料庫中。
如果使用者輸入 2,則使用名稱 - "LibraryStatefulSessionBean/remote" 執行另一個 JNDI 查詢以再次獲取遠端業務物件(有狀態 EJB),並完成書籍列表。
執行客戶端以訪問 EJB
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在Netbeans控制檯中驗證以下輸出:
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn EJB ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 2 1. learn java 1. learn EJB BUILD SUCCESSFUL (total time: 15 seconds)
上面顯示的輸出表明我們的訊息驅動 Bean 正在接收訊息並將書籍儲存在持久儲存中,並且書籍是從資料庫中檢索的。
EJB - 註解
註解是在 Java 5.0 中引入的。使用註解的目的是在類或類的元資料中附加其他資訊,使其包含在原始碼中。在 EJB 3.0 中,註解用於描述 EJB 類中的配置元資料。透過這種方式,EJB 3.0 消除了在配置 XML 檔案中描述配置資料的需要。
EJB 容器使用編譯器工具透過讀取這些註解來生成所需的工件,如介面、部署描述符。以下是常用註解的列表。
| 序號 | 名稱 | 描述 |
|---|---|---|
| 1 | javax.ejb.Stateless |
指定給定的 EJB 類是無狀態會話 Bean。 屬性
|
| 2 | javax.ejb.Stateful |
指定給定的 EJB 類是有狀態會話 Bean。 屬性
|
| 3 | javax.ejb.MessageDrivenBean |
指定給定的 EJB 類是訊息驅動 Bean。 屬性
|
| 4 | javax.ejb.EJB |
用於將依賴項指定或注入為 EJB 例項到另一個 EJB 中。 屬性
|
| 5 | javax.ejb.Local |
用於指定會話 Bean 的本地介面。此本地介面宣告會話 Bean 的業務方法(可以是無狀態或有狀態)。 此介面用於向本地客戶端公開業務方法,這些客戶端在與 EJB 相同的部署/應用程式中執行。 屬性
|
| 6 | javax.ejb.Remote |
用於指定會話 Bean 的遠端介面。此遠端介面宣告會話 Bean 的業務方法(可以是無狀態或有狀態)。 此介面用於向遠端客戶端公開業務方法,這些客戶端在與 EJB 不同的部署/應用程式中執行。 屬性
|
| 7 | javax.ejb.Activation ConfigProperty |
用於指定訊息驅動 Bean 所需的屬性。例如,端點、目標、訊息選擇器等。 此註解作為 javax.ejb.MessageDrivenBean 註解的 activationConfig 屬性的引數傳遞。 屬性
|
| 8 | javax.ejb.PostActivate |
用於指定 EJB 生命週期回撥方法。當 EJB 容器剛剛啟用/重新啟用 Bean 例項時,將呼叫此方法。 此介面用於向本地客戶端公開業務方法,這些客戶端在與 EJB 相同的部署/應用程式中執行。 |
EJB - 回撥
回撥是一種機制,透過該機制可以攔截企業 Bean 的生命週期。EJB 3.0 規範指定了回撥,為此建立了回撥處理程式方法。EJB 容器呼叫這些回撥。我們可以在 EJB 類本身或單獨的類中定義回撥方法。EJB 3.0 為回撥提供了許多註解。
以下是無狀態 Bean 的回撥註解列表 -
| 註解 | 描述 |
|---|---|
| @PostConstruct | 首次建立 Bean 時呼叫。 |
| @PreDestroy | 當 Bean 從 Bean 池中刪除或被銷燬時呼叫。 |
以下是有狀態 Bean 的回撥註解列表 -
| 註解 | 描述 |
|---|---|
| @PostConstruct | 首次建立 Bean 時呼叫。 |
| @PreDestroy | 當 Bean 從 Bean 池中刪除或被銷燬時呼叫。 |
| @PostActivate | 當載入 Bean 以供使用時呼叫。 |
| @PrePassivate | 當 Bean 放回 Bean 池時呼叫。 |
以下是訊息驅動 Bean 的回撥註解列表 -
| 註解 | 描述 |
|---|---|
| @PostConstruct | 首次建立 Bean 時呼叫。 |
| @PreDestroy | 當 Bean 從 Bean 池中刪除或被銷燬時呼叫。 |
以下是實體 Bean 的回撥註解列表 -
| 註解 | 描述 |
|---|---|
| @PrePersist | 在資料庫中建立實體時呼叫。 |
| @PostPersist | 在資料庫中建立實體後呼叫。 |
| @PreRemove | 從資料庫中刪除實體時呼叫。 |
| @PostRemove | 從資料庫中刪除實體後呼叫。 |
| @PreUpdate | 在資料庫中更新實體之前呼叫。 |
| @PostLoad | 從資料庫中提取記錄並載入到實體中時呼叫。 |
示例應用程式
讓我們建立一個測試 EJB 應用程式來測試 EJB 中的各種回撥。
| 步驟 | 描述 |
|---|---|
| 1 | 在 EJB - 建立應用程式 章節中解釋的包 com.tutorialspoint.stateless 下建立一個名為 EjbComponent 的專案。您也可以使用 EJB - 永續性 章節中建立的專案,為該章節新增各種 EJB 回撥。 |
| 2 | 建立LibrarySessionBean.java和LibrarySessionBeanRemote,如EJB - 建立應用程式章節中所述。保持其餘檔案不變。 |
| 3 | 使用 EJB - 永續性 章節中建立的 Bean。新增如下所示的回撥方法。保持其餘檔案不變。 |
| 4 | 在包 com.tutorialspoint.callback 下建立一個 Java 類 BookCallbackListener。此類將演示回撥方法的分離。 |
| 5 | 清理並構建應用程式,以確保業務邏輯按要求工作。 |
| 6 | 最後,將應用程式以jar檔案的形式部署到JBoss應用伺服器上。如果JBoss應用伺服器尚未啟動,它將自動啟動。 |
| 7 | 現在建立EJB客戶端,一個基於控制檯的應用程式,其方式與EJB - 建立應用程式章節中主題建立訪問EJB的客戶端中所述相同。 |
EJBComponent(EJB模組)
BookCallbackListener.java
package com.tutorialspoint.callback;
import javax.persistence.PrePersist;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
import com.tutorialspoint.entity.Book;
public class BookCallbackListener {
@PrePersist
public void prePersist(Book book) {
System.out.println("BookCallbackListener.prePersist:"
+ "Book to be created with book id: "+book.getId());
}
@PostPersist
public void postPersist(Object book) {
System.out.println("BookCallbackListener.postPersist::"
+ "Book created with book id: "+((Book)book).getId());
}
@PreRemove
public void preRemove(Book book) {
System.out.println("BookCallbackListener.preRemove:"
+ " About to delete Book: " + book.getId());
}
@PostRemove
public void postRemove(Book book) {
System.out.println("BookCallbackListener.postRemove::"
+ " Deleted Book: " + book.getId());
}
@PreUpdate
public void preUpdate(Book book) {
System.out.println("BookCallbackListener.preUpdate::"
+ " About to update Book: " + book.getId());
}
@PostUpdate
public void postUpdate(Book book) {
System.out.println("BookCallbackListener.postUpdate::"
+ " Updated Book: " + book.getId());
}
@PostLoad
public void postLoad(Book book) {
System.out.println("BookCallbackListener.postLoad::"
+ " Loaded Book: " + book.getId());
}
}
Book.java
package com.tutorialspoint.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="books")
public class Book implements Serializable{
private int id;
private String name;
public Book() {
}
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
LibraryStatefulSessionBean.java
package com.tutorialspoint.stateful;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.PostActivate;
import javax.ejb.PrePassivate;
import javax.ejb.Stateful;
@Stateful
public class LibraryStatefulSessionBean
implements LibraryStatefulSessionBeanRemote {
List<String> bookShelf;
public LibraryStatefulSessionBean() {
bookShelf = new ArrayList<String>();
}
public void addBook(String bookName) {
bookShelf.add(bookName);
}
public List<String> getBooks() {
return bookShelf;
}
@PostConstruct
public void postConstruct() {
System.out.println("LibraryStatefulSessionBean.postConstruct::"
+ " bean created.");
}
@PreDestroy
public void preDestroy() {
System.out.println("LibraryStatefulSessionBean.preDestroy:"
+ " bean removed.");
}
@PostActivate
public void postActivate() {
System.out.println("LibraryStatefulSessionBean.postActivate:"
+ " bean activated.");
}
@PrePassivate
public void prePassivate() {
System.out.println("LibraryStatefulSessionBean.prePassivate:"
+ " bean passivated.");
}
}
LibraryStatefulSessionBeanRemote.java
package com.tutorialspoint.stateful;
import java.util.List;
import javax.ejb.Remote;
@Remote
public interface LibraryStatefulSessionBeanRemote {
void addBook(String bookName);
List getBooks();
}
LibraryPersistentBean.java
package com.tutorialspoint.stateless;
import com.tutorialspoint.entity.Book;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Stateless
public class LibraryPersistentBean
implements LibraryPersistentBeanRemote {
public LibraryPersistentBean() {}
@PersistenceContext(unitName="EntityEjbPU")
private EntityManager entityManager;
public void addBook(Book book) {
entityManager.persist(book);
}
public List<Book> getBooks() {
return entityManager.createQuery("From Book")
.getResultList();
}
@PostConstruct
public void postConstruct() {
System.out.println("postConstruct:: LibraryPersistentBean session bean"
+ " created with entity Manager object: ");
}
@PreDestroy
public void preDestroy() {
System.out.println("preDestroy: LibraryPersistentBean session"
+ " bean is removed ");
}
}
LibraryPersistentBeanRemote.java
package com.tutorialspoint.stateless;
import com.tutorialspoint.entity.Book;
import java.util.List;
import javax.ejb.Remote;
@Remote
public interface LibraryPersistentBeanRemote {
void addBook(Book bookName);
List<Book> getBooks();
}
一旦您在JBOSS上部署了EjbComponent專案,請注意jboss日誌。
JBoss已自動為我們的會話Bean建立了一個JNDI條目 - LibraryPersistentBean/remote。
我們將使用此查詢字串獲取型別為com.tutorialspoint.stateless.LibraryPersistentBeanRemote的遠端業務物件。
JBoss 應用程式伺服器日誌輸出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.stateless.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryPersistentBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibrarySessionBeanRemote ejbName: LibraryPersistentBean ...
EJBTester(EJB客戶端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
這些屬性用於初始化java命名服務的InitialContext物件。
InitialContext物件將用於查詢無狀態會話Bean。
EJBTester.java
package com.tutorialspoint.test;
import com.tutorialspoint.stateful.LibrarySessionBeanRemote;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class EJBTester {
BufferedReader brConsoleReader = null;
Properties props;
InitialContext ctx;
{
props = new Properties();
try {
props.load(new FileInputStream("jndi.properties"));
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ctx = new InitialContext(props);
} catch (NamingException ex) {
ex.printStackTrace();
}
brConsoleReader =
new BufferedReader(new InputStreamReader(System.in));
}
public static void main(String[] args) {
EJBTester ejbTester = new EJBTester();
ejbTester.testEntityEjb();
}
private void showGUI() {
System.out.println("**********************");
System.out.println("Welcome to Book Store");
System.out.println("**********************");
System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: ");
}
private void testEntityEjb() {
try {
int choice = 1;
LibraryPersistentBeanRemote libraryBean =
(LibraryPersistentBeanRemote)
ctx.lookup("LibraryPersistentBean/remote");
while (choice != 2) {
String bookName;
showGUI();
String strChoice = brConsoleReader.readLine();
choice = Integer.parseInt(strChoice);
if (choice == 1) {
System.out.print("Enter book name: ");
bookName = brConsoleReader.readLine();
Book book = new Book();
book.setName(bookName);
libraryBean.addBook(book);
} else if (choice == 2) {
break;
}
}
List<Book> booksList = libraryBean.getBooks();
System.out.println("Book(s) entered so far: " + booksList.size());
int i = 0;
for (Book book:booksList) {
System.out.println((i+1)+". " + book.getName());
i++;
}
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}finally {
try {
if(brConsoleReader !=null) {
brConsoleReader.close();
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}
}
EJBTester執行以下任務:
從jndi.properties載入屬性並初始化InitialContext物件。
在 testStatelessEjb() 方法中,使用名稱 - "LibrarySessionBean/remote" 執行 JNDI 查詢以獲取遠端業務物件(無狀態 EJB)。
然後向用戶顯示圖書館商店使用者介面,並提示使用者輸入選擇。
如果使用者輸入 1,系統將提示輸入書籍名稱,並使用無狀態會話 Bean 的 addBook() 方法儲存書籍。會話 Bean 將書籍儲存在資料庫中。
如果使用者輸入2,系統將使用無狀態會話Bean的getBooks()方法檢索書籍並退出。
執行客戶端以訪問 EJB
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在 Netbeans 控制檯中驗證以下輸出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Java ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. Learn Java BUILD SUCCESSFUL (total time: 13 seconds)
JBoss 應用程式伺服器日誌輸出
您可以在 JBoss 日誌中找到以下回調條目
14:08:34,293 INFO [STDOUT] postConstruct:: LibraryPersistentBean session bean created with entity Manager object ... 16:39:09,484 INFO [STDOUT] BookCallbackListener.prePersist:: Book to be created with book id: 0 16:39:09,531 INFO [STDOUT] BookCallbackListener.postPersist:: Book created with book id: 1 16:39:09,900 INFO [STDOUT] BookCallbackListener.postLoad:: Loaded Book: 1 ...
EJB - 定時器服務
定時器服務是一種機制,透過該機制可以構建計劃應用程式。例如,每月 1 日生成工資單。EJB 3.0 規範指定了 @Timeout 註解,該註解有助於在無狀態或訊息驅動 Bean 中對 EJB 服務進行程式設計。EJB 容器呼叫使用 @Timeout 註解的方法。
EJB 定時器服務是 EJB 容器提供的一項服務,它有助於建立定時器並在定時器到期時安排回撥。
建立定時器的步驟
使用 @Resource 註解將 SessionContext 注入 Bean 中 -
@Stateless
public class TimerSessionBean {
@Resource
private SessionContext context;
...
}
使用 SessionContext 物件獲取 TimerService 並建立定時器。以毫秒和訊息的形式傳遞時間。
public void createTimer(long duration) {
context.getTimerService().createTimer(duration, "Hello World!");
}
使用定時器的步驟
將 @Timeout 註解應用於方法。返回型別應為 void,並傳遞型別為 Timer 的引數。我們在第一次執行後取消定時器,否則它將在固定間隔後繼續執行。
@Timeout
public void timeOutHandler(Timer timer) {
System.out.println("timeoutHandler : " + timer.getInfo());
timer.cancel();
}
示例應用程式
讓我們建立一個測試 EJB 應用程式來測試 EJB 中的定時器服務。
| 步驟 | 描述 |
|---|---|
| 1 | 在 EJB - 建立應用程式 章節中解釋的包 com.tutorialspoint.timer 下建立一個名為 EjbComponent 的專案。 |
| 2 | 建立 TimerSessionBean.java 和 TimerSessionBeanRemote,如 EJB - 建立應用程式 章節中所解釋。保持其餘檔案不變。 |
| 3 | 清理並構建應用程式,以確保業務邏輯按要求工作。 |
| 4 | 最後,將應用程式以jar檔案的形式部署到JBoss應用伺服器上。如果JBoss應用伺服器尚未啟動,它將自動啟動。 |
| 5 | 現在建立EJB客戶端,一個基於控制檯的應用程式,其方式與EJB - 建立應用程式章節中主題建立訪問EJB的客戶端中所述相同。 |
EJBComponent(EJB模組)
TimerSessionBean.java
package com.tutorialspoint.timer;
import javax.annotation.Resource;
import javax.ejb.SessionContext;
import javax.ejb.Timer;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
@Stateless
public class TimerSessionBean implements TimerSessionBeanRemote {
@Resource
private SessionContext context;
public void createTimer(long duration) {
context.getTimerService().createTimer(duration, "Hello World!");
}
@Timeout
public void timeOutHandler(Timer timer) {
System.out.println("timeoutHandler : " + timer.getInfo());
timer.cancel();
}
}
TimerSessionBeanRemote.java
package com.tutorialspoint.timer;
import javax.ejb.Remote;
@Remote
public interface TimerSessionBeanRemote {
public void createTimer(long milliseconds);
}
一旦您在JBOSS上部署了EjbComponent專案,請注意jboss日誌。
JBoss 已自動為我們的會話 Bean 建立了一個 JNDI 條目 - TimerSessionBean/remote。
我們將使用此查詢字串來獲取型別為 - com.tutorialspoint.timer.TimerSessionBeanRemote 的遠端業務物件。
JBoss 應用程式伺服器日誌輸出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: TimerSessionBean/remote - EJB3.x Default Remote Business Interface TimerSessionBean/remote-com.tutorialspoint.timer.TimerSessionBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=TimerSessionBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.timer.TimerSessionBeanRemote ejbName: TimerSessionBean ...
EJBTester(EJB客戶端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
這些屬性用於初始化java命名服務的InitialContext物件。
InitialContext物件將用於查詢無狀態會話Bean。
EJBTester.java
package com.tutorialspoint.test;
import com.tutorialspoint.stateful.TimerSessionBeanRemote;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class EJBTester {
BufferedReader brConsoleReader = null;
Properties props;
InitialContext ctx;
{
props = new Properties();
try {
props.load(new FileInputStream("jndi.properties"));
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ctx = new InitialContext(props);
} catch (NamingException ex) {
ex.printStackTrace();
}
brConsoleReader =
new BufferedReader(new InputStreamReader(System.in));
}
public static void main(String[] args) {
EJBTester ejbTester = new EJBTester();
ejbTester.testTimerService();
}
private void showGUI() {
System.out.println("**********************");
System.out.println("Welcome to Book Store");
System.out.println("**********************");
System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: ");
}
private void testTimerService() {
try {
TimerSessionBeanRemote timerServiceBean = (TimerSessionBeanRemote)ctx.lookup("TimerSessionBean/remote");
System.out.println("["+(new Date()).toString()+ "]" + "timer created.");
timerServiceBean.createTimer(2000);
} catch (NamingException ex) {
ex.printStackTrace();
}
}
}
EJBTester 正在執行以下任務。
從jndi.properties載入屬性並初始化InitialContext物件。
在 testTimerService() 方法中,使用名稱 - "TimerSessionBean/remote" 執行 JNDI 查詢以獲取遠端業務物件(定時器無狀態 EJB)。
然後呼叫 createTimer,將 2000 毫秒作為計劃時間傳遞。
EJB 容器在 2 秒後呼叫 timeoutHandler 方法。
執行客戶端以訪問 EJB
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在 Netbeans 控制檯中驗證以下輸出。
run: [Wed Jun 19 11:35:47 IST 2013]timer created. BUILD SUCCESSFUL (total time: 0 seconds)
JBoss 應用程式伺服器日誌輸出
您可以在 JBoss 日誌中找到以下回調條目
... 11:35:49,555 INFO [STDOUT] timeoutHandler : Hello World! ...
EJB - 依賴注入
EJB 3.0 規範提供了註解,這些註解可以應用於欄位或 setter 方法以注入依賴項。EJB 容器使用全域性 JNDI 登錄檔來查詢依賴項。以下註解在 EJB 3.0 中用於依賴項注入。
@EJB − 用於注入其他 EJB 引用。
@Resource − 用於注入資料來源或單例服務,如 sessionContext、timerService 等。
使用 @EJB 的步驟
@EJB 可以以下列方式用於欄位或方法中 -
public class LibraryMessageBean implements MessageListener {
//dependency injection on field.
@EJB
LibraryPersistentBeanRemote libraryBean;
...
}
public class LibraryMessageBean implements MessageListener {
LibraryPersistentBeanRemote libraryBean;
//dependency injection on method.
@EJB(beanName="com.tutorialspoint.stateless.LibraryPersistentBean")
public void setLibraryPersistentBean(
LibraryPersistentBeanRemote libraryBean)
{
this.libraryBean = libraryBean;
}
...
}
使用 @Resource 的步驟
@Resource 通常用於注入 EJB 容器提供的單例。
public class LibraryMessageBean implements MessageListener {
@Resource
private MessageDrivenContext mdctx;
...
}
示例應用程式
讓我們建立一個測試 EJB 應用程式來測試 EJB 中的依賴項注入服務。
| 步驟 | 描述 |
|---|---|
| 1 | 在 EJB - 建立應用程式 章節中解釋的包 com.tutorialspoint.timer 下建立一個名為 EjbComponent 的專案。 |
| 2 | 使用 EJB - 訊息驅動 Bean 章節中建立的 Bean。保持其餘檔案不變。 |
| 3 | 清理並構建應用程式,以確保業務邏輯按要求工作。 |
| 4 | 最後,將應用程式以jar檔案的形式部署到JBoss應用伺服器上。如果JBoss應用伺服器尚未啟動,它將自動啟動。 |
| 5 | 現在建立EJB客戶端,一個基於控制檯的應用程式,其方式與EJB - 建立應用程式章節中主題建立訪問EJB的客戶端中所述相同。 |
EJBComponent(EJB模組)
LibraryMessageBean.java
package com.tuturialspoint.messagebean;
import com.tutorialspoint.entity.Book;
import com.tutorialspoint.stateless.LibraryPersistentBeanRemote;
import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.EJB;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
@MessageDriven(
name = "BookMessageHandler",
activationConfig = {
@ActivationConfigProperty( propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty( propertyName = "destination",
propertyValue ="/queue/BookQueue")
}
)
public class LibraryMessageBean implements MessageListener {
@Resource
private MessageDrivenContext mdctx;
@EJB
LibraryPersistentBeanRemote libraryBean;
public LibraryMessageBean() {
}
public void onMessage(Message message) {
ObjectMessage objectMessage = null;
try {
objectMessage = (ObjectMessage) message;
Book book = (Book) objectMessage.getObject();
libraryBean.addBook(book);
}catch (JMSException ex) {
mdctx.setRollbackOnly();
}
}
}
EJBTester(EJB客戶端)
EJBTester.java
package com.tutorialspoint.test;
import com.tutorialspoint.entity.Book;
import com.tutorialspoint.stateless.LibraryPersistentBeanRemote;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Properties;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class EJBTester {
BufferedReader brConsoleReader = null;
Properties props;
InitialContext ctx;
{
props = new Properties();
try {
props.load(new FileInputStream("jndi.properties"));
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ctx = new InitialContext(props);
} catch (NamingException ex) {
ex.printStackTrace();
}
brConsoleReader =
new BufferedReader(new InputStreamReader(System.in));
}
public static void main(String[] args) {
EJBTester ejbTester = new EJBTester();
ejbTester.testMessageBeanEjb();
}
private void showGUI() {
System.out.println("**********************");
System.out.println("Welcome to Book Store");
System.out.println("**********************");
System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: ");
}
private void testMessageBeanEjb() {
try {
int choice = 1;
Queue queue = (Queue) ctx.lookup("/queue/BookQueue");
QueueConnectionFactory factory =
(QueueConnectionFactory) ctx.lookup("ConnectionFactory");
QueueConnection connection = factory.createQueueConnection();
QueueSession session = connection.createQueueSession(
false, QueueSession.AUTO_ACKNOWLEDGE);
QueueSender sender = session.createSender(queue);
while (choice != 2) {
String bookName;
showGUI();
String strChoice = brConsoleReader.readLine();
choice = Integer.parseInt(strChoice);
if (choice == 1) {
System.out.print("Enter book name: ");
bookName = brConsoleReader.readLine();
Book book = new Book();
book.setName(bookName);
ObjectMessage objectMessage =
session.createObjectMessage(book);
sender.send(objectMessage);
} else if (choice == 2) {
break;
}
}
LibraryPersistentBeanRemote libraryBean =
(LibraryPersistentBeanRemote)
ctx.lookup("LibraryPersistentBean/remote");
List<Book> booksList = libraryBean.getBooks();
System.out.println("Book(s) entered so far: "
+ booksList.size());
int i = 0;
for (Book book:booksList) {
System.out.println((i+1)+". " + book.getName());
i++;
}
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}finally {
try {
if(brConsoleReader !=null) {
brConsoleReader.close();
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}
}
EJBTester執行以下任務:
從jndi.properties載入屬性並初始化InitialContext物件。
在 testStatefulEjb() 方法中,使用名稱 - "/queue/BookQueue" 執行 JNDI 查詢以獲取在 JBoss 中可用的佇列的引用。然後使用佇列會話建立傳送者。
然後向用戶顯示一個圖書館商店使用者介面,並要求他/她輸入選擇。
如果使用者輸入1,系統會要求輸入書籍名稱,傳送者將書籍名稱傳送到佇列。當JBoss容器在佇列中接收到此訊息時,它會呼叫我們訊息驅動Bean的onMessage方法。然後,我們的訊息驅動Bean使用有狀態會話Bean的addBook()方法儲存書籍。會話Bean透過EntityManager呼叫將書籍持久化到資料庫中。
如果使用者輸入2,則使用名稱“LibraryStatefulSessionBean/remote”進行另一個JNDI查詢,以再次獲取遠端業務物件(有狀態EJB),並進行書籍列表。
執行客戶端以訪問 EJB
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在 Netbeans 控制檯中驗證以下輸出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn EJB ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 2 1. learn java 1. learn EJB BUILD SUCCESSFUL (total time: 15 seconds)
上面顯示的輸出表明我們的訊息驅動Bean正在接收訊息並將書籍儲存在持久儲存中,並且書籍是從資料庫中檢索的。
我們的訊息驅動Bean使用注入到其中的LibraryPersistentBean,使用@EJB註解,如果發生異常,則使用MessageDrivenContext物件回滾事務。
EJB - 攔截器
EJB 3.0提供規範,使用帶有@AroundInvoke註解的方法攔截業務方法呼叫。攔截器方法在ejbContainer呼叫其攔截的業務方法之前被呼叫。以下是攔截器方法的示例簽名
@AroundInvoke
public Object methodInterceptor(InvocationContext ctx) throws Exception {
System.out.println("*** Intercepting call to LibraryBean method: "
+ ctx.getMethod().getName());
return ctx.proceed();
}
攔截器方法可以應用或繫結在三個級別。
預設 - 預設攔截器對部署中的每個Bean都呼叫。預設攔截器只能透過xml(ejb-jar.xml)應用。
類 - 類級別攔截器對Bean的每個方法都呼叫。類級別攔截器可以透過註解或透過xml(ejb-jar.xml)應用。
方法 - 方法級別攔截器對Bean的特定方法呼叫。方法級別攔截器可以透過註解或透過xml(ejb-jar.xml)應用。
我們在這裡討論類級別攔截器。
攔截器類
package com.tutorialspoint.interceptor;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
public class BusinessInterceptor {
@AroundInvoke
public Object methodInterceptor(InvocationContext ctx) throws Exception {
System.out.println("*** Intercepting call to LibraryBean method: "
+ ctx.getMethod().getName());
return ctx.proceed();
}
}
遠端介面
import javax.ejb.Remote;
@Remote
public interface LibraryBeanRemote {
//add business method declarations
}
被攔截的無狀態EJB
@Interceptors ({BusinessInterceptor.class})
@Stateless
public class LibraryBean implements LibraryBeanRemote {
//implement business method
}
示例應用程式
讓我們建立一個測試EJB應用程式來測試被攔截的無狀態EJB。
| 步驟 | 描述 |
|---|---|
| 1 | 在EJB - 建立應用程式章節中說明的com.tutorialspoint.interceptor包下,建立一個名為EjbComponent的專案。您也可以使用EJB - 建立應用程式章節中建立的專案,以便在本節中瞭解被攔截的EJB概念。 |
| 2 | 在EJB - 建立應用程式章節中說明的com.tutorialspoint.interceptor包下建立LibraryBean.java和LibraryBeanRemote。保持其餘檔案不變。 |
| 3 | 清理並構建應用程式,以確保業務邏輯按要求工作。 |
| 4 | 最後,將應用程式以jar檔案的形式部署到JBoss應用伺服器上。如果JBoss應用伺服器尚未啟動,它將自動啟動。 |
| 5 | 現在建立EJB客戶端,一個基於控制檯的應用程式,其方式與EJB - 建立應用程式章節中主題建立訪問EJB的客戶端中說明的相同。 |
EJBComponent(EJB模組)
LibraryBeanRemote.java
package com.tutorialspoint.interceptor;
import java.util.List;
import javax.ejb.Remote;
@Remote
public interface LibraryBeanRemote {
void addBook(String bookName);
List getBooks();
}
LibraryBean.java
package com.tutorialspoint.interceptor;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
@Interceptors ({BusinessInterceptor.class})
@Stateless
public class LibraryBean implements LibraryBeanRemote {
List<String> bookShelf;
public LibraryBean() {
bookShelf = new ArrayList<String>();
}
public void addBook(String bookName) {
bookShelf.add(bookName);
}
public List<String> getBooks() {
return bookShelf;
}
}
一旦您在JBOSS上部署了EjbComponent專案,請注意jboss日誌。
JBoss已自動為我們的會話Bean建立了一個JNDI條目 - LibraryBean/remote。
我們將使用此查詢字串獲取型別為com.tutorialspoint.interceptor.LibraryBeanRemote的遠端業務物件。
JBoss 應用程式伺服器日誌輸出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryBean/remote - EJB3.x Default Remote Business Interface LibraryBean/remote-com.tutorialspoint.interceptor.LibraryBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.interceptor.LibraryBeanRemote ejbName: LibraryBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryBean/remote - EJB3.x Default Remote Business Interface LibraryBean/remote-com.tutorialspoint.interceptor.LibraryBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客戶端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
這些屬性用於初始化java命名服務的InitialContext物件。
InitialContext物件將用於查詢無狀態會話Bean。
EJBTester.java
package com.tutorialspoint.test;
import com.tutorialspoint.stateful.LibraryBeanRemote;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class EJBTester {
BufferedReader brConsoleReader = null;
Properties props;
InitialContext ctx;
{
props = new Properties();
try {
props.load(new FileInputStream("jndi.properties"));
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ctx = new InitialContext(props);
} catch (NamingException ex) {
ex.printStackTrace();
}
brConsoleReader =
new BufferedReader(new InputStreamReader(System.in));
}
public static void main(String[] args) {
EJBTester ejbTester = new EJBTester();
ejbTester.testInterceptedEjb();
}
private void showGUI() {
System.out.println("**********************");
System.out.println("Welcome to Book Store");
System.out.println("**********************");
System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: ");
}
private void testInterceptedEjb() {
try {
int choice = 1;
LibraryBeanRemote libraryBean =
LibraryBeanRemote)ctx.lookup("LibraryBean/remote");
while (choice != 2) {
String bookName;
showGUI();
String strChoice = brConsoleReader.readLine();
choice = Integer.parseInt(strChoice);
if (choice == 1) {
System.out.print("Enter book name: ");
bookName = brConsoleReader.readLine();
Book book = new Book();
book.setName(bookName);
libraryBean.addBook(book);
} else if (choice == 2) {
break;
}
}
List<Book> booksList = libraryBean.getBooks();
System.out.println("Book(s) entered so far: " + booksList.size());
int i = 0;
for (Book book:booksList) {
System.out.println((i+1)+". " + book.getName());
i++;
}
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}finally {
try {
if(brConsoleReader !=null) {
brConsoleReader.close();
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}
}
EJBTester執行以下任務:
從jndi.properties載入屬性並初始化InitialContext物件。
在testInterceptedEjb()方法中,使用名稱“LibraryBean/remote”進行JNDI查詢,以獲取遠端業務物件(無狀態EJB)。
然後向用戶顯示圖書館商店使用者介面,並提示使用者輸入選擇。
如果使用者輸入1,系統會要求輸入書籍名稱,並使用無狀態會話Bean的addBook()方法儲存書籍。會話Bean將其儲存在例項變數中。
如果使用者輸入2,系統將使用無狀態會話Bean的getBooks()方法檢索書籍並退出。
執行客戶端以訪問 EJB
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在 Netbeans 控制檯中驗證以下輸出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Java ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. Learn Java BUILD SUCCESSFUL (total time: 13 seconds)
JBoss 應用程式伺服器日誌輸出
在JBoss應用程式伺服器日誌輸出中驗證以下輸出。
.... 09:55:40,741 INFO [STDOUT] *** Intercepting call to LibraryBean method: addBook 09:55:43,661 INFO [STDOUT] *** Intercepting call to LibraryBean method: getBooks
EJB - 可嵌入物件
EJB 3.0提供選項將JAVA POJO(普通舊Java物件)嵌入到實體Bean中,並允許將列名與嵌入式POJO類的方法對映。要嵌入的Java POJO必須用@Embeddable註釋。
@Embeddable
public class Publisher implements Serializable{
private String name;
private String address;
...
}
可以使用@Embedded註解嵌入上面的類。
@Entity
public class Book implements Serializable{
private int id;
private String name;
private Publisher publisher;
...
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "name",
column = @Column(name = "PUBLISHER")),
@AttributeOverride(name = "address",
column = @Column(name = "PUBLISHER_ADDRESS"))
})
public Publisher getPublisher() {
return publisher;
}
...
}
示例應用程式
讓我們建立一個測試EJB應用程式來測試EJB 3.0中的嵌入物件。
| 步驟 | 描述 |
|---|---|
| 1 | 在EJB - 建立應用程式章節中說明的com.tutorialspoint.entity包下,建立一個名為EjbComponent的專案。請在本節中使用EJB - 永續性章節中建立的專案,以便了解EJB概念中的嵌入物件。 |
| 2 | 在EJB - 建立應用程式章節中說明的com.tutorialspoint.entity包下建立Publisher.java。保持其餘檔案不變。 |
| 3 | 在com.tutorialspoint.entity包下建立Book.java。使用EJB - 永續性章節作為參考。保持其餘檔案不變。 |
| 4 | 清理並構建應用程式,以確保業務邏輯按要求工作。 |
| 5 | 最後,將應用程式以jar檔案的形式部署到JBoss應用程式伺服器上。如果JBoss應用程式伺服器尚未啟動,它將自動啟動。 |
| 6 | 現在建立EJB客戶端,一個基於控制檯的應用程式,其方式與EJB - 建立應用程式章節中主題建立訪問EJB的客戶端中所述相同。 |
建立/修改Book表
CREATE TABLE book ( id integer PRIMARY KEY, name varchar(50) ); Alter table book add publisher varchar(100); Alter table book add publisher_address varchar(200);
EJBComponent(EJB模組)
Publisher.java
package com.tutorialspoint.entity;
import java.io.Serializable;
import javax.persistence.Embeddable;
@Embeddable
public class Publisher implements Serializable{
private String name;
private String address;
public Publisher() {}
public Publisher(String name, String address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String toString() {
return name + "," + address;
}
}
Book.java
package com.tutorialspoint.entity;
import com.tutorialspoint.callback.BookCallbackListener;
import java.io.Serializable;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="book")
public class Book implements Serializable{
private int id;
private String name;
private Publisher publisher;
public Book() {
}
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "name",
column = @Column(name = "PUBLISHER")),
@AttributeOverride(name = "address",
column = @Column(name = "PUBLISHER_ADDRESS"))
})
public Publisher getPublisher() {
return publisher;
}
public void setPublisher(Publisher publisher) {
this.publisher = publisher;
}
}
LibraryPersistentBeanRemote.java
package com.tutorialspoint.stateless;
import com.tutorialspoint.entity.Book;
import java.util.List;
import javax.ejb.Remote;
@Remote
public interface LibraryPersistentBeanRemote {
void addBook(Book bookName);
List<Book> getBooks();
}
LibraryPersistentBean.java
package com.tutorialspoint.stateless;
import com.tutorialspoint.entity.Book;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Stateless
public class LibraryPersistentBean implements LibraryPersistentBeanRemote {
public LibraryPersistentBean() {
}
@PersistenceContext(unitName="EjbComponentPU")
private EntityManager entityManager;
public void addBook(Book book) {
entityManager.persist(book);
}
public List<Book> getBooks() {
return entityManager.createQuery("From Book").getResultList();
}
}
一旦您在JBOSS上部署了EjbComponent專案,請注意jboss日誌。
JBoss已自動為我們的會話Bean建立了一個JNDI條目 - LibraryPersistentBean/remote。
我們將使用此查詢字串獲取型別為com.tutorialspoint.interceptor.LibraryPersistentBeanRemote的遠端業務物件。
JBoss 應用程式伺服器日誌輸出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.interceptor.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryPersistentBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.interceptor.LibraryPersistentBeanRemote ejbName: LibraryPersistentBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.interceptor.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客戶端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
這些屬性用於初始化java命名服務的InitialContext物件。
InitialContext物件將用於查詢無狀態會話Bean。
EJBTester.java
package com.tutorialspoint.test;
import com.tutorialspoint.stateful.LibraryBeanRemote;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class EJBTester {
BufferedReader brConsoleReader = null;
Properties props;
InitialContext ctx;
{
props = new Properties();
try {
props.load(new FileInputStream("jndi.properties"));
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ctx = new InitialContext(props);
} catch (NamingException ex) {
ex.printStackTrace();
}
brConsoleReader =
new BufferedReader(new InputStreamReader(System.in));
}
public static void main(String[] args) {
EJBTester ejbTester = new EJBTester();
ejbTester.testEmbeddedObjects();
}
private void showGUI() {
System.out.println("**********************");
System.out.println("Welcome to Book Store");
System.out.println("**********************");
System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: ");
}
private void testEmbeddedObjects() {
try {
int choice = 1;
LibraryPersistentBeanRemote libraryBean =
(LibraryPersistentBeanRemote)
ctx.lookup("LibraryPersistentBean/remote");
while (choice != 2) {
String bookName;
String publisherName;
String publisherAddress;
showGUI();
String strChoice = brConsoleReader.readLine();
choice = Integer.parseInt(strChoice);
if (choice == 1) {
System.out.print("Enter book name: ");
bookName = brConsoleReader.readLine();
System.out.print("Enter publisher name: ");
publisherName = brConsoleReader.readLine();
System.out.print("Enter publisher address: ");
publisherAddress = brConsoleReader.readLine();
Book book = new Book();
book.setName(bookName);
book.setPublisher
(new Publisher(publisherName,publisherAddress));
libraryBean.addBook(book);
} else if (choice == 2) {
break;
}
}
List<Book> booksList = libraryBean.getBooks();
System.out.println("Book(s) entered so far: " + booksList.size());
int i = 0;
for (Book book:booksList) {
System.out.println((i+1)+". " + book.getName());
System.out.println("Publication: "+book.getPublisher());
i++;
}
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}finally {
try {
if(brConsoleReader !=null) {
brConsoleReader.close();
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}
}
EJBTester執行以下任務:
從jndi.properties載入屬性並初始化InitialContext物件。
在testInterceptedEjb()方法中,使用名稱“LibraryPersistenceBean/remote”進行JNDI查詢,以獲取遠端業務物件(無狀態EJB)。
然後向用戶顯示一個圖書館商店使用者介面,並要求他/她輸入選擇。
如果使用者輸入1,系統會要求輸入書籍名稱,並使用無狀態會話Bean的addBook()方法儲存書籍。會話Bean將書籍儲存到資料庫中。
如果使用者輸入2,系統將使用無狀態會話Bean的getBooks()方法檢索書籍並退出。
執行客戶端以訪問 EJB
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在 Netbeans 控制檯中驗證以下輸出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: learn html5 Enter publisher name: SAMS Enter publisher address: DELHI ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. learn html5 Publication: SAMS,DELHI BUILD SUCCESSFUL (total time: 21 seconds)
EJB - Blob/Clob
EJB 3.0使用@Lob註解支援Blob和Clob型別。可以使用@Lob註解對映以下Java型別。
- java.sql.Blob
- java.sql.Clob
- byte[]
- String
- 可序列化物件
@Entity
@Table(name="books")
@EntityListeners(BookCallbackListener.class)
public class Book implements Serializable{
...
private byte[] image;
@Lob @Basic(fetch= FetchType.EAGER)
public byte[] getImage() {
return image;
}
...
}
示例應用程式
讓我們建立一個測試EJB應用程式來測試EJB 3.0中的blob/clob支援。
| 步驟 | 描述 |
|---|---|
| 1 | 在EJB - 建立應用程式章節中說明的com.tutorialspoint.entity包下,建立一個名為EjbComponent的專案。請在本節中使用EJB - 永續性章節中建立的專案,以便了解ejb概念中的clob/blob物件。 |
| 2 | 在com.tutorialspoint.entity包下建立Book.java。使用EJB - 永續性章節作為參考。保持其餘檔案不變。 |
| 3 | 清理並構建應用程式,以確保業務邏輯按要求工作。 |
| 4 | 最後,將應用程式以jar檔案的形式部署到JBoss應用程式伺服器上。如果JBoss應用程式伺服器尚未啟動,它將自動啟動。 |
| 5 | 現在建立EJB客戶端,一個基於控制檯的應用程式,其方式與EJB - 建立應用程式章節中主題建立訪問EJB的客戶端中所述相同。 |
建立/修改Book表
CREATE TABLE book ( id integer PRIMARY KEY, name varchar(50) ); Alter table book add image bytea; Alter table book add xml text;
EJBComponent(EJB模組)
Book.java
package com.tutorialspoint.entity;
import com.tutorialspoint.callback.BookCallbackListener;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
@Entity
@Table(name="book")
public class Book implements Serializable{
private int id;
private String name;
private byte[] image;
private String xml;
public Book() {
}
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Lob @Basic(fetch= FetchType.EAGER)
public byte[] getImage() {
return image;
}
public void setImage(byte[] image) {
this.image = image;
}
@Lob @Basic(fetch= FetchType.EAGER)
public String getXml() {
return xml;
}
public void setXml(String xml) {
this.xml = xml;
}
}
LibraryPersistentBeanRemote.java
package com.tutorialspoint.stateless;
import com.tutorialspoint.entity.Book;
import java.util.List;
import javax.ejb.Remote;
@Remote
public interface LibraryPersistentBeanRemote {
void addBook(Book bookName);
List<Book> getBooks();
}
LibraryPersistentBean.java
package com.tutorialspoint.stateless;
import com.tutorialspoint.entity.Book;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Stateless
public class LibraryPersistentBean implements LibraryPersistentBeanRemote {
public LibraryPersistentBean() {
}
@PersistenceContext(unitName="EjbComponentPU")
private EntityManager entityManager;
public void addBook(Book book) {
entityManager.persist(book);
}
public List<Book> getBooks() {
return entityManager.createQuery("From Book").getResultList();
}
}
一旦您在JBOSS上部署了EjbComponent專案,請注意jboss日誌。
JBoss已自動為我們的會話Bean建立了一個JNDI條目 - LibraryPersistentBean/remote。
我們將使用此查詢字串獲取型別為com.tutorialspoint.interceptor.LibraryPersistentBeanRemote的遠端業務物件。
JBoss 應用程式伺服器日誌輸出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.interceptor.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryPersistentBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.interceptor.LibraryPersistentBeanRemote ejbName: LibraryPersistentBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.interceptor.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客戶端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
這些屬性用於初始化java命名服務的InitialContext物件。
InitialContext物件將用於查詢無狀態會話Bean。
EJBTester.java
package com.tutorialspoint.test;
import com.tutorialspoint.stateful.LibraryBeanRemote;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class EJBTester {
BufferedReader brConsoleReader = null;
Properties props;
InitialContext ctx;
{
props = new Properties();
try {
props.load(new FileInputStream("jndi.properties"));
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ctx = new InitialContext(props);
} catch (NamingException ex) {
ex.printStackTrace();
}
brConsoleReader =
new BufferedReader(new InputStreamReader(System.in));
}
public static void main(String[] args) {
EJBTester ejbTester = new EJBTester();
ejbTester.testBlobClob();
}
private void showGUI() {
System.out.println("**********************");
System.out.println("Welcome to Book Store");
System.out.println("**********************");
System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: ");
}
private void testBlobClob() {
try {
int choice = 1;
LibraryPersistentBeanRemote libraryBean =
(LibraryPersistentBeanRemote)
ctx.lookup("LibraryPersistentBean/remote");
while (choice != 2) {
String bookName;
String publisherName;
String publisherAddress;
showGUI();
String strChoice = brConsoleReader.readLine();
choice = Integer.parseInt(strChoice);
if (choice == 1) {
System.out.print("Enter book name: ");
bookName = brConsoleReader.readLine();
String xml = "<book><name>"+bookName+"</name></book>";
Book book = new Book();
book.setName(bookName);
byte[] imageBytes = {0x32, 0x32,0x32, 0x32,0x32,
0x32,0x32, 0x32,
0x32, 0x32,0x32, 0x32,0x32, 0x32,0x32, 0x32,
0x32, 0x32,0x32, 0x32,0x32, 0x32,0x32, 0x32
};
book.setImage(imageBytes);
book.setXml(xml);
libraryBean.addBook(book);
} else if (choice == 2) {
break;
}
}
List<Book> booksList = libraryBean.getBooks();
System.out.println("Book(s) entered so far: " + booksList.size());
int i = 0;
for (Book book:booksList) {
System.out.println((i+1)+". " + book.getName());
byte[] imageByts = book.getImage();
if(imageByts != null) {
System.out.print("image bytes: [");
for(int j = 0; j < imageByts.length ; j++) {
System.out.print("0x"
+ String.format("%x", imageByts[j]) +" ");
}
System.out.println("]");
}
System.out.println(book.getXml());
i++;
}
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}finally {
try {
if(brConsoleReader !=null) {
brConsoleReader.close();
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}
}
EJBTester執行以下任務。
從jndi.properties載入屬性並初始化InitialContext物件。
在testInterceptedEjb()方法中,使用名稱“LibraryPersistenceBean/remote”進行JNDI查詢,以獲取遠端業務物件(無狀態EJB)。
然後向用戶顯示一個圖書館商店使用者介面,並要求他/她輸入選擇。
如果使用者輸入1,系統會要求輸入書籍名稱,並使用無狀態會話Bean的addBook()方法儲存書籍。會話Bean將書籍儲存到資料庫中。
如果使用者輸入2,系統將使用無狀態會話Bean的getBooks()方法檢索書籍並退出。
執行客戶端以訪問 EJB
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在 Netbeans 控制檯中驗證以下輸出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: learn testing ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. learn testing image bytes: [ 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 ] <book><name>learn testing</name></book> BUILD SUCCESSFUL (total time: 20 seconds)
EJB - 事務
事務是一組工作項,遵循ACID屬性。ACID代表原子性、一致性、隔離性和永續性。
原子性 - 如果任何工作項失敗,則整個單元將被視為失敗。成功意味著所有專案都成功執行。
一致性 - 事務必須使系統保持一致狀態。
隔離性 - 每個事務獨立於任何其他事務執行。
永續性 - 如果事務已執行或提交,則應在系統故障中存活。
EJB容器/伺服器是事務伺服器,負責處理事務上下文傳播和分散式事務。事務可以由容器管理,也可以由Bean程式碼中的自定義程式碼處理。
容器管理的事務 - 在這種型別中,容器管理事務狀態。
Bean管理的事務 - 在這種型別中,開發人員管理事務狀態的生命週期。
容器管理的事務
EJB 3.0指定了事務的以下屬性,EJB容器實現了這些屬性 -
REQUIRED - 指示業務方法必須在事務中執行,否則將為該方法啟動一個新事務。
REQUIRES_NEW - 指示為業務方法啟動一個新事務。
SUPPORTS - 指示業務方法將作為事務的一部分執行。
NOT_SUPPORTED - 指示業務方法不應作為事務的一部分執行。
MANDATORY - 指示業務方法將作為事務的一部分執行,否則將丟擲異常。
NEVER - 指示如果業務方法作為事務的一部分執行,則將丟擲異常。
示例
package com.tutorialspoint.txn.required;
import javax.ejb.*
@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class UserDetailBean implements UserDetailRemote {
private UserDetail;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void createUserDetail() {
//create user details object
}
}
createUserDetail()業務方法使用Required註解設定為Required。
package com.tutorialspoint.txn.required;
import javax.ejb.*
@Stateless
public class UserSessionBean implements UserRemote {
private User;
@EJB
private UserDetailRemote userDetail;
public void createUser() {
//create user
//...
//create user details
userDetail.createUserDetail();
}
}
createUser()業務方法使用createUserDetail()。如果在createUser()呼叫期間發生異常並且未建立User物件,則也不會建立UserDetail物件。
Bean管理的事務
在Bean管理的事務中,可以透過在應用程式級別處理異常來管理事務。
以下是需要考慮的關鍵點 -
開始 - 何時在業務方法中啟動事務。
成功 - 確定事務要提交時的成功場景。
失敗 - 確定事務要回滾時的失敗場景。
示例
package com.tutorialspoint.txn.bmt;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.transaction.UserTransaction;
@Stateless
@TransactionManagement(value=TransactionManagementType.BEAN)
public class AccountBean implements AccountBeanLocal {
@Resource
private UserTransaction userTransaction;
public void transferFund(Account fromAccount, double fund ,
Account toAccount) throws Exception{
try{
userTransaction.begin();
confirmAccountDetail(fromAccount);
withdrawAmount(fromAccount,fund);
confirmAccountDetail(toAccount);
depositAmount(toAccount,fund);
userTransaction.commit();
}catch (InvalidAccountException exception) {
userTransaction.rollback();
}catch (InsufficientFundException exception) {
userTransaction.rollback();
}catch (PaymentException exception) {
userTransaction.rollback();
}
}
private void confirmAccountDetail(Account account)
throws InvalidAccountException {
}
private void withdrawAmount() throws InsufficientFundException {
}
private void depositAmount() throws PaymentException{
}
}
在此示例中,我們使用UserTransaction介面使用userTransaction.begin()方法呼叫來標記事務的開始。我們使用userTransaction.commit()方法標記事務的完成,如果在事務期間發生任何異常,則使用userTransaction.rollback()方法呼叫回滾整個事務。
EJB - 安全性
安全性是任何企業級應用程式的主要關注點。它包括識別訪問應用程式的使用者或系統。根據身份識別,它允許或拒絕訪問應用程式中的資源。EJB容器管理標準安全問題,或者可以對其進行自定義以處理任何特定安全問題。
安全的重要術語
身份驗證 - 這是確保訪問系統或應用程式的使用者已驗證為真實的流程。
授權 - 這是確保經過身份驗證的使用者具有訪問系統資源的正確許可權級別的流程。
使用者 - 使用者代表訪問應用程式的客戶端或系統。
使用者組 - 使用者可能是具有某些許可權的組的一部分,例如管理員組。
使用者角色 - 角色定義使用者擁有的許可權級別或訪問系統資源的許可權。
容器管理的安全
EJB 3.0指定了安全性的以下屬性/註解,EJB容器實現了這些屬性。
DeclareRoles - 指示類將接受宣告的角色。註解應用於類級別。
RolesAllowed - 指示方法可以由指定角色的使用者訪問。可以應用於類級別,這會導致類所有方法都可以由指定角色的使用者訪問。
PermitAll - 指示業務方法對所有人可見。它可以應用於類級別以及方法級別。
DenyAll - 指示業務方法對類或方法級別指定的任何使用者都不可訪問。
示例
package com.tutorialspoint.security.required;
import javax.ejb.*
@Stateless
@DeclareRoles({"student" "librarian"})
public class LibraryBean implements LibraryRemote {
@RolesAllowed({"librarian"})
public void delete(Book book) {
//delete book
}
@PermitAll
public void viewBook(Book book) {
//view book
}
@DenyAll
public void deleteAll() {
//delete all books
}
}
安全配置
在配置檔案中對映角色和使用者組。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 EJB 3.0//EN" "http://www.sun.com/software/appserver/dtds/sun-ejb-jar_3_0-0.dtd">
<ejb-jar>
<security-role-mapping>
<role-name>student</role-name>
<group-name>student-group</group-name>
</security-role-mapping>
<security-role-mapping>
<role-name>librarian</role-name>
<group-name>librarian-group</group-name>
</security-role-mapping>
<enterprise-beans/>
</ejb-jar>
EJB - JNDI 繫結
JNDI代表Java命名和目錄介面。它是一組API和服務介面。基於Java的應用程式使用JNDI進行命名和目錄服務。在EJB的上下文中,有兩個術語。
繫結 - 這指的是為EJB物件分配一個名稱,該名稱可以在以後使用。
查詢 - 這指的是查詢和獲取EJB的物件。
在Jboss中,會話Bean預設情況下以以下格式繫結到JNDI中。
本地 - EJB-name/local
遠端 - EJB-name/remote
如果EJB與<application-name>.ear檔案捆綁在一起,則預設格式如下 -
本地 - application-name/ejb-name/local
遠端 - application-name/ejb-name/remote
預設繫結的示例
請參閱EJB - 建立應用程式章節的JBoss控制檯輸出。
JBoss 應用程式伺服器日誌輸出
... 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibrarySessionBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibrarySessionBean ejbName: LibrarySessionBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibrarySessionBean/remote - EJB3.x Default Remote Business Interface LibrarySessionBean/remote-com.tutorialspoint.stateless.LibrarySessionBeanRemote - EJB3.x Remote Business Interface ...
自定義繫結
以下註解可用於自定義預設JNDI繫結 -
本地 - org.jboss.ejb3.LocalBinding
遠端 - org.jboss.ejb3.RemoteBindings
更新LibrarySessionBean.java。請參閱EJB - 建立應用程式章節。
LibrarySessionBean
package com.tutorialspoint.stateless;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.Stateless;
@Stateless
@LocalBinding(jndiBinding="tutorialsPoint/librarySession")
public class LibrarySessionBean implements LibrarySessionBeanLocal {
List<String> bookShelf;
public LibrarySessionBean() {
bookShelf = new ArrayList<String>();
}
public void addBook(String bookName) {
bookShelf.add(bookName);
}
public List<String> getBooks() {
return bookShelf;
}
}
LibrarySessionBeanLocal
package com.tutorialspoint.stateless;
import java.util.List;
import javax.ejb.Local;
@Local
public interface LibrarySessionBeanLocal {
void addBook(String bookName);
List getBooks();
}
構建專案,將應用程式部署到Jboss,並在Jboss控制檯中驗證以下輸出 -
... 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibrarySessionBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibrarySessionBean ejbName: LibrarySessionBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: tutorialsPoint/librarySession - EJB3.x Default Local Business Interface tutorialsPoint/librarySession-com.tutorialspoint.stateless.LibrarySessionBeanLocal - EJB3.x Local Business Interface ...
EJB - 實體關係
EJB 3.0提供選項定義資料庫實體關係/對映,如一對一、一對多、多對一和多對多關係。
以下是相關的註解 -
一對一 − 物件之間存在一對一的關係。例如,乘客一次只能使用一張票出行。
一對多 − 物件之間存在一對多的關係。例如,一位父親可以有多個孩子。
多對一 − 物件之間存在多對一的關係。例如,多個孩子共有一個母親。
多對多 − 物件之間存在多對多的關係。例如,一本書可以有多個作者,一個作者可以寫多本書。
這裡我們將演示多對多對映的使用。為了表示多對多的關係,需要以下三個表:
Book − 書籍表,包含書籍的記錄。
Author − 作者表,包含作者的記錄。
Book_Author − 書籍作者表,包含上述書籍表和作者表的關聯關係。
建立表
在預設資料庫postgres中建立book、author和book_author表。
CREATE TABLE book ( book_id integer, name varchar(50) ); CREATE TABLE author ( author_id integer, name varchar(50) ); CREATE TABLE book_author ( book_id integer, author_id integer );
建立實體類
@Entity
@Table(name="author")
public class Author implements Serializable{
private int id;
private String name;
...
}
@Entity
@Table(name="book")
public class Book implements Serializable{
private int id;
private String title;
private Set<Author> authors;
...
}
在Book實體中使用ManyToMany註解。
@Entity
public class Book implements Serializable{
...
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}
, fetch = FetchType.EAGER)
@JoinTable(table = @Table(name = "book_author"),
joinColumns = {@JoinColumn(name = "book_id")},
inverseJoinColumns = {@JoinColumn(name = "author_id")})
public Set<Author> getAuthors() {
return authors;
}
...
}
示例應用程式
讓我們建立一個測試EJB應用程式,以測試EJB 3.0中實體關係物件。
| 步驟 | 描述 |
|---|---|
| 1 | 在EJB - 建立應用程式章節中說明的com.tutorialspoint.entity包下,建立一個名為EjbComponent的專案。請在本節中使用EJB - 永續性章節中建立的專案,以便了解EJB概念中的嵌入物件。 |
| 2 | 在com.tutorialspoint.entity包下建立Author.java檔案,如EJB - 建立應用程式章節中所述。保持其他檔案不變。 |
| 3 | 在com.tutorialspoint.entity包下建立Book.java。使用EJB - 永續性章節作為參考。保持其餘檔案不變。 |
| 4 | 清理並構建應用程式,以確保業務邏輯按要求工作。 |
| 5 | 最後,將應用程式以jar檔案的形式部署到JBoss應用伺服器上。如果JBoss應用伺服器尚未啟動,它將自動啟動。 |
| 6 | 現在建立EJB客戶端,一個基於控制檯的應用程式,其方式與EJB - 建立應用程式章節中主題建立訪問EJB的客戶端中所述相同。 |
EJBComponent(EJB模組)
Author.java
package com.tutorialspoint.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="author")
public class Author implements Serializable{
private int id;
private String name;
public Author() {}
public Author(int id, String name) {
this.id = id;
this.name = name;
}
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="author_id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return id + "," + name;
}
}
Book.java
package com.tutorialspoint.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
@Entity
@Table(name="book")
public class Book implements Serializable{
private int id;
private String name;
private Set<Author> authors;
public Book() {
}
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="book_id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setAuthors(Set<Author> authors) {
this.authors = authors;
}
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}
, fetch = FetchType.EAGER)
@JoinTable(table = @Table(name = "book_author"),
joinColumns = {@JoinColumn(name = "book_id")},
inverseJoinColumns = {@JoinColumn(name = "author_id")})
public Set<Author> getAuthors() {
return authors;
}
}
LibraryPersistentBeanRemote.java
package com.tutorialspoint.stateless;
import com.tutorialspoint.entity.Book;
import java.util.List;
import javax.ejb.Remote;
@Remote
public interface LibraryPersistentBeanRemote {
void addBook(Book bookName);
List<Book> getBooks();
}
LibraryPersistentBean.java
package com.tutorialspoint.stateless;
import com.tutorialspoint.entity.Book;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Stateless
public class LibraryPersistentBean implements LibraryPersistentBeanRemote {
public LibraryPersistentBean() {
}
@PersistenceContext(unitName="EjbComponentPU")
private EntityManager entityManager;
public void addBook(Book book) {
entityManager.persist(book);
}
public List<Book> getBooks() {
return entityManager.createQuery("From Book").getResultList();
}
}
一旦您在JBOSS上部署了EjbComponent專案,請注意jboss日誌。
JBoss已自動為我們的會話Bean建立了一個JNDI條目 - LibraryPersistentBean/remote。
我們將使用此查詢字串獲取型別為com.tutorialspoint.interceptor.LibraryPersistentBeanRemote的遠端業務物件。
JBoss 應用程式伺服器日誌輸出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.interceptor.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryPersistentBean,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.interceptor.LibraryPersistentBeanRemote ejbName: LibraryPersistentBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.interceptor.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客戶端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
這些屬性用於初始化java命名服務的InitialContext物件。
InitialContext物件將用於查詢無狀態會話Bean。
EJBTester.java
package com.tutorialspoint.test;
import com.tutorialspoint.stateful.LibraryBeanRemote;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class EJBTester {
BufferedReader brConsoleReader = null;
Properties props;
InitialContext ctx;
{
props = new Properties();
try {
props.load(new FileInputStream("jndi.properties"));
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ctx = new InitialContext(props);
} catch (NamingException ex) {
ex.printStackTrace();
}
brConsoleReader =
new BufferedReader(new InputStreamReader(System.in));
}
public static void main(String[] args) {
EJBTester ejbTester = new EJBTester();
ejbTester.testEmbeddedObjects();
}
private void showGUI() {
System.out.println("**********************");
System.out.println("Welcome to Book Store");
System.out.println("**********************");
System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: ");
}
private void testEmbeddedObjects() {
try {
int choice = 1;
LibraryPersistentBeanRemote libraryBean =
(LibraryPersistentBeanRemote)
ctx.lookup("LibraryPersistentBean/remote");
while (choice != 2) {
String bookName;
String authorName;
showGUI();
String strChoice = brConsoleReader.readLine();
choice = Integer.parseInt(strChoice);
if (choice == 1) {
System.out.print("Enter book name: ");
bookName = brConsoleReader.readLine();
System.out.print("Enter author name: ");
authorName = brConsoleReader.readLine();
Book book = new Book();
book.setName(bookName);
Author author = new Author();
author.setName(authorName);
Set<Author> authors = new HashSet<Author>();
authors.add(author);
book.setAuthors(authors);
libraryBean.addBook(book);
} else if (choice == 2) {
break;
}
}
List<Book> booksList = libraryBean.getBooks();
System.out.println("Book(s) entered so far: " + booksList.size());
int i = 0;
for (Book book:booksList) {
System.out.println((i+1)+". " + book.getName());
System.out.print("Author: ");
Author[] authors = (Author[])books.getAuthors().toArray();
for(int j=0;j<authors.length;j++) {
System.out.println(authors[j]);
}
i++;
}
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}finally {
try {
if(brConsoleReader !=null) {
brConsoleReader.close();
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}
}
EJBTester執行以下任務:
從jndi.properties載入屬性並初始化InitialContext物件。
在testInterceptedEjb()方法中,使用名稱“LibraryPersistenceBean/remote”進行JNDI查詢,以獲取遠端業務物件(無狀態EJB)。
然後向用戶顯示一個圖書館商店使用者介面,並要求他/她輸入選擇。
如果使用者輸入1,系統會要求輸入書籍名稱,並使用無狀態會話Bean的addBook()方法儲存書籍。會話Bean將書籍儲存到資料庫中。
如果使用者輸入2,系統將使用無狀態會話Bean的getBooks()方法檢索書籍並退出。
執行客戶端以訪問 EJB
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在 Netbeans 控制檯中驗證以下輸出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: learn html5 Enter Author name: Robert ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. learn html5 Author: Robert BUILD SUCCESSFUL (total time: 21 seconds)
EJB - 訪問資料庫
在EJB 3.0中,持久化機制用於訪問資料庫,其中容器管理資料庫相關操作。開發人員可以直接在EJB業務方法中使用JDBC API呼叫訪問資料庫。
為了演示EJB中的資料庫訪問,我們需要執行以下任務:
步驟1 − 在資料庫中建立一個表。
步驟2 − 建立一個包含業務方法的無狀態EJB。
步驟3 − 更新無狀態EJB。新增方法,透過實體管理器向資料庫新增記錄和獲取記錄。
步驟4 − 一個基於控制檯的應用程式客戶端將訪問無狀態EJB,以將資料持久化到資料庫中。
建立表
在預設資料庫postgres中建立一個名為books的表。
CREATE TABLE books ( id integer PRIMARY KEY, name varchar(50) );
建立模型類
public class Book implements Serializable{
private int id;
private String name;
public Book() {
}
public int getId() {
return id;
}
...
}
建立無狀態EJB
@Stateless
public class LibraryPersistentBean implements LibraryPersistentBeanRemote {
public void addBook(Book book) {
//persist book using jdbc calls
}
public List<Book> getBooks() {
//get books using jdbc calls
}
...
}
構建EJB模組後,我們需要一個客戶端來訪問無狀態Bean,我們將在下一節中建立。
示例應用程式
讓我們建立一個測試EJB應用程式,以測試EJB資料庫訪問機制。
| 步驟 | 描述 |
|---|---|
| 1 | 在com.tutorialspoint.entity包下建立一個名為EjbComponent的專案,如EJB - 建立應用程式章節中所述。您也可以使用EJB - 建立應用程式章節中建立的專案,以便理解EJB資料訪問的概念。 |
| 2 | 在包com.tutorialspoint.entity下建立Book.java,並按如下所示修改它。 |
| 3 | 建立LibraryPersistentBean.java和LibraryPersistentBeanRemote,如EJB - 建立應用程式章節中所述,並按如下所示修改它們。 |
| 4 | 清理並構建應用程式,以確保業務邏輯按要求工作。 |
| 5 | 最後,將應用程式以jar檔案的形式部署到JBoss應用伺服器上。如果JBoss應用伺服器尚未啟動,它將自動啟動。 |
| 6 | 現在建立EJB客戶端,一個基於控制檯的應用程式,其方式與EJB - 建立應用程式章節中主題建立訪問EJB的客戶端中所述相同。按如下所示修改它。 |
EJBComponent(EJB模組)
Book.java
package com.tutorialspoint.entity;
import java.io.Serializable;
public class Book implements Serializable{
private int id;
private String name;
public Book() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
LibraryPersistentBeanRemote.java
package com.tutorialspoint.stateless;
import com.tutorialspoint.entity.Book;
import java.util.List;
import javax.ejb.Remote;
@Remote
public interface LibraryPersistentBeanRemote {
void addBook(Book bookName);
List<Book> getBooks();
}
LibraryPersistentBean.java
package com.tutorialspoint.stateless;
import com.tutorialspoint.entity.Book;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.Stateless;
@Stateless
public class LibraryPersistentBean implements LibraryPersistentBeanRemote {
public LibraryPersistentBean() {
}
public void addBook(Book book) {
Connection con = null;
String url = "jdbc:postgresql://: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://:5432/postgres";
String driver = "org.postgresql.driver";
String userName = "sa";
String password = "sa";
List<Book> books = new ArrayList<Book>();
try {
Class.forName(driver).newInstance();
con = DriverManager.getConnection(url , userName, password);
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select * from book");
Book book;
while (rs.next()) {
book = new Book();
book.setId(rs.getInt(1));
book.setName(rs.getString(2));
books.add(book);
}
} catch (SQLException ex) {
ex.printStackTrace();
} catch (InstantiationException ex) {
ex.printStackTrace();
} catch (IllegalAccessException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
return books;
}
}
一旦您在JBOSS上部署了EjbComponent專案,請注意jboss日誌。
JBoss已自動為我們的會話Bean建立了一個JNDI條目 - LibraryPersistentBean/remote。
我們將使用此查詢字串獲取型別為com.tutorialspoint.stateless.LibraryPersistentBeanRemote的遠端業務物件。
JBoss 應用程式伺服器日誌輸出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.stateless.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryPersistentBeanRemote,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibraryPersistentBeanRemote ejbName: LibraryPersistentBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.stateless.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客戶端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
這些屬性用於初始化java命名服務的InitialContext物件。
InitialContext物件將用於查詢無狀態會話Bean。
EJBTester.java
package com.tutorialspoint.test;
import com.tutorialspoint.stateless.LibraryPersistentBeanRemote;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class EJBTester {
BufferedReader brConsoleReader = null;
Properties props;
InitialContext ctx;
{
props = new Properties();
try {
props.load(new FileInputStream("jndi.properties"));
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ctx = new InitialContext(props);
} catch (NamingException ex) {
ex.printStackTrace();
}
brConsoleReader =
new BufferedReader(new InputStreamReader(System.in));
}
public static void main(String[] args) {
EJBTester ejbTester = new EJBTester();
ejbTester.testEntityEjb();
}
private void showGUI() {
System.out.println("**********************");
System.out.println("Welcome to Book Store");
System.out.println("**********************");
System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: ");
}
private void testEntityEjb() {
try {
int choice = 1;
LibraryPersistentBeanRemote libraryBean =
LibraryPersistentBeanRemote)
ctx.lookup("LibraryPersistentBean/remote");
while (choice != 2) {
String bookName;
showGUI();
String strChoice = brConsoleReader.readLine();
choice = Integer.parseInt(strChoice);
if (choice == 1) {
System.out.print("Enter book name: ");
bookName = brConsoleReader.readLine();
Book book = new Book();
book.setName(bookName);
libraryBean.addBook(book);
} else if (choice == 2) {
break;
}
}
List<Book> booksList = libraryBean.getBooks();
System.out.println("Book(s) entered so far: " + booksList.size());
int i = 0;
for (Book book:booksList) {
System.out.println((i+1)+". " + book.getName());
i++;
}
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}finally {
try {
if(brConsoleReader !=null) {
brConsoleReader.close();
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}
}
EJBTester執行以下任務:
從jndi.properties載入屬性並初始化InitialContext物件。
在testStatefulEjb()方法中,使用名稱“LibraryStatelessSessionBean/remote”進行JNDI查詢,以獲取遠端業務物件(有狀態EJB)。
然後向用戶顯示圖書館商店使用者介面,並提示使用者輸入選擇。
如果使用者輸入1,系統將提示輸入書籍名稱,並使用無狀態會話Bean的addBook()方法儲存書籍。會話Bean透過EntityManager呼叫將書籍持久化到資料庫中。
如果使用者輸入2,系統將使用無狀態會話Bean的getBooks()方法檢索書籍並退出。
然後,再次使用名稱“LibraryStatelessSessionBean/remote”進行JNDI查詢,以獲取遠端業務物件(有狀態EJB),並列出書籍。
執行客戶端以訪問 EJB
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在 Netbeans 控制檯中驗證以下輸出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Java ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. learn java BUILD SUCCESSFUL (total time: 15 seconds)
EJB - 查詢語言
EJB查詢語言非常方便編寫自定義查詢,而無需擔心底層資料庫的細節。它與HQL(Hibernate查詢語言)非常相似,通常被稱為EJBQL。
為了演示EJB中EJBQL的使用,我們將執行以下任務:
步驟1 - 在資料庫中建立表。
步驟2 − 建立一個包含業務方法的無狀態EJB。
步驟3 − 更新無狀態EJB。新增方法,透過實體管理器向資料庫新增記錄和獲取記錄。
步驟4 − 一個基於控制檯的應用程式客戶端將訪問無狀態EJB,以將資料持久化到資料庫中。
建立表
在預設資料庫postgres中建立一個名為books的表。
CREATE TABLE books ( id integer PRIMARY KEY, name varchar(50) );
建立模型類
public class Book implements Serializable{
private int id;
private String name;
public Book() {
}
public int getId() {
return id;
}
...
}
建立無狀態EJB
@Stateless
public class LibraryPersistentBean implements LibraryPersistentBeanRemote {
public void addBook(Book book) {
//persist book using entity manager
}
public List<Book> getBooks() {
//get books using entity manager
}
...
}
構建EJB模組後,我們需要一個客戶端來訪問無狀態Bean,我們將在下一節中建立。
示例應用程式
讓我們建立一個測試EJB應用程式,以測試EJB資料庫訪問機制。
| 步驟 | 描述 |
|---|---|
| 1 | 在com.tutorialspoint.entity包下建立一個名為EjbComponent的專案,如EJB - 建立應用程式章節中所述。您也可以使用EJB - 建立應用程式章節中建立的專案,以便理解EJB資料訪問的概念。 |
| 2 | 在包com.tutorialspoint.entity下建立Book.java,並按如下所示修改它。 |
| 3 | 建立LibraryPersistentBean.java和LibraryPersistentBeanRemote,如EJB - 建立應用程式章節中所述,並按如下所示修改它們。 |
| 4 | 清理並構建應用程式,以確保業務邏輯按要求工作。 |
| 5 | 最後,將應用程式以jar檔案的形式部署到JBoss應用伺服器上。如果JBoss應用伺服器尚未啟動,它將自動啟動。 |
| 6 | 現在建立EJB客戶端,一個基於控制檯的應用程式,其方式與EJB - 建立應用程式章節中主題建立訪問EJB的客戶端中所述相同。按如下所示修改它。 |
EJBComponent(EJB模組)
Book.java
package com.tutorialspoint.entity;
import java.io.Serializable;
public class Book implements Serializable{
private int id;
private String name;
public Book() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
LibraryPersistentBeanRemote.java
package com.tutorialspoint.stateless;
import com.tutorialspoint.entity.Book;
import java.util.List;
import javax.ejb.Remote;
@Remote
public interface LibraryPersistentBeanRemote {
void addBook(Book bookName);
List<Book> getBooks();
}
LibraryPersistentBean.java
package com.tutorialspoint.stateless;
import com.tutorialspoint.entity.Book;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
@Stateless
public class LibraryPersistentBean implements LibraryPersistentBeanRemote {
public LibraryPersistentBean() {
}
@PersistenceContext(unitName="EntityEjbPU")
private EntityManager entityManager;
public void addBook(Book book) {
entityManager.persist(book);
}
public List<Book> getBooks() {
//create an ejbql expression
String ejbQL = "From Book b where b.name like ?1";
//create query
Query query = entityManager.createQuery(ejbQL);
//substitute parameter.
query.setParameter(1, "%test%");
//execute the query
return query.getResultList();
}
}
一旦您在JBOSS上部署了EjbComponent專案,請注意jboss日誌。
JBoss已自動為我們的會話Bean建立了一個JNDI條目 - LibraryPersistentBean/remote。
我們將使用此查詢字串獲取型別為com.tutorialspoint.stateless.LibraryPersistentBeanRemote的遠端業務物件。
JBoss 應用程式伺服器日誌輸出
... 16:30:01,401 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.stateless.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface 16:30:02,723 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=EjbComponent.jar,name=LibraryPersistentBeanRemote,service=EJB3 16:30:02,723 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibraryPersistentBeanRemote ejbName: LibraryPersistentBean 16:30:02,731 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.stateless.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface ...
EJBTester(EJB客戶端)
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost
這些屬性用於初始化java命名服務的InitialContext物件。
InitialContext物件將用於查詢無狀態會話Bean。
EJBTester.java
package com.tutorialspoint.test;
import com.tutorialspoint.stateless.LibraryPersistentBeanRemote;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class EJBTester {
BufferedReader brConsoleReader = null;
Properties props;
InitialContext ctx;
{
props = new Properties();
try {
props.load(new FileInputStream("jndi.properties"));
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ctx = new InitialContext(props);
} catch (NamingException ex) {
ex.printStackTrace();
}
brConsoleReader =
new BufferedReader(new InputStreamReader(System.in));
}
public static void main(String[] args) {
EJBTester ejbTester = new EJBTester();
ejbTester.testEntityEjb();
}
private void showGUI() {
System.out.println("**********************");
System.out.println("Welcome to Book Store");
System.out.println("**********************");
System.out.print("Options \n1. Add Book\n2. Exit \nEnter Choice: ");
}
private void testEntityEjb() {
try {
int choice = 1;
LibraryPersistentBeanRemote libraryBean =
LibraryPersistentBeanRemote)
ctx.lookup("LibraryPersistentBean/remote");
while (choice != 2) {
String bookName;
showGUI();
String strChoice = brConsoleReader.readLine();
choice = Integer.parseInt(strChoice);
if (choice == 1) {
System.out.print("Enter book name: ");
bookName = brConsoleReader.readLine();
Book book = new Book();
book.setName(bookName);
libraryBean.addBook(book);
} else if (choice == 2) {
break;
}
}
List<Book> booksList = libraryBean.getBooks();
System.out.println("Book(s) entered so far: " + booksList.size());
int i = 0;
for (Book book:booksList) {
System.out.println((i+1)+". " + book.getName());
i++;
}
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}finally {
try {
if(brConsoleReader !=null) {
brConsoleReader.close();
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}
}
EJBTester執行以下任務:
從jndi.properties載入屬性並初始化InitialContext物件。
在testStatefulEjb()方法中,使用名稱“LibraryStatelessSessionBean/remote”進行JNDI查詢,以獲取遠端業務物件(有狀態EJB)。
然後向用戶顯示圖書館商店使用者介面,並提示使用者輸入選擇。
如果使用者輸入1,系統將提示輸入書籍名稱,並使用無狀態會話Bean的addBook()方法儲存書籍。會話Bean透過EntityManager呼叫將書籍持久化到資料庫中。
如果使用者輸入2,系統將使用無狀態會話Bean的getBooks()方法檢索書籍並退出。
然後,再次使用名稱“LibraryStatelessSessionBean/remote”進行JNDI查詢,以獲取遠端業務物件(有狀態EJB),並列出書籍。
執行客戶端以訪問 EJB
在專案瀏覽器中找到 EJBTester.java。右鍵單擊 EJBTester 類並選擇執行檔案。
在 Netbeans 控制檯中驗證以下輸出。
run: ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 1 Enter book name: Learn Testing ********************** Welcome to Book Store ********************** Options 1. Add Book 2. Exit Enter Choice: 2 Book(s) entered so far: 1 1. learn Testing BUILD SUCCESSFUL (total time: 15 seconds)
EJB - 異常處理
EJB是企業應用程式的一部分,這些應用程式通常基於分散式環境。因此,除了可能發生的普通異常之外,還可能發生諸如通訊失敗、安全許可權、伺服器宕機等異常。
EJB容器以兩種方式考慮異常:
應用程式異常 − 如果違反業務規則或在執行業務邏輯時發生異常。
系統異常 − 任何不是由業務邏輯或業務程式碼引起的異常。RuntimeException、RemoteException都是SystemException。例如,EJB查詢期間發生錯誤。RuntimeException、RemoteException都是SystemException。
EJB容器如何處理異常?
當發生應用程式異常時,EJB容器會攔截異常,但將其原樣返回給客戶端。它不會回滾事務,除非在程式碼中透過EJBContext.setRollBackOnly()方法指定。在應用程式異常的情況下,EJB容器不會包裝異常。
當發生系統異常時,EJB容器會攔截異常,回滾事務並開始清理任務。它將異常包裝到RemoteException中,並將其拋給客戶端。
處理應用程式異常
應用程式異常通常在會話EJB方法中丟擲,因為這些方法負責執行業務邏輯。應用程式異常應在業務方法的throws子句中宣告,並在業務邏輯失敗時丟擲。
@Stateless
public class LibraryPersistentBean implements LibraryPersistentBeanRemote {
...
public List<Book> getBooks() throws NoBookAvailableException {
List<Book> books =
entityManager.createQuery("From Books").getResultList();
if(books.size == 0)
throw NoBookAvailableException
("No Book available in library.");
return books;
}
...
}
處理系統異常
系統異常可能隨時發生,例如命名查詢失敗、獲取資料時發生SQL錯誤。在這種情況下,此類異常應包裝在EJBException中,並拋回給客戶端。
@Stateless
public class LibraryPersistentBean implements LibraryPersistentBeanRemote {
...
public List<Book> getBooks() {
try {
List<Book> books =
entityManager.createQuery("From Books").getResultList();
} catch (CreateException ce) {
throw (EJBException) new EJBException(ce).initCause(ce);
} catch (SqlException se) {
throw (EJBException) new EJBException(se).initCause(se);
}
return books;
}
...
}
在客戶端,處理EJBException。
public class EJBTester {
private void testEntityEjb() {
...
try{
LibraryPersistentBeanRemote libraryBean =
LibraryPersistentBeanRemote)ctx.lookup("LibraryPersistentBean/remote");
List<Book> booksList = libraryBean.getBooks();
} catch(EJBException e) {
Exception ne = (Exception) e.getCause();
if(ne.getClass().getName().equals("SqlException")) {
System.out.println("Database error: "+ e.getMessage());
}
}
...
}
}
EJB - Web 服務
EJB 3.0提供了一個選項,可以將會話EJB公開為Web服務。@WebService註解用於將類標記為Web服務端點,@WebMethod用於將方法公開為客戶端的Web方法。
@Stateless
@WebService(serviceName="LibraryService")
public class LibraryPersistentBean implements LibraryPersistentBeanRemote {
...
@WebMethod(operationName="getBooks")
public List<Book> getBooks() {
return entityManager.createQuery("From Books").getResultList();
}
...
}
示例應用程式
讓我們建立一個測試EJB應用程式來測試EJB 3.0中的blob/clob支援。
| 步驟 | 描述 |
|---|---|
| 1 | 在com.tutorialspoint.entity包下建立一個名為EjbComponent的專案,如EJB - 建立應用程式章節中所述。請使用EJB - 持久化章節中建立的專案,以便理解EJB概念中的clob/blob物件。 |
| 2 | 在com.tutorialspoint.stateless包下建立LibraryPersistentBean.java。使用EJB - 持久化章節作為參考。保持其他檔案不變。 |
| 3 | 清理並構建應用程式,以確保業務邏輯按要求工作。 |
| 4 | 最後,將應用程式以jar檔案的形式部署到JBoss應用伺服器上。如果JBoss應用伺服器尚未啟動,它將自動啟動。 |
LibraryPersistentBean.java
package com.tutorialspoint.stateless;
import com.tutorialspoint.entity.Book;
import java.util.List;
import javax.ejb.Stateless;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Stateless
@WebService(serviceName="LibraryService")
public class LibraryPersistentBean implements LibraryPersistentBeanRemote {
public LibraryPersistentBean() {
}
@PersistenceContext(unitName="EjbComponentPU")
private EntityManager entityManager;
public void addBook(Book book) {
entityManager.persist(book);
}
@WebMethod(operationName="getBooks")
public List <Book> getBooks() {
return entityManager.createQuery("From Book").getResultList();
}
}
JBoss 應用程式伺服器日誌輸出
10:51:37,271 INFO [EJBContainer] STARTED EJB: com.tutorialspoint.stateless.LibraryPersistentBean ejbName: LibraryPersistentBean 10:51:37,287 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI: LibraryPersistentBean/remote - EJB3.x Default Remote Business Interface LibraryPersistentBean/remote-com.tutorialspoint.stateless.LibraryPersistentBeanRemote - EJB3.x Remote Business Interface 10:51:37,349 INFO [EJBContainer] STARTED EJB: com.tuturialspoint.messagebean.LibraryMessageBean ejbName: BookMessageHandler 10:51:37,443 INFO [DefaultEndpointRegistry] register: jboss.ws:context=EjbComponent,endpoint=LibraryPersistentBean 10:51:38,191 INFO [WSDLFilePublisher] WSDL published to: file:/D:/Jboss-5.0.1/server/default/data/wsdl/EjbComponent.jar/ LibraryService3853081455302946642.wsdl
建立客戶端以訪問EJB作為Web服務
在NetBeans IDE中,選擇檔案 > 新建專案 >。在類別下選擇專案型別Java,專案型別為Java應用程式。單擊下一步 >按鈕。輸入專案名稱和位置。單擊完成 >按鈕。我們選擇名稱為EJBWebServiceClient。
在專案資源管理器視窗中,右鍵單擊專案名稱。選擇新建 > Web服務客戶端。
使用新增專案按鈕在編譯選項卡中新增前面建立的EJB元件專案的LibraryPersistentBean,以及WSDL和客戶端位置。
單擊完成按鈕。在專案資源管理器中驗證以下結構。
建立EJBWebServiceClient.java
package ejbwebserviceclient;
public class EJBWebServiceClient {
public static void main(String[] args) {
}
}
選擇Web服務getBooks Web方法,如下圖所示,並將其拖動到EJBWebServiceClient的程式碼視窗。
您將看到類似於以下所示的輸出。
更新EJBWebServiceClient程式碼以使用此方法。
package ejbwebserviceclient;
public class EJBWebServiceClient {
public static void main(String[] args) {
for(com.tutorialspoint.stateless.Book book:getBooks()) {
System.out.println(book.getName());
}
}
private static java.util.List
<com.tutorialspoint.stateless.Book> getBooks() {
com.tutorialspoint.stateless.LibraryService service =
new com.tutorialspoint.stateless.LibraryService();
com.tutorialspoint.stateless.LibraryPersistentBean port =
service.getLibraryPersistentBeanPort();
return port.getBooks();
}
}
執行客戶端
在專案資源管理器視窗中,右鍵單擊專案名稱。選擇執行。Netbeans將構建客戶端並執行它。驗證以下輸出。
ant -f D:\\SVN\\EJBWebServiceClient run init: Deleting: D:\SVN\EJBWebServiceClient\build\built-jar.properties deps-jar: Updating property file: D:\SVN\EJBWebServiceClient\build\built-jar.properties wsimport-init: wsimport-client-LibraryPersistentBean: files are up to date classLoader = java.net.URLClassLoader@4ce46c SharedSecrets.getJavaNetAccess()=java.net.URLClassLoader$7@182cdac wsimport-client-generate: Compiling 1 source file to D:\SVN\EJBWebServiceClient\build\classes compile: run: learn java Learn Spring learn JSF Learn HTML Learn JBoss Learn EJB Learn Hibernate Learn IBatis Times Now learn html5 Learn images Learn Testing Forbes test1 BUILD SUCCESSFUL (total time: 1 second)
EJB - 打包應用程式
使用EJB 3.0打包應用程式的要求類似於J2EE平臺的要求。EJB元件打包到模組中作為jar檔案,並打包到應用程式企業歸檔中作為ear檔案。
任何企業應用程式主要有三個元件:
jar − Java應用程式歸檔,包含EJB模組、EJB客戶端模組和實用程式模組。
war − Web應用程式歸檔,包含Web模組。
ear − 企業應用程式歸檔,包含jar和war模組。
在NetBeans中,建立、開發、打包和部署J2EE應用程式非常容易。
在NetBeans IDE中,選擇檔案 > 新建專案 >。在類別下選擇專案型別Java EE,專案型別為企業應用程式。單擊下一步 >按鈕。輸入專案名稱和位置。單擊完成 >按鈕。我們選擇名稱為EnterpriseApplicaton。
選擇伺服器和設定。保持建立EJB模組和建立Web應用程式模組選中,並使用提供的預設名稱。單擊完成按鈕。NetBeans將在專案視窗中建立以下結構。
在專案資源管理器中,右鍵單擊專案企業應用程式,然後選擇構建。
ant -f D:\\SVN\\EnterpriseApplication dist pre-init: init-private: init-userdir: init-user: init-project: do-init: post-init: init-check: init: deps-jar: deps-j2ee-archive: EnterpriseApplication-ejb.init: EnterpriseApplication-ejb.deps-jar: EnterpriseApplication-ejb.compile: EnterpriseApplication-ejb.library-inclusion-in-manifest: Building jar: D:\SVN\EnterpriseApplication\EnterpriseApplication-ejb\dist\EnterpriseApplication-ejb.jar EnterpriseApplication-ejb.dist-ear: EnterpriseApplication-war.init: EnterpriseApplication-war.deps-module-jar: EnterpriseApplication-war.deps-ear-jar: EnterpriseApplication-ejb.init: EnterpriseApplication-ejb.deps-jar: EnterpriseApplication-ejb.compile: EnterpriseApplication-ejb.library-inclusion-in-manifest: EnterpriseApplication-ejb.dist-ear: EnterpriseApplication-war.deps-jar: EnterpriseApplication-war.library-inclusion-in-archive: EnterpriseApplication-war.library-inclusion-in-manifest: EnterpriseApplication-war.compile: EnterpriseApplication-war.compile-jsps: EnterpriseApplication-war.do-ear-dist: Building jar: D:\SVN\EnterpriseApplication\EnterpriseApplication-war\dist\EnterpriseApplication-war.war EnterpriseApplication-war.dist-ear: pre-pre-compile: pre-compile: Copying 1 file to D:\SVN\EnterpriseApplication\build Copying 1 file to D:\SVN\EnterpriseApplication\build do-compile: post-compile: compile: pre-dist: do-dist-without-manifest: do-dist-with-manifest: Building jar: D:\SVN\EnterpriseApplication\dist\EnterpriseApplication.ear post-dist: dist: BUILD SUCCESSFUL (total time: 1 second)
在這裡您可以看到,Netbeans首先準備Jar,然後是War,最後是包含Jar和War檔案的ear檔案。每個jar、war和ear檔案都包含一個meta-inf資料夾,以根據J2EE規範儲存元資料。