JTA トランザクション
- JTA トランザクションは javax.transaction.UserTransaction#begin() メソッドを呼び出して開始する。
- javax.transaction.UserTransaction インターフェースのメソッドによりトランザクション制御を行う。
| メソッド | 内容 |
| void commit() |
トランザクションをコミットする。 |
| void rollback() | トランザクションをロールバックする。 |
サンプル
銀行の現金出納を管理する。セッション Bean で直接 DB アクセスする。
データベース定義
テーブル作成 SQL スクリプト作成 - D:\teller.sql
DROP TABLE CHECKING;
DROP TABLE CASH_IN_MACHINE;
CREATE TABLE CHECKING (
id VARCHAR(3) ,
balance DECIMAL(10,2) ,
CONSTRAINT pk_checking
PRIMARY KEY (id)
);
CREATE TABLE CASH_IN_MACHINE (
amount DECIMAL(10,2) ,
time_stamp DATE
);
INSERT INTO CHECKING VALUES ('123', 500.00);
INSERT INTO CASH_IN_MACHINE VALUES (10000.00, CURDATE());
データベース切替
以下 MySQL にログインして操作する。
warehouse.sql を保存したディレクトリに移動してからログインすること。
USE example_ejb
テーブル作成
SOURCE teller.sql
データベース接続定義
EJB:CMPSavingsAccountEJB と同じものを使用する。
プロジェクト構成
| プロジェクト種別 | EJB プロジェクト |
| プロジェクト |
BMTJTAEJB |
| プロジェクト追加先 EAR | BMTJTAApp |
| エンタープライズ Bean | teller.TellerBean.java |
コーディング完了後、XDoclet 実行。
teller.TellerBean
/**
*
*/
package teller;
import java.rmi.RemoteException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.SessionContext;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
/**
*
* <!-- begin-user-doc -->
* A generated session bean
* <!-- end-user-doc -->
* *
* <!-- begin-xdoclet-definition -->
* @ejb.bean name="Teller"
* description="An EJB named Teller"
* display-name="Teller"
* jndi-name="Teller"
* type="Stateful"
* transaction-type="Bean"
* view-type="remote"
*
* <!-- end-xdoclet-definition -->
* @generated
*/
public abstract class TellerBean implements javax.ejb.SessionBean {
private static final String dbName = "java:/MySqlDS";
private String customerId;
private double machineBalance;
private SessionContext context;
private Connection con;
/**
*
* <!-- begin-xdoclet-definition -->
* @ejb.create-method view-type="remote"
* <!-- end-xdoclet-definition -->
* @generated
*
* //TODO: Must provide implementation for bean create stub
*/
public void ejbCreate(String id) throws CreateException {
customerId = id;
try {
machineBalance = selectMachine();
} catch (Exception ex) {
throw new CreateException(ex.getMessage());
}
}
public void setSessionContext(SessionContext context)
throws EJBException, RemoteException {
this.context = context;
}
/**
*
* <!-- begin-xdoclet-definition -->
* @ejb.interface-method view-type="remote"
* <!-- end-xdoclet-definition -->
* @generated
*
* //TODO: Must provide implementation for bean method stub
*/
public void withdrawCash(double amount) {
UserTransaction ut = context.getUserTransaction();
try {
ut.begin();
updateChecking(amount);
machineBalance -= amount;
insertMachine(machineBalance);
ut.commit();
} catch (Exception ex) {
try {
ut.rollback();
} catch (SystemException syex) {
throw new EJBException("Rollback failed: "
+ syex.getMessage());
}
throw new EJBException("Transaction failed: "
+ ex.getMessage());
}
}
/**
* @ejb.interface-method
*/
public double getCheckingBalance() {
try {
return selectChecking();
} catch (SQLException ex) {
throw new EJBException("Unable to get balance: "
+ ex.getMessage());
}
}
private void makeConnection() {
try {
InitialContext ic = new InitialContext();
DataSource ds = (DataSource) ic.lookup(dbName);
con = ds.getConnection();
} catch (Exception ex) {
throw new EJBException("Unable to connect to database. "
+ ex.getMessage());
}
}
private void releaseConnection() {
try {
con.close();
} catch (SQLException ex) {
throw new EJBException("releaseConnection: "
+ ex.getMessage());
}
}
private void updateChecking(double amount) throws SQLException {
makeConnection();
String updateStatement = "UPDATE CHECKING " +
"SET balance = balance - ? WHERE id = ?";
PreparedStatement prepStmt = con
.prepareStatement(updateStatement);
prepStmt.setDouble(1, amount);
prepStmt.setString(2, customerId);
prepStmt.executeUpdate();
prepStmt.close();
releaseConnection();
}
private void insertMachine(double amount) throws SQLException {
makeConnection();
String insertStatement = "INSERT INTO CASH_IN_MACHINE " +
"VALUES ( ? , CURDATE() )";
PreparedStatement prepStmt = con
.prepareStatement(insertStatement);
prepStmt.setDouble(1, amount);
prepStmt.executeUpdate();
prepStmt.close();
releaseConnection();
}
private double selectMachine() throws SQLException {
makeConnection();
String selectStatement = "SELECT amount FROM CASH_IN_MACHINE " +
"WHERE time_stamp = " +
"(SELECT MAX(time_stamp) FROM CASH_IN_MACHINE)";
PreparedStatement prepStmt = con
.prepareStatement(selectStatement);
ResultSet rs = prepStmt.executeQuery();
if (rs.next()) {
double result = rs.getDouble(1);
prepStmt.close();
releaseConnection();
return result;
} else {
prepStmt.close();
releaseConnection();
throw new EJBException("Row for id " + customerId
+ " not found.");
}
}
private double selectChecking() throws SQLException {
makeConnection();
String selectStatement = "SELECT balance FROM CHECKING " +
"WHERE id = ?";
PreparedStatement prepStmt = con
.prepareStatement(selectStatement);
prepStmt.setString(1, customerId);
ResultSet rs = prepStmt.executeQuery();
if (rs.next()) {
double result = rs.getDouble(1);
prepStmt.close();
releaseConnection();
return result;
} else {
prepStmt.close();
releaseConnection();
throw new EJBException("Row for id " + customerId
+ " not found.");
}
}
}
参考 - ejb-jar.xml (抜粋)
<?xml version="1.0" encoding="UTF-8"?> <ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd" version="2.1"> <display-name>BMTJTAEJB</display-name> <enterprise-beans> <session> <display-name>Teller</display-name> <ejb-name>Teller</ejb-name> <home>teller.TellerHome</home> <remote>teller.Teller</remote> <ejb-class>teller.TellerSession</ejb-class> <session-type>Stateful</session-type> <transaction-type>Bean</transaction-type> </session> </enterprise-beans> <assembly-descriptor></assembly-descriptor> </ejb-jar>
プロジェクト構成 - Java アプリケーションクライアント
| プロジェクト種別 | Java プロジェクト |
| プロジェクト | BMTJTAClient |
| ビルド・パス 追加プロジェクト | BMTJTAEJB |
| ビルド・パス 追加ライブラリ | C:\jboss\client\jbosall-client.jar |
| クラス | TellerClient.java |
| ファイル | BMTJTAClient/jndi.properties |
TellerClient
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import teller.Teller;
import teller.TellerHome;
public class TellerClient {
public static void main(String[] args) {
try {
Context initial = new InitialContext();
Object objref = initial.lookup("Teller");
TellerHome home = (TellerHome) PortableRemoteObject.narrow(
objref, TellerHome.class);
Teller duke = home.create("123");
System.out.println("checking = "
+ duke.getCheckingBalance());
duke.withdrawCash(60.00);
System.out.println("checking = "
+ duke.getCheckingBalance());
duke.remove();
System.exit(0);
} catch (Exception ex) {
System.err.println("Caught an exception.");
ex.printStackTrace();
}
}
}
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.provider.url=jnp://localhost:1099 java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
実行結果
BMTJTAApp をサーバーで実行する。
サーバービューの JBoss の [状況] が「始動済み」になったら、BMTJTAClient プロジェクトについて Java アプリケーション実行を行う。
実行結果はコンソールに次のとおり出力される。
コンソール表示結果 - TellerClient
checking = 500.0
checking = 440.0
詳細
クラス: teller.TellerBean
public void withdrawCash(double amount)
- 出金メソッド。
- javax.ejb.EJBContext#getUserTransaction() で UserTransaction オブジェクトを取得。トランザクション制御可能となる。
- トランザクションを開始し、DB 更新する。
- 全ての更新が成功したらトランザクションをコミットする。
- 例外をキャッチしたらトランザクションをロールバックする。
- 現金がなくなってもこのロジックでは例外が発生するわけではないので、現金が足りない場合は例外をスローするように改良が必要。
クラス: TellerClient
public class TellerClient
- EJB に現金残の照会を行い、コンソールに出力する。
- 出金を行い、再度現金残の照会を行う。
【EJBトランザクションの最新記事】


