用 JOTM 向Servlet中添加事务
生活随笔
收集整理的這篇文章主要介紹了
用 JOTM 向Servlet中添加事务
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
J2EE 除了提供了 servlet 之外,還提供了大量的其它功能。Servlet 開發者們也許難得使用這些功能,不情愿也沒有時間用一個超出所需的大型 J2EE 服務器來替換自己的簡單的 servlet。然而,依據J2EE 的模塊化特征,有可能將負責特定 J2EE 功能的小組件整合到 servlet 容器里,以此來增強 WEB 應用程序。其中之一就是事務。有關 J2EE 事務的完整描述,您可以參考Onjava上的其他三篇文章,現在只需知道事務是資源的操作步驟(例如:數據庫),它由四個屬性定義,這四個屬性根據其首字母濃縮為 ACID: 原子性:事務的操作,或者是全部成功(此時提交事務),或者是全部不成功(此時回滾事務),謂之為 all-or-nothing 屬性。一個事務應該被視為單個工作單元,在一個事務里面絕對不可能同時存在完成了的和沒有完成的操作。 一致性:完成了的事務將資源從一個有效狀態轉變為另一個有效狀態。一致性的具體例子有:數據庫的參照完整性和表中的主鍵唯一性。 獨立性在事務沒有提交之前,事務作用的共享資源的改變在事務之外是不可見的。獨立性確保了不同事務不會同時訪問正在更新的數據。 持久性:由事務提交的改變會永久存在。 JOTM (Java Open Transaction Manager)是由ObjectWeb協會開發的功能完整的且資源開放的獨立的事務管理器。它提供了 JAVA 應用程序的事務支持,而且與 JTA( JAVA 事務 API)兼容。您可以在JOTM home page了解到更多的詳細信息。在 TOMCAT(或其它 Servlet 容器)整合了 JOTM 后,JSP 和 servlet 的開發者們就可以獲得事務的優勢輕而易舉的創建更多健壯的 web 應用程序。 為了突出事務是怎樣增強 web 應用程序的,舉一個常用的例子, web 瀏覽器與客戶端交互的 ATM 。 ATM 樣例: 情景 此例比較簡單:一個客戶想從 ATM 提款,輸入了他的客戶名稱,john_doe;想提款數,$50。如果他的銀行帳戶上有足夠的錢并且在 ATM 機上有足夠的現金的話,應用程序就能給他相當數目的現金,并從銀行帳戶上提出同樣的數目。否則,操作中斷,并且除出現錯誤信息之外,其他都不會改變。我們 無需擔心安全問題,只是在猜想用戶是否正確授權。 這是一個非常簡單的例子,但是如果不使用事務,用別的方法執行起來將會很難。客戶端操作 將會涉及到兩個不同的資源:ATM 和客戶銀行帳號。它們會自動的在應用程序設計中產生 ACID 問題。例如:如果在 ATM 上操作成功而在銀行帳戶上卻失敗(也許是因為交流失敗),客戶將會取到錢,但是他的帳戶將不會更新。對于銀行來說,這就虧大了。更糟的是,如果銀行帳戶更 新了,但是由于一個錯誤阻止 ATM 傳送錢,客戶得不到現金,但是帳戶上卻提掉了這筆款。 為了防止出現上述事故,在你的應用程序里,你能夠 1) 聯系兩個資源,并告知兩者客戶執行的所有當前操作,2) 詢問兩者是否能執行操作,3)如果兩者都同意,則請求操作。即使這樣,此方法也不能謂之足夠健壯,因為,如果客戶帳戶上的錢在第二步和第三步的時候被另外 一操作提走,提款可能會失敗,例如,客戶帳戶不能出現逆差。 事務能使應用程序更簡單更健壯的之處就是:在同一事務的兩個資源上執行所有的操作的時候,它將會解決 ACID 的問題(尤其是原子性)。 應用程序設計 數據層:在數據層,有兩個不同的數據庫,并各自有一張表。為了使例子更接近實際,我們使用兩個不同的數據庫,因為有可能從 ATM 提走不是屬于該客戶帳戶的款(請參見下文配置數據庫)。 banktest 包含代表客戶帳號的 account 表。 atmtest包含代表 ATM 的 atm 表。 邏輯層:在邏輯層,有三個類來訪問資源和執行操作: foo.BankAccount 代表給定客戶的銀行帳號 account,并能通過 JDBC在 account 執行數據庫操作。 bar.ATM 代表 ATM,并在 atm 表上執行 JDBC 操作。 bar.CashDelivery 使用前面兩個類來執行一個客戶操作。 所有邏輯在 CashDelivery.java 的 deliverCash 方法中實現。 javax.transaction.UserTransaction 接口用于劃分事務所有 utx.begin() 和 utx.commit() (或 utx.rollback())之間的操作在同一事務內執行。這確保了應用程序不會受到如前述的遭遇。 事務使得應用程序更為簡單,由以下簡單的步驟組成: 1. 開始事務。 2. 聯系客戶的銀行帳戶并從帳戶上提款。 3. 告訴 ATM 傳送錢。 4. 完成事務:如果所有事件完成,提交事務。否則,回滾事務。 5. 報告客戶事務結果。如果事務成功,現金將被提出,錢數也將從帳戶上提出。否則,一切都不會改變。 例1. CashDelivery.java
表示層:在表示層,就用程序由兩個 JSP 文件組成: atm.jsp, 應用程序,它發送給bar.CashDelivery 類客戶登錄和提款數目,并顯示客戶操作的結果 。 admin.jsp,,用于顯示和更新兩個資源的信息。(它不屬于應用程序設計的部分,但是添加它來簡化資源更新,比如處理客戶帳戶的錢數。) 圖1 應用程序設計 配置數據庫 關于數據庫,建議使用MySQL 4.0.12和相應的 JDBC 驅動程序(見Resources)。默認情況下,MySQL 表不會受影響。為支持事務,表在創建的時候設置為 InnoDB 類型。另外,為啟用 InnoDB 類型,您可以將 MySQL 配置文件內的 #skip-innodb 行注釋掉。 已配置了一個 MySQL 的例子,用戶名為 javauser,密碼為 javadude。確保該用戶已被創建并且擁有創建數據庫的權限。 創建數據庫和表的腳本在 .s/ 目錄下的 example file 內含有。它將創建一個 account 表并插入兩個客戶:john_doe 他的帳戶金額為 $100。jane_doe 他的帳戶金額為 $600。 例2 創建 account 表
腳本還會創建有 $500 可用現金的 atm 表。 例3 創建 atm 表
最后,復制 $CATALINA_HOME/shared/lib 內的 JDBC 驅動程序 .jar 文件。 獲取并安裝 TOMCAT:本章主要介紹 Tomcat 4.1.18 及以上的版本。首先確保沒有使用以前的舊版本,安裝 TOMCAT 沒有什么特別,只需下載并解壓縮即可。 獲取并安裝 JOTM:如果要使用 JOTM,只需要下載最近的二元版本并將解壓縮即可。再從lib/ 目錄下將.jar 文件(除了 log4j.jar、ommons-cli.jar 和 jotm_iiop_stubs.jar) 復制到 $CATALINA_HOME/shared/lib。這樣就完成了。 配置 TOMCAT:需要配置 Tomcat,使之能夠從 JNDI 獲取 UserTransaction 和 DataSource 對象(它們用在 foo.BankAccount 和 bar.ATM)。 首先,告訴 TOMCAT 你所使用的 JNDI 名字,以便在 WEB 應用程序中查詢數據源。這些步驟由 web.xml 完成,其代碼如下。對于銀行帳戶數據源,使用的 JNDI 名字是 java:comp/env/jdbc/bankAccount ,而且只能在 java:comp/env/ 之后給出名字。TOMCAT 通過 JNDI 機制來解決其余的問題。對于 ATM 數據源也同樣于此。 例 4. web.xml
您必須告訴 TOMCAT 怎么樣返回 web.xml內的資源,這個過程就由bank.xml文件來完成了。對于銀行帳戶和 ATM 資源,您必須設置參數,以便 TOMCAT 能將 WEB 應用程序與數據源正確相連。有關更多的詳細信息,請參考 TOMCAT JNDI 數據源基礎知識。(參見 Resources) 其中一個參數需特別關注:factory.類中設置這個參數,用于當 WEB 應用程序通過 JNDI 查詢時來創建一個數據源。另外一個重要的資源(在 web.xml 中有描述)是 UserTransaction。java:comp/UserTransaction 使用這個資源來區分事務,它由 JOTM 來執行。 例 5 bank.xml
展開 WEB 應用程序:一旦你設置了 JOTM 和TOMCAT ,展開并使用 WEB 應用程序就很容易了。首先,下載 bank.tgz 并將之解壓縮, 再將bank.xml 和 bank.war 復制到 $CATALINA_HOME/webapps 下;然后,啟動 TOMCAT:
您會在日志里面發現 JOTM 還沒有啟動。它是在當您第一次訪問 DataSource 時才會啟動的,在那個時候,您將會發現以下信息:
鍵入URL[url]http://localhost:8080/bank/[/url]來使用 WEB 應用程序。 使用 WEB 應用程序 WEB 應用程序的首頁包含兩個鏈接: 1. 是 Cash Delivery 頁面,您可以在上面像在 ATM 一樣提款。 圖2 Cash Delivery 頁面 2. 是management console,您在上面可以對 ATM 或自己創建的銀行帳戶進行檢測或更新。 圖3 Management Console 操作之前,ATM 有$500,John Doe 銀行帳戶上有 $100 ,Jane Doe 銀行帳戶有 $600 。 如果 John Doe想取 $400 ,交易將會失敗,因為在他的帳戶上余額不夠。結果將是: Client ID:john_doe, value: $400 Cash can not be delivered to you because:not enough money in your account (only $100). 如果 Jane Doe想取 $550 ,交易也會失敗,因為ATM上的現金不夠。結果將是: Client ID:jane_doe, value: $550 Cash can not be delivered to you because:not enough cash available from this ATM (only $500). 如果 John Doe 取 $50 的話,交易將會成功。結果將是: Client ID:john_doe, value: $50 Please take your cash ($50) Thank you! 總結 這個簡單的例子證明了 servlet 是怎樣通過使用事務提供健壯和簡化的,并且確保在任何情況下都正確。Tomcat 和 JOTM 完美的結合使在 servlet 內能輕而易舉的取得事務的優勢。 除上述簡單的例子以外,JOTM還有更多的優點。JOTM 提供了以下性能,有助于增強 WEB 應用程序。 1.完全分布式事務支持.如果數據層、業務層、表示層運行在不同的 JVM 上,則有可能有一個全程的事務跨度這些JVM,事務的內容在 RMI/JRMP 和 RMI/IIOP 上傳播。 2.整合 JDBC。使用的 XAPool例子就是一個 XA-兼容的 JDBC 連接池,可以與數據庫相互操作。XAPool 類似于 Jakarta DBCP,只是增加了 XA-兼容的特征,如果要結合 JDBC 使用 JTA 事務就必須遵從這個特征。 3.整合 JMS。JOTM 可以結合 JORAM,由ObjectWeb 協會開發的“JMS 提供者”提供了事務的 JMS 消息。你可以得到出現在 servlet中同一事務的 JMS 消息發送件和更新的數據庫。 4.WEB 服務事務。JOTM 提供了BTP(Business Transaction Protocol)、JOTM-BTP接口,它們用于在 WEB 服務中增加事務行為。 所有這些功能的樣例和文檔都可以在 JOTM 的檔案和網站上找到。
| public boolean deliver(String client, int value) { InitialContext ctx = new InitialContext(); UserTransaction utx = (UserTransaction) ctx.lookup("java:comp/UserTransaction"); ... boolean success = false; try { // 開始事務 utx.begin(); //聯系客戶銀行帳戶... BankAccount account = new BankAccount(client); // ... 從帳戶上提款 account.withdraw(value); //聯系 ATM... ATM atm = new ATM(); // ... 傳送現金給客戶 atm.deliverCash(value); //一切正常 success = true; } catch (Exception e) { // 出現故障,不得不 // 報告給客戶 explanation += e.getMessage(); } finally { try { if (success) { /*一切正常提交事務 直到現在,錢才真正的從帳戶上提出,并且將現金傳送給客戶。 */ utx.commit(); } else { /* 出現故障,就回滾事務。 *所有在事務內處理的操作不會發生。 */ utx.rollback(); } } catch (Exception e) { /* 在完成事務的過程中出現故障, *仍舊保證 * 事務內的操作不會發生。/ */ // 報告給客戶 explanation += "\n" + e.getMessage(); //最后,事務不會成功 success = false; } finally { return success; } } } |
| mysql> CREATE DATABASE banktest; mysql> USE banktest; mysql> CREATE TABLE account( -> client VARCHAR(25) NOT NULL PRIMARY KEY, -> money INT) TYPE=InnoDB; mysql> INSERT INTO account valueS("john_doe", 100); mysql> INSERT INTO account valueS("jane_doe", 600); mysql> SELECT * FROM account; +----------+-------+ | client | money | +----------+-------+ | john_doe | 100 | | jane_doe | 600 | +----------+-------+ |
| mysql> CREATE DATABASE atmtest; mysql> USE atmtest; mysql> CREATE TABLE atm( -> id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, -> cash INT) TYPE=InnoDB; mysql> INSERT INTO atm valueS(null, 500); mysql> SELECT * FROM atm; +----+------+ | id | cash | +----+------+ | 1 | 500 | +----+------+ |
| <web-app> <resource-env-ref> <de.ion>Bank Account DataSource</de.ion> <resource-env-ref-name>jdbc/bankAccount</resource-env-ref-name> <resource-env-ref-type>javax.sql.DataSource</resource-env-ref-type> </resource-env-ref> <resource-env-ref> <de.ion>ATM DataSource</de.ion> <resource-env-ref-name>jdbc/ATM</resource-env-ref-name> <resource-env-ref-type>javax.sql.DataSource</resource-env-ref-type> </resource-env-ref> </web-app> |
| <Context path="/bank" docBase="bank.war" debug="0" reloadable="true" crossContext="true"> <!-- De.ion of the DataSource "jdbc/bankAccount" --> <Resource name="jdbc/bankAccount" auth="Container" type="javax.sql.DataSource" /> <ResourceParams name="jdbc/bankAccount"> <parameter> <!-- Factory of the DataSource --> <name>factory</name> <value>org.objectweb.jndi.DataSourceFactory</value> </parameter> <parameter> <name>url</name> <value>jdbc:mysql://localhost/banktest</value> </parameter> <!-- other parameters include: o username - name of database user o password - password of the database user o driverClassName - JDBC Driver name --> ... </ResourceParams> <!-- De.ion of the DataSource "jdbc/ATM" --> <Resource name="jdbc/ATM" auth="Container" type="javax.sql.DataSource" /> <!-- same type of parameters than for resource "jdbc/bankAccount" --> <ResourceParams name="jdbc/ATM"> ... </ResourceParams> <!-- De.ion of the resource "UserTransaction --> <Resource name="UserTransaction" auth="Container" type="javax.transaction.UserTransaction" /> <ResourceParams name="UserTransaction"> <parameter> <name>factory</name> <value>org.objectweb.jotm.UserTransactionFactory</value> </parameter> <parameter> <name>jotm.timeout</name> <value>60</value> </parameter> </ResourceParams> </Context> |
| > cd $CATALINA_HOME/bin > ./catalina.sh run Using CATALINA_BASE:/home/jmesnil/lib/tomcat Using CATALINA_HOME:/home/jmesnil/lib/tomcat Using CATALINA_TMPDIR:/home/jmesnil/lib/tomcat/temp Using JAVA_HOME:/usr/local/java May 6, 2003 5:56:00 PM org.apache.commons.modeler.Registry loadRegistry INFO:Loading registry information May 6, 2003 5:56:00 PM org.apache.commons.modeler.Registry getRegistry INFO:Creating new Registry instance May 6, 2003 5:56:00 PM org.apache.commons.modeler.Registry getServer INFO:Creating MBeanServer May 6, 2003 5:56:07 PM org.apache.coyote.http11.Http11Protocol init INFO:Initializing Coyote HTTP/1.1 . port 8080 Starting service Tomcat-Standalone Apache Tomcat/4.1.24-LE-jdk14 |
| May 6, 2003 5:56:20 PM org.objectweb.jotm.Jotm <init> INFO:JOTM started with a local transaction factory that is not bound. May 6, 2003 5:56:20 PM org.objectweb.jotm.Jotm <init> INFO:CAROL initialization |
轉載于:https://blog.51cto.com/newhappy/135996
總結
以上是生活随笔為你收集整理的用 JOTM 向Servlet中添加事务的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [导入]php 安全基础 附录C. 加密
- 下一篇: 系统管理-第1部分 系统的易管理性