JavaEE使い方メモ（JTA）

[環境構築](http://qiita.com/opengl-8080/items/aeda0d8bad42af3113bd)

[コード](https://github.com/opengl8080-javaee-samples/jta)

# 環境
## OS
Windows 7

## AP サーバー
GlassFish 4.1 Open Source Edition (一部 Wildfly 9.0.1 使用)

## Java
1.8.0_60

## DB サーバー
MySQL 5.5.28, for Win64 (x86)

# JTA とは
Java Transaction API の略。  
Java で **トランザクションマネージャ** を扱うための各種 API を定義した仕様。

トランザクションマネージャとは、分散トランザクションの管理を行うサービス（ミドルウェア）で、 Java EE サーバーだと EJB コンテナがその役割を担っている。

# DTP
Distributed Transaction Processing の略（直訳するなら、分散トランザクション処理？）。

**X/Open** という組織が策定した、分散トランザクションについての標準規格。JTA が扱う分散トランザクションも、この DTP に準拠している。
X/Open は UNIX に関する技術の標準化を行う団体だったが、 1996 年に Open Software Foundation と合併して、現在は The Open Group という組織になっている。

## DTP で登場するソフトウェア
DTP では、分散トランザクションに登場するソフトウェアとして、以下のものを挙げている。

**リソースマネージャ**

トランザクション機能を備えたリソースを管理するソフトウェア。
リレーショナルデータベース製品や、メッセージ指向ミドルウェアなどがこれにあたる。

**トランザクションマネージャ**

複数のリソースマネージャを管理し、分散トランザクションを制御するソフトウェア。

**アプリケーション**

トランザクションを利用するソフトウェア。

## ２フェーズコミット
分散トランザクションを実現するための具体的なプロトコル。

トランザクションのコミットを２回のフェーズに分けて行う。

第１フェーズでは、各リソースマネージャに現在のトランザクションがコミット可能かどうかを確認する。
全てのリソースマネージャがコミット可能であると返事をしたら、第２フェーズとして各リソースマネージャにコミットの指示が飛ぶ。

どれか１つでもコミット不可の返事をしたリソースマネージャが存在した場合は、全てのリソースマネージャにロールバックの指示が飛ぶ。

こうすることで、分散トランザクションでの原子性の担保が実現されている。

より細かい話は [分散トランザクションに挑戦しよう！][1] を参照のこと。

DTP では、このプロトコルを実現するための具体的なインターフェースが定義されている。

## インターフェース
DTP では、大きく以下の２つのインターフェースが定義されている。

**TX インターフェース**

トランザクションマネージャが、アプリケーションに対して公開するインターフェース。

**XA インターフェース**

リソースマネージャが、トランザクションマネージャに対して公開するインターフェース。

# JTA が定義するインターフェース
JTA では、以下の３つのインターフェースが定義されている。

## [javax.transaction.TransactionManager](http://docs.oracle.com/javaee/7/api/javax/transaction/TransactionManager.html)
トランザクションマネージャが、 **アプリケーション・サーバー** に対して公開するインターフェース。

EJB がサポートしているコンテナ管理のトランザクション（CMT）は、この `TransactionManager` を使って実現されている。

## [javax.transaction.UserTransaction](http://docs.oracle.com/javaee/7/api/javax/transaction/UserTransaction.html)
トランザクションマネージャが、 **アプリケーション** に対して公開するインターフェース。

トランザクションの開始・終了をコンテナに任せないとき（BMT）は、この `UserTransaction` を EJB などにインジェクションして明示的にトランザクション境界を定義する。

## [javax.transaction.xa.XAResource](http://docs.oracle.com/javaee/7/api/javax/transaction/xa/XAResource.html)
リソースマネージャが、トランザクションマネージャに対して公開するインターフェース。

トランザクションマネージャは、この `XAResource` に定義されたメソッドを利用して、 DTP で定義された分散トランザクションを実現している。

`XAResource` の実装は、各リソースマネージャが用意する。
例えば、 MySQL の場合は JDBC ドライバの中に `XAResource` を実装したクラスが含まれている（`com.mysql.jdbc.jdbc2.optional.MysqlXAConnection`）。

下図は、[JTA の仕様書][2]に描かれている図で、各インターフェースとソフトウェアの関連を把握するのに丁度いい図になっている。

![jta.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/8f5fb537-b18d-2274-c2d9-76b7631e0eea.jpeg)

# グローバルトランザクションとローカルトランザクション
JTA の設定のときなどに目にする、これらの言葉について。

## グローバルトランザクション
分散トランザクションのこと。

トランザクションの対象となるリソースマネージャが複数存在するときに利用する。

## ローカルトランザクション
リソースマネージャが提供するトランザクションの API をそのまま利用すること。
JDBC なら、 `java.sql.Connection` の `commit()` メソッドとかを直接利用することになる。

トランザクションの対象となるリソースマネージャが１つしか存在しないときに利用する。

# GlassFish で分散トランザクションを使用する
JTA を使うということは、すなわち分散トランザクションを使うことになるので、「明示的に分散トランザクションを使用する」といった設定は必要ない。

強いて言うなら、データソースの登録で `XA` を実装したクラスを指定しないといけない、という点くらい。

MySQL を使って `XA` に対応した２つのデータソースを登録する。
JDBC ドライバは[あらかじめインストールしておくこと](http://qiita.com/opengl-8080/items/7d51351cb540269e1d05#mysql-%E3%81%AE-jdbc-%E3%83%89%E3%83%A9%E3%82%A4%E3%83%90%E3%82%92%E9%85%8D%E7%BD%AE%E3%81%99%E3%82%8B)。

## データソースの登録
### コネクションプールの登録
- 管理コンソールを開き、 [Resources] > [JDBC] > [JDBC Connection Pools] を選択する。
- [New...] をクリック。
- 以下入力。
    - [Pool Name] に適当な名前を入れる。
    - [Resource Type] で `javax.sql.XADataSource` を選択。
    - [Database Driver Vendor] で `MySql` を選択。

![jta.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/7f5d2891-34ad-1cde-4297-bef9c058409a.jpeg)


- [Next] をクリックして、次のページで以下のプロパティを設定。
    - `Username`, `Password`, `ServerName`, `DatabaseName`
- 同じ要領で、名前違いのデータソースをもう１つ登録する。

### JDBC リソースの登録
- 管理コンソールから、 [Resources] > [JDBC] > [JDBC Resources] を選択する。
- [New...] をクリック。
- 以下入力。
    - [JNDI Name] に適当な名前を入れる。
    - [Pool Name] で、先ほど登録したコネクションプールを選択。

![jta.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/866cfe5a-0d03-4139-dd8a-4f01eab49c7c.jpeg)

- [OK] をクリック。

## データベース
![jta.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/7f60d6bd-bc94-edfa-012c-5530dac196cf.jpeg)

こんなテーブルを用意しておく。

## 実装＋動作確認
```java:HelloEjb.java
package sample.jta.ejb;

import java.sql.Connection;
import java.sql.PreparedStatement;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.sql.DataSource;
import javax.transaction.TransactionSynchronizationRegistry;

@Stateless
public class HelloEjb {
    
    @Resource(lookup = "jdbc/mysql_global_1") // ★データソースを注入
    private DataSource ds1;
    
    @Resource(lookup = "jdbc/mysql_global_2") // ★データソースを注入
    private DataSource ds2;
    
    @Resource
    private TransactionSynchronizationRegistry tx;
    
    public void execute() {
        this.insert(this.ds1, "hoge");
        this.insert(this.ds2, "fuga");
        
        System.out.println("txKey = " + tx.getTransactionKey());
    }
    
    private void insert(DataSource ds, String value) {
        // ★ TEST_TABLE にレコードを INSERT
        try (Connection con = ds.getConnection();
            PreparedStatement ps = con.prepareStatement("INSERT INTO TEST_TABLE (VALUE) VALUES (?)");
            ) {
            
            ps.setString(1, value);
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

`HelloEjb#execute()` を実行するように外部からアクセスする。

```text:GlassFishコンソール出力
情報:   txKey = JavaEETransactionImpl: txId=21 nonXAResource=null jtsTx=com.sun.jts.jta.TransactionImpl@c3161dfc ...]
```

**実行後のテーブル**

![jta.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/8579a56d-4bff-fa95-8d4e-bb79326ad079.jpeg)

複数のデータソースを、１つの EJB （トランザクション）の中で利用できている。

# 非 XA なデータソースを登録するとどうなる？
GlassFish に限らず、 AP サーバーのデータソースには、 XA を実装していないデータソースも登録できる。

JTA は分散トランザクションのための API なので、 XA に対応していないデータソースは利用できなさそうだが、実際は登録できるし、そのまま JTA 経由で利用もできてしまう。

何が起こっているのか調べてみる。

## データソースを登録する
先ほどと同じ要領で、非 XA なデータソースを登録する。

![jta.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/50ddbeec-d953-a5dc-ca4e-36af2b84d5e1.jpeg)

## 実装
```java:NonXADataSourceEjb.java
package sample.jta.ejb;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.sql.DataSource;
import javax.transaction.TransactionSynchronizationRegistry;

@Stateless
public class NonXADataSourceEjb {
    
    @Resource(lookup = "jdbc/mysql_local_1")
    private DataSource ds;
    
    @Resource
    private TransactionSynchronizationRegistry tx;
    
    public void execute() throws Exception {
        try (Connection con = ds.getConnection()) { // ★ ds を JTA に参加させる
            Object key = tx.getTransactionKey();
            System.out.println("txKey = " + key);

            // ★ TransactionKey が持つ nonXAResource の具体的なクラス名を確認する
            Object nonXAResource = this.getFieldValue(key, "nonXAResource");
            System.out.println("nonXAResource.class = " + nonXAResource.getClass());
        }
    }
    
    public Object getFieldValue(Object obj, String fieldName) throws Exception {
        if (obj == null) return null;
        
        Class<?> clazz = obj.getClass();
        try {
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            Object result = field.get(obj);
            field.setAccessible(false);
            return result;
        } catch (NoSuchFieldException e) {
            return null;
        }
    }
}
```

非 XA なデータソースを JTA 経由で利用すると、 `nonXAResource` といういかにもそれっぽいインスタンスが生成されているようなので、その値をリフレクションを使って無理やり取得している。

```text:GlassFishコンソール出力
情報:   txKey = JavaEETransactionImpl: txId=39 nonXAResource=715 jtsTx=null ...]
情報:   nonXAResource.class = class com.sun.enterprise.resource.ResourceHandle
```

`nonXAResource` の実体は `com.sun.enterprise.resource.ResourceHandle` というクラスらしい。

このクラスのコードは以下になる。

[GC: ResourceHandle - com.sun.enterprise.resource.ResourceHandle (.java) - GrepCode Class Source](http://grepcode.com/file/repo1.maven.org/maven2/org.glassfish.connectors/connectors-runtime/3.0/com/sun/enterprise/resource/ResourceHandle.java#ResourceHandle)

```java:ResourceHandle.java
public class ResourceHandle implements
        com.sun.appserv.connectors.internal.api.ResourceHandle, TransactionalResource {

    ...

    private XAResource xares;

    ...
```

よく見ると、中に `XAResource` が入っている。

このフィールドの実体を調べてみると、 `com.sun.enterprise.resource.ConnectorXAResource` というクラスだった。
コードは以下。

[GC: ConnectorXAResource - com.sun.enterprise.resource.ConnectorXAResource (.java) - GrepCode Class Source](http://grepcode.com/file/repo1.maven.org/maven2/org.glassfish.connectors/connectors-runtime/3.0/com/sun/enterprise/resource/ConnectorXAResource.java#ConnectorXAResource)

このクラスの、 `commit()` メソッドを覗いてみる。

```java:ConnectorXAResource.java
    public void commit(Xid xid, boolean onePhase) throws XAException {
        try {
            ResourceHandle handle = getResourceHandle();
            ManagedConnection mc = (ManagedConnection) handle.getResource();
            mc.getLocalTransaction().commit(); // ★ getLocalTransaction() してる！
        } catch (Exception ex) {
            handleResourceException(ex);
        }finally{
            resetAssociation();
        }
    }
```

`ConnectorXAResource` は `XAResource` を実装しているものの、実はローカルトランザクションを使っているっぽい。

つまり、データソースが XA に対応していなかったとしても、内部でラップして `XAResource` に擬似的に対応させることで、 JTA 経由で利用できるようになっているのだろう。

一応 Wildfly でも同じように確認したが、やはり非 XA データソースは、内部でローカルトランザクションを使うクラスにラップされて利用されていた。

# 複数のローカルトランザクションを使うとどうなる？
```java:MultiNonXADataSourceEjb.java
package sample.jta.ejb;

import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.sql.DataSource;

@Stateless
public class MultiNonXADataSourceEjb {

    @Resource(lookup = "jdbc/mysql_local_1")
    private DataSource ds1;

    @Resource(lookup = "jdbc/mysql_local_2")
    private DataSource ds2;
    
    public void execute() throws Exception {
        try (Connection c1 = ds1.getConnection();
             Connection c2 = ds2.getConnection()) {}
    }
}
```

```text:GlassFishコンソール出力
情報:   ejb = MultiNonXADataSourceEjb, method = execute
重大:   RAR5029:Unexpected exception while registering component 
java.lang.IllegalStateException: Local transaction already has 1 non-XA Resource: cannot add more resources.  
	at com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.enlistResource(JavaEETransactionManagerSimplified.java:345)
        ...

警告:   RAR7132: Unable to enlist the resource in transaction. Returned resource to pool. Pool name: [ mysql_local_2 ]
警告:   RAR5117 : Failed to obtain/create connection from connection pool [ mysql_local_2 ]. Reason : com.sun.appserv.connectors.internal.api.PoolingException: java.lang.IllegalStateException: Local transaction already has 1 non-XA Resource: cannot add more resources.  
警告:   RAR5114 : Error allocating connection : [Error in allocating a connection. Cause: java.lang.IllegalStateException: Local transaction already has 1 non-XA Resource: cannot add more resources.  ]
重大:   java.lang.reflect.InvocationTargetException
       ...
```

`ds2` を JTA に参加させようとした時点で例外がスローされた。

`Local transaction already has 1 non-XA Resource: cannot add more resources. ` とあるので、非 XA なデータソースを複数使うことはできないようになっている。

## データベースコネクションが JTA に参加するタイミング
説明を省いていたが、データベースコネクションが JTA の管理下に置かれるタイミングは、上記の例からも分かる通り `DataSource#getConnection()` を実行したタイミングになる。

ただし、 JTA の仕様的にそうなのか、 GlassFish の実装的にそうなのかは分からない。

# アノテーション
JTA1.2 (Java EE 7) から追加されたアノテーションに、 `@Transactional` と `@TransactionScoped` がある。

これらのアノテーションを利用することで、 CDI 管理ビーンでコンテナ管理のトランザクションが利用できるようになる。

## @Transactional
このアノテーションで CDI 管理ビーンをアノテートすることで、 EJB と同じように宣言的なトランザクションが使えるようになる。

### 基本
```java:
package sample.jta.ejb;

import java.sql.Connection;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.sql.DataSource;
import javax.transaction.TransactionSynchronizationRegistry;
import sample.jta.cdi.TransactionalBean;

@Stateless
public class TransactionalEjb {
    
    @Resource(lookup = "jdbc/mysql_local_1")
    private DataSource ds;
    
    @Resource
    private TransactionSynchronizationRegistry tx;
    
    @Inject
    private TransactionalBean bean;
    
    public void execute() throws Exception {
        try (Connection con = ds.getConnection()) {
            System.out.println("TransactionalEjb : txKey=" + tx.getTransactionKey());
            this.bean.requreisNew();
            this.bean.required();
        }
    }
}
```

```java:TransactionalBean.java
package sample.jta.cdi;

import java.sql.Connection;
import javax.annotation.Resource;
import javax.enterprise.context.ApplicationScoped;
import javax.sql.DataSource;
import javax.transaction.TransactionSynchronizationRegistry;
import javax.transaction.Transactional;

@ApplicationScoped
public class TransactionalBean {
    
    @Resource(lookup = "jdbc/mysql_local_1")
    private DataSource ds;
    
    @Resource
    private TransactionSynchronizationRegistry tx;
    
    @Transactional
    public void required() throws Exception {
        try (Connection con = ds.getConnection()) {
            System.out.println("required() : txKey=" + tx.getTransactionKey());
        }
    }
    
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public void requreisNew() throws Exception {
        try (Connection con = ds.getConnection()) {
            System.out.println("requreisNew() : txKey=" + tx.getTransactionKey());
        }
    }
}
```

```text:GlassFishコンソール出力
情報:   TransactionalEjb : txKey=JavaEETransactionImpl: txId=52 nonXAResource=827 jtsTx=null ...]

情報:   requreisNew() : txKey=JavaEETransactionImpl: txId=53 nonXAResource=null jtsTx=null localTxStatus=0 syncs=[]

情報:   required() : txKey=JavaEETransactionImpl: txId=52 nonXAResource=827 jtsTx=null ...]
```

- `@Transactional` で CDI 管理ビーンのメソッド（クラス）をアノテートすることで、トランザクション境界を設けることができる。
- アノテーションの引数で、トランザクションの開始・終了をどうするかの制御ができる。
    - 列挙型の `TxType` を使用する。
    - それぞれの意味は、 [EJB の場合](http://qiita.com/opengl-8080/items/e4840aa3e33b42ae0d6b#%E3%81%BE%E3%81%A8%E3%82%81-1) と同じ。

### 例外発生時の制御
※以下のコードは、最初 GlassFish 4.1 と Payara 4.1.153 で動作検証したのですが、どうも仕様通りに動いてくれなかったので Wildfly 9.0.1 で動作検証しました。

**追記**

[コメント](http://qiita.com/opengl-8080/items/9b9d432e0a10486bc1b4#comment-5a6f430884930097e028) にあるとおり、バグだったようで今後修正されるようです。

#### 基本
```java:TransactionalExceptionBean.java
package sample.jta.ejb;

import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.jta.cdi.TransactionalExceptionBean;

@Stateless
public class TransactionalExceptionEjb {
    
    @Inject
    private TransactionalExceptionBean bean;
    
    public void execute() {
        this.executeQuietly(this.bean::insertAndThrowException);
        this.executeQuietly(this.bean::insertAndThrowRuntimeException);
    }
    
    private void executeQuietly(ThrowableRunnable tr) {
        try {
            tr.execute();
        } catch (Exception ex) {
            System.out.println("[" + ex.getClass().getSimpleName() + "] " + ex.getMessage());
        }
    }
    
    @FunctionalInterface
    private static interface ThrowableRunnable {
        void execute() throws Exception;
    }
}
```

```java:TransactionalExceptionBean.java
package sample.jta.cdi;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import javax.annotation.Resource;
import javax.enterprise.context.ApplicationScoped;
import javax.sql.DataSource;
import javax.transaction.Transactional;

@ApplicationScoped
@Transactional(Transactional.TxType.REQUIRES_NEW)
public class TransactionalExceptionBean {
    
    @Resource(lookup = "java:/jdbc/mysql_test")
    private DataSource ds;
    
    public void insertAndThrowException() throws Exception {
        this.insert(this.ds, "insertAndThrowException");
        throw new Exception("test exception");
    }
    
    public void insertAndThrowRuntimeException() throws Exception {
        this.insert(this.ds, "insertAndThrowRuntimeException");
        throw new RuntimeException("test runtime exception");
    }
    
    private void insert(DataSource ds, String value) throws SQLException {
        try (Connection con = ds.getConnection();
            PreparedStatement ps = con.prepareStatement("INSERT INTO TEST_TABLE (VALUE) VALUES (?)");
            ) {
            
            ps.setString(1, value);
            ps.executeUpdate();
        }
    }
}
```

```text:Wildflyコンソール出力
22:14:29,546 INFO  [stdout] (default task-2) [Exception] test exception
22:14:29,552 INFO  [stdout] (default task-2) [RuntimeException] test runtime exception
```

**実行後のデータベース**

![jta.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/aee30cb6-90a6-b294-9980-4f95778707c3.jpeg)

- チェック例外がスローされた場合、トランザクションは**ロールバックされない**。
- 非チェック例外がスローされた場合、トランザクションはロールバックされる。

この辺の挙動は、 EJB の場合と同じ。

#### ロールバックする（しない）例外を限定する
```java:TransactionalExceptionBean.java
package sample.jta.cdi;

...
import java.util.function.Supplier;

@ApplicationScoped
@Transactional(Transactional.TxType.REQUIRES_NEW)
public class TransactionalExceptionBean {
    
    @Resource(lookup = "java:/jdbc/mysql_test")
    private DataSource ds;
    
    ...

    @Transactional(
        value=Transactional.TxType.REQUIRES_NEW,
        rollbackOn=IOException.class,
        dontRollbackOn=NullPointerException.class
    )
    public void insertAndThrowAnyException(Supplier<? extends Exception> exceptionProducer) throws Exception {
        Exception e = exceptionProducer.get();
        this.insert(this.ds, "insertAndThrowAnyException(" + e.getClass().getSimpleName() + ")");
        throw e;
    }
    
    ...
}
```

```java:TransactionalExceptionEjb.java
package sample.jta.ejb;

import java.io.IOException;
...

@Stateless
public class TransactionalExceptionEjb {
    
    @Inject
    private TransactionalExceptionBean bean;
    
    public void execute() {
        ...
        
        // RuntimeException
        this.executeQuietly(() -> this.bean.insertAndThrowAnyException(NullPointerException::new));
        this.executeQuietly(() -> this.bean.insertAndThrowAnyException(IllegalStateException::new));
        
        // Exception
        this.executeQuietly(() -> this.bean.insertAndThrowAnyException(NoSuchMethodException::new));
        this.executeQuietly(() -> this.bean.insertAndThrowAnyException(IOException::new));
    }
    
    ...
}
```

```text:Wildflyコンソール出力
22:30:30,120 INFO  [stdout] (default task-4) [NullPointerException] null
22:30:30,127 INFO  [stdout] (default task-4) [IllegalStateException] null
22:30:30,132 INFO  [stdout] (default task-4) [NoSuchMethodException] null
22:30:30,137 INFO  [stdout] (default task-4) [IOException] null
```

**実行後のデータベース**

![jta.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/be9504c1-4df1-3b80-fea8-84f8ce93be94.jpeg)

- `rollbackOn` で、チェック例外の中でもロールバックする例外を指定できる。
    - `IOException` はチェック例外なので、デフォルトではロールバックされずデータベースに登録される。
    - しかし、 `rollbackOn` で指定しているので、ロールバックされてデータベースには登録されていない。
- `dontRollbackOn` で、非チェック例外の中でもロールバックしない例外を指定できる。
    - `NullPointerException` は非チェック例外なので、デフォルトではロールバックされてデータベースには登録されない。
    - しかし、 `dontRollbackOn` で指定しているので、ロールバックされずにデータベースに登録されている。

#### GlassFish での動作
GlassFish で動かすと、 `RuntimeException` をスローしてもロールバックしてくれませんでした。
代わりに、 `RollbackException` がスローされるという現象が発生します。

挙動的には、 [GlassFish4.1をなおしてみた - 見習いプログラミング日記](http://n-agetsuma.hatenablog.com/entry/2014/12/25/000638) こちらの記事に描かれているものと同じ感じでした。
issue は reolved だけど、もしかして例外のセットだけ直して、 rollbackonly → commit の即死コンボは直ってないのかなぁ。。。

## @TransactionScoped
```java:TransactionScopedBean.java
package sample.jta.cdi;

import java.io.Serializable;
import javax.transaction.TransactionScoped;

@TransactionScoped
public class TransactionScopedBean implements Serializable {

    @Override
    public String toString() {
        return "TransactionScopedBean {hash=" + this.hashCode() + "}";
    }
}
```

```java:TransactionScopedEjb.java
package sample.jta.ejb;

import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.transaction.TransactionSynchronizationRegistry;
import sample.jta.cdi.TransactionScopedBean;

@Stateless
public class TransactionScopedEjb {
    
    @Inject
    private TransactionScopedBean bean;
    
    @Resource
    private TransactionSynchronizationRegistry tx;
    
    @Inject
    private RequiredEjb requiredEjb;
    
    @Inject
    private RequiresNewEjb requiresNewEjb;
    
    public void execute() {
        System.out.println("[TestTransactionScopedEjb]");
        System.out.println("txKey=" + this.tx.getTransactionKey());
        System.out.println("bean=" + this.bean);
        
        this.requiresNewEjb.execute();
        this.requiredEjb.execute();
    }
}
```

```java:RequiredEjb.java
package sample.jta.ejb;

import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;
import javax.transaction.TransactionSynchronizationRegistry;
import sample.jta.cdi.TransactionScopedBean;

@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class RequiredEjb {
    
    @Inject
    private TransactionScopedBean bean;
    
    @Resource
    private TransactionSynchronizationRegistry tx;
    
    public void execute() {
        System.out.println("[RequiredEjb]");
        System.out.println("txKey=" + this.tx.getTransactionKey());
        System.out.println("bean=" + this.bean);
    }
}
```

```java:RequiresNewEjb.java
package sample.jta.ejb;

import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;
import javax.transaction.TransactionSynchronizationRegistry;
import sample.jta.cdi.TransactionScopedBean;

@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class RequiresNewEjb {

    @Inject
    private TransactionScopedBean bean;
    
    @Resource
    private TransactionSynchronizationRegistry tx;
    
    public void execute() {
        System.out.println("[RequiresNewEjb]");
        System.out.println("txKey=" + this.tx.getTransactionKey());
        System.out.println("bean=" + this.bean);
    }
}
```

```text:GlassFishコンソール出力
情報:   [TestTransactionScopedEjb]
情報:   txKey=JavaEETransactionImpl: txId=1 ...]
情報:   bean=TransactionScopedBean {hash=1815003275}

情報:   [RequiresNewEjb]
情報:   txKey=JavaEETransactionImpl: txId=2 ...]
情報:   bean=TransactionScopedBean {hash=2009619742}

情報:   [RequiredEjb]
情報:   txKey=JavaEETransactionImpl: txId=1 ...]
情報:   bean=TransactionScopedBean {hash=1815003275}
```

- `txId` と `TransactionScopedBean` のハッシュ値の関係に注目。
- `@TransactionScoped` でアノテートすると、トランザクションごとにインスタンスが生成される。

# 参考
- [JSR 907: JavaTM Transaction API (JTA) | The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 907][2]
- [1.4 Java Transaction Service(JTS)](http://software.fujitsu.com/jp/manual/manualfiles/M050000/B1WN4841/04/j2ee01/j2ee0008.htm)
- [分散トランザクションに挑戦しよう！][1]
- [トランザクションモニター - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%A2%E3%83%8B%E3%82%BF%E3%83%BC)
- [X/Open XA - Wikipedia](https://ja.wikipedia.org/wiki/X/Open_XA)
- [X/Openとは 「X/Open Company, Ltd.」 (エックスオープン)： - IT用語辞典バイナリ](http://www.sophia-it.com/content/X/Open)
- [Oracle XAを使用したアプリケーションの開発](http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/appdev.102/B19248-02/adfns_xa.htm)
- [The Open Groupとは ジオープングループ： - IT用語辞典バイナリ](http://www.sophia-it.com/content/The+Open+Group)
- [JDBC 接続プールの編集 (Sun GlassFish Enterprise Server 2.1 管理ガイド)](http://docs.oracle.com/cd/E19879-01/820-7432/ablir/index.html)
- [Amazon.co.jp： マスタリングJavaEE5 第2版 (DVD付) (Programmer’s SELECTION): 三菱UFJインフォメーションテクノロジー株式会社 斉藤 賢哉: 本](http://www.amazon.co.jp/%E3%83%9E%E3%82%B9%E3%82%BF%E3%83%AA%E3%83%B3%E3%82%B0JavaEE5-%E7%AC%AC2%E7%89%88-DVD%E4%BB%98-Programmer%E2%80%99s-SELECTION/dp/4798120545)
- [Amazon.co.jp： Beginning Java EE 6 GlassFish 3で始めるエンタープライズJava (Programmer’s SELECTION): Antonio Goncalves, 日本オラクル株式会社, 株式会社プロシステムエルオーシー: 本](http://www.amazon.co.jp/Beginning-GlassFish-3%E3%81%A7%E5%A7%8B%E3%82%81%E3%82%8B%E3%82%A8%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%97%E3%83%A9%E3%82%A4%E3%82%BAJava-Programmer%E2%80%99s-SELECTION/dp/4798124605/ref=sr_1_1?ie=UTF8&qid=1443342866&sr=8-1&keywords=beginning+java+ee+6)
- [Amazon.co.jp： Enterprise JavaBeans 3.1 第6版: Andrew Lee Rubinger, Bill Burke, 佐藤 直生, 木下 哲也: 本](http://www.amazon.co.jp/Enterprise-JavaBeans-3-1-Andrew-Rubinger/dp/4873115086)
- [Java Transaction API - Wikipedia](https://ja.wikipedia.org/wiki/Java_Transaction_API)

[1]: http://www.ogis-ri.co.jp/otc/hiroba/technical/DTP/step2/
[2]: https://jcp.org/en/jsr/detail?id=907