EJB 3.0(Public Draft)入門記 Simplified API Chapter 10 その3 TransactionAttribute
今回はTransactionAttributeアノテーションを扱います。Simplified APIのドキュメントで言えば10.5節です。
TransactionAttributeアノテーションはコンテナがビジネスメソッドをトランザクションコンテキスト内で呼び出すかどうかを指定するそうです。トランザクションアトリビュートのセマンティクスは「EJB Core Contracts and Requirements」ドキュメントのchapter 12 に定義されているらしいです。
TransactionAttributeアノテーションはコンテナ管理のトランザクション境界が使われる場合にのみ使うことができるそうです。このアノテーションはBeanクラスもしくはビジネスメソッドに指定できて、クラスに指定した場合はビジネスインタフェースのメソッドすべてに適用され、メソッドに指定した場合はそのメソッドのみに適用されるそうです。クラスとメソッド両方に指定があった場合はメソッドのアノテーションが優先です。
Bean管理のトランザクション境界が使われていてTransactionAttributeアノテーションが指定されていない場合、REQUIREDのトランザクション属性があるとみなされるそうです。
TransactionAttributeTypeとTransactionAttributeの定義を写してみます(写経?)。
public enum TransactionAttributeType { MANDATORY, REQUIRED, REQUIRES_NEW, SUPPORTS, NOT_SUPPORTED, NEVER }
@Target ({METHOD, TYPE}) @Retention(RUNTIME) public @interface TransactionAttribute { TransactionAttributeType value() default TransactionAttributeType.REQUIRED; }
ではTransactionAttributeを使った簡単なコードを動かしてみます。今回はREQUIREDとREQUIRES_NEWを使って監査ログをとるといったようなサンプルを作ってみたいと思います。監査ログはREQUIRES_NEWでとるべしといった話がJ2EE勉強会であったと思います。
- 登場人物はClient、EmployeeLogic(Bean)、EmployeeDao(Bean)、AuditLogic(Bean)、AuditDao(Bean)
- 使用するテーブルはEMPとAUDIT
- EmployeeLogicBeanクラスのトランザクション属性は指定しない(DefaultのREQUIREDを使う)
- AuditLogicBeanクラスのメソッドのトランザクション属性をREQUIRES_NEWに指定。
- EmployeeLogicBeanのAroundInvokeでAuditLogicを呼び出す。
- EmployeeLogicBeanとAuditLogicBeanが異なったトランザクションで実行されていることを確かめるためEmployeeLogicBeanでトランザクションをロールバックさせてみる。
結果として次のようになるはず
- EMPテーブルのデータは追加されないがAUDITテーブルにはデータが追加される
EMPテーブルとAUDITテーブル:監査ログって普通どんな風にとるもんなんでしょう?とりあえずAUDITテーブルは、呼び出されたクラスの名称、メソッド名、パラメータを記録できるようにしました。
CREATE TABLE EMP ( EMPNO NUMERIC(4) NOT NULL PRIMARY KEY, ENAME VARCHAR(10) )
CREATE TABLE AUDIT ( CLASSNAME VARCHAR(50), METHODNAME VARCHAR(50), PARAMETERS VARCHAR(100) )
EmployeeLogic ビジネスインタフェース
public interface EmployeeLogic { void insert(int no, String name); }
EmployeeLogic Beanクラス:TransactionAttributeアノテーションは使っていない(トランザクション属性はdefaultのREQUIRED)。AroundInvokeメソッドでAuditLogicを呼び出している。SessionContextのsetRollbackOnly()を呼び出している。
@Stateless @Remote(EmployeeLogic.class) public class EmployeeLogicBean implements EmployeeLogic { @EJB private AuditLogic auditLogic; @EJB private EmployeeDao dao; @Resource private SessionContext ctx; public void insert(int no, String name) { dao.insert(no, name); ctx.setRollbackOnly(); // 意図的にロールバック } @AroundInvoke public Object audit(InvocationContext inv) throws Exception { auditLogic.audit(inv.getBean().getClass(), inv.getMethod(), inv .getParameters()); return inv.proceed(); } }
AuditLogic ビジネスインタフェース
public interface AuditLogic { void audit(Class clazz, Method method, Object[] parameters); }
AuditLogic Beanクラス:メソッドにTransactionAttributeアノテーションを指定している。TypeはREQUIRES_NEW。
@Stateless public class AuditLogicBean implements AuditLogic { @EJB private AuditDao dao; @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void audit(Class clazz, Method method, Object[] parameters) { StringBuilder b = new StringBuilder(); for(Object o : parameters) { b.append(o.toString()); b.append(", "); } b.setLength(b.length() - 2); dao.insert(clazz.getName(), method.getName(), b.toString()); } }
AuditDao ビジネスインタフェース
public interface AuditDao { void insert(String className, String methodName, String parameters); }
AuditDao Beanクラス
@Stateless public class AuditDaoBean implements AuditDao { @Resource(name = "DefaultDS") private DataSource dataSource; public void insert(String className, String methodName, String parameters) { Connection con = null; try { con = dataSource.getConnection(); PreparedStatement ps = con .prepareStatement("INSERT INTO audit VALUES(?, ?, ?)"); ps.setString(1, className); ps.setString(2, methodName); ps.setString(3, parameters); ps.executeUpdate(); } catch (SQLException e) { throw new RuntimeException(e); } finally { try { con.close(); } catch (SQLException e) { } } } }
EmployeeDaoのビジネスインタフェースとBeanクラス、それとEmployeeLogicを呼び出すクライアントは前回(Chapter 10 その2)と同じ。
デプロイしてクライアントを実行させると期待通りEMPテーブルは更新されずAUDITテーブルだけ更新されました。
AUDITテーブルのデータ
CLASSNAME METHODNAME PARAMETERS --------------------------- ---------- ---------- study.ejb.EmployeeLogicBean insert 1001, ゴン
Chapter10 その3 終了です。