EJB 3.0(Public Draft)入門記 Simplified API Chapter 10 その2 TransactionManagement

地震長かった...。入門記どころじゃないんですけどTransactionManagementアノテーションです。

TransactionManagementアノテーションはセッションBeanやメッセージ駆動型Beanのトランザクション境界のタイプを指定するそうです。タイプにはコンテナ管理とBean管理の2種類があって、セッションBeanやメッセージ駆動型BeanにTransactionManagementアノテーションが指定されていない場合、そのBeanはコンテナ管理のトランザクション境界をもつとみなされます、とあります。
defaultはコンテナ管理ということで、いままで動かしてきたセッションBeanやメッセージ駆動型Beanはどれも自動でトランザクションに入っていたんですね。

TransactionManagementアノテーションとTransactionManagementのタイプの定義を写してみます(写経?)。

@Target(TYPE) @Retention(RUNTIME)
public @interface TransactionManagement {
  TransactionManagementType value()
    default TransactionManagementType.CONTAINER;
}
public enum TransactionManagementType {
  CONTAINER,
  BEAN
}


実験コーナー〜。Bean管理のトランザクションを使って、SQLの更新がコミットされているかどうか見てみたいと思います。。まずJBossにくっついているHSQLDBにテーブルをつくります。JBossの管理コンソールからHSQLDBGUIツールを動かすにはhsqldb-ds.xmlの設定を変える必要みたい?。hsqldb-ds.xmlの設定についてはhttp://cnd.daitec.co.jp/openSo/documents/jboss/GettingStartedV4/dukesbank.htmlを参考にさせてもらいました。

HSQLDBGUIツールでEMPテーブルを作ります。

CREATE TABLE EMP (
   EMPNO NUMERIC(4) NOT NULL PRIMARY KEY,
   ENAME VARCHAR(10)
)


DAOを作ります。
DAOのビジネスインタフェース

public interface EmployeeDao {
  void insert(int no, String name);
}

DAOのBeanクラス:たんにinsertをしたいだけです。

@Stateless
public class EmployeeDaoBean implements EmployeeDao {

  @Resource(name = "DefaultDS")
  private DataSource dataSource;

  public void insert(int no, String name) {
    Connection con = null;
    try {
      con = dataSource.getConnection();
      PreparedStatement ps = con
          .prepareStatement("INSERT INTO emp VALUES(?, ?)");
      ps.setInt(1, no);
      ps.setString(2, name);
      ps.executeUpdate();
    } catch (SQLException e) {
      throw new RuntimeException(e);
    } finally {
      try {
        con.close();
      } catch (SQLException e) {
      }
    }
  }
}


Logicを作ります。
Logicのビジネスインタフェース

public interface EmployeeLogic {
  void insert(int no, String name);
}

LogicのBeanクラス:TransactionManagementアノテーションつかってTransactionManagementTypeにBEANを指定してます。UserTransactionを使ってBean内でトランザクションの開始とコミットを行います。

@Stateless
@Remote(EmployeeLogic.class)
@TransactionManagement(TransactionManagementType.BEAN)
public class EmployeeLogicBean implements EmployeeLogic {

  @EJB
  private EmployeeDao dao;

  @Resource
  private UserTransaction tx;
  
  public void insert(int no, String name) {
    try {
      tx.begin();
      dao.insert(no, name);
      tx.commit();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
}


クライアント:Logicのビジネスインタフェースに値を渡してます。

public class EmployeeClient {
  public static void main(String[] args) throws Exception {
    InitialContext ctx = new InitialContext();
    EmployeeLogic logic = (EmployeeLogic)ctx.lookup(EmployeeLogic.class.getName());
    logic.insert(1001, "ゴン");
  }
}

デプロイしてクライアントを実行し、HSQLDBGUIでEMPテーブルを見てみるとデータが追加されていることがわかりました。
今回は無理やりBean管理のトランザクションを使ってみました。コンテナ管理を使った場合、LogicのBeanクラスはこんな感じになると思います。

@Stateless
@Remote(EmployeeLogic.class)
public class EmployeeLogicBean implements EmployeeLogic {

  @EJB
  private EmployeeDao dao;

  public void insert(int no, String name) {
    dao.insert(no, name);
  }
}

コンテナ管理では当然UserTransactionを使う必要はないですが、UserTransactionのDIの記述を残したままデプロイしたらJBossに怒られて「it is illegal to inject UserTransaction into a CMT bean」とか言われました。