JTA

Java Transaction API

コンテナ管理の同時実行(CMT) デフォルト Container-Managed Transaction

■注意点:

1.CMTが利用できる条件はクラス境界なので、クラス内のメソッド間に@TransactionAttribute(REQUIRES_NEW)を指定しても無効

2.削除・更新処理が行う場合、トランザクション設定が必要なので、外すとTransactionRequiredExceptionが発生する

サンプル1

import javax.ejb.Singleton;

import javax.ejb.ConcurrencyManagement;

import static javax.ejb.ConcurrencyManagementType.CONTAINER;

import javax.ejb.Lock;

import javax.ejb.LockType.WRITE;

@DependsOn("BootstrapEjb","InitialCacheEjb")

@Startup // アプリの起動時に初期化

@Singleton

@Lock(LockType.READ) // 共有ロック

@ConcurrencyManagement(CONTAINER) //デフォルト

public class MyEJB {

private Map<Long, Object> cache = new HashMap<Long, Object>();

//@AccessTimeout(0) //同時実行の禁止

public void addToCache(long id, Object object) {

if (!cache.containsKey(id)) {

cache.put(id, object);

}

}

public void removeFromCache(long id) {

if (cache.containsKey(id)) {

cache.remove(id);

}

}

// 排他ロックが解除されない場合はConcurrentAccessTimeoutException

@AccessTimeout(value=30, unit=TimeUnit.SECONDS)

@Lock(LockType.WRITE) // 排他ロック

public Object getFromCache(long id) {

if (cache.containsKey(id)) {

return cache.get(id);

}

return null;

}

}

@Lock(LockType.WRITE) 順序実行 default

@Lock(LockType.READ) 並行実行

サンプル2

@Stateless

//@TransactionAttribute(TransactionAttributeType.SUPPORTS)

public class MyEJB {

...

@EJB

private BookEJB bookEJB;

@Resource

private SessionContext ctx;

//@TransactionAttribute(TransactionAttributeType.REQUIRED) //デフォルト

public Book createBook(Book book) {

em.persist(book);

bookEJB.addBook(book);

if (...) {

// ロールバックの方法1

ctx.setRollbackOnly();

// ロールバックの方法2

throw new MyBizException();

}

return book;

}

}

@ApplicationException(rollback=true)

public class MyBizException extends Exception {

public MyBizException() { }

public MyBizException(String message) {

super(message);

}

}

下記の属性を付加したaddBook()メソッド

アプリケーション例外

システム例外

トランザクションのロールバックが指示される

RuntimeException、java.rmi.RemoteException、javax.ejb.EJBExceptionのサブクラス

例:ArithmeticException、ClassCastException、IllegalArgumentException、NullPointerExceptionなど

Bean管理の同時実行(BMT) Bean-Managed Transaction

サンプル1

@Singleton

@ConcurrencyManagement(ConcurrencyManagementType.BEAN)

public class MyEJB {

private Map<Long, Object> cache = new HashMap<Long, Object>();

public synchronized void addToCache(long id, Object object) {

if (!cache.containsKey(id)) {

cache.put(id, object);

}

}

public synchronized void removeFromCache(long id) {

if (cache.containsKey(id)) {

cache.remove(id);

}

}

public Object getFromCache(long id) {

if (cache.containsKey(id)) {

return cache.get(id);

}

return null;

}

}

サンプル2

import javax.transaction.UserTransaction;

@Stateless

@TransactionManagement(TransactionManagementType.BEAN)

public class MyEJB {

...

@EJB

private BookEJB bookEJB;

@Resource

private UserTransaction ut;

public Book createBook(Book book) {

try {

ut.begin();

em.persist(book);

bookEJB.addBook(book);

if (...) {

ut.rollback();

} else {

ut.commit();

}

} catch (Exception e) {

ut.rollback();

}

...

return book;

}

}

★Control transaction

CMTの場合

public void mergeEntity() {

MyEntity entity = new MyEntity("xxx", "yyy");

em.persist(entity);

try{

this.tryMergeEntity(entity);

}catch(UpdateException e) { // Not rollback

entity.setContent("");

entity.setCode("ERROR");

}

}

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)

public void tryMergeEntity(final MyEntity entity) throws UpdateException {

entity.setContent("zzz");

em.merge(entity); // Because new transaction

try{

em.flush();

}catch(PersistenceException e) { // Rollback

throw new UpdateException();

}

}

//rollback=false by default

public class UpdateException extends Exception {

}

BMTの場合

public void mergeEntity() throws Exception {

ut.begin();

MyEntity entity = new MyEntity("xxx", "yyy");

em.persist(entity);

ut.commit();

ut.begin();

entity.setContent("zzz");

em.merge(entity);

try{

em.flush();

}catch(PersistenceException e) {

ut.rollback();

ut.begin();

entity.setContent("");

entity.setCode("ERROR");

em.merge(entity);

ut.commit();

}

}

★JDBC vs EJB

JDBC

public class MyLogic {

public void method() {

Connection con = dataSource.getConnection();

try {

con.setAutoCommit(false);

dao.method(con);

con.commit();

} catch (Exception e) {

con.rollback();

throw e;

} finally {

con.setAutoCommit(true);

con.close();

}

}

}

public class MyDao {

public void method(Connection con) {

...

}

}

EJB 3.0

@Stateless

@TransactionManagement(CONTAINER) //default

@TransactionAttribute(REQUIRED) //default

public class MyLogic {

public void method() { //開始~終了は一つのTransaction

dao.method();

}

}

@Stateless

@TransactionManagement(CONTAINER)

@TransactionAttribute(REQUIRED)

public class MyDao {

public void method() { //呼び出し元と同じTransaction

...

}

}

★InterceptorによるTransaction設定

public static class Interceptor {

@Resource

private SessionContext sessionContext;

@AroundInvoke

public Object invoke(InvocationContext context) throws Exception {

sessionContext.setRollbackOnly();

return context.proceed();

}

}

★JTA 1.2 (Java EE7)

Java EE7以前のトランザクション制御には、EJBとして管理される。

簡単なサンプル

import javax.transaction.Transactional;

public class MyCDIBean {

@Transactional

public void insert(String content) {

...

}

}

複雑なサンプル

@Transactional(Transactional.TxType.MANDATORY)

public class MyCDIBean {

@Transactional(Transactional.TxType.NEVER)

public void withoutTransaction() throws Exception {

...

}

@Transactional(

dontRollbackOn = AException.class,

rollbackOn = BException.class)

public void withTransaction() throws Exception {

...

}

}

★Get current JTA transaction status from EJB

BMTの場合

@Resource

private UserTransaction ut;

ut.getStatus();

CMTの場合

@Resource

private TransactionSynchronizationRegistry tsr;

tsr.getTransactionStatus();

★TransactionSynchronizationRegistryについて

public class MyInterceptor {

@Resource

private TransactionSynchronizationRegistry tsr;

@AroundInvoke

public Object putResource(InvocationContext ic) throws Exception{

...

tsr.putResource("xxxKey", someObject);

return ic.proceed();

}

}

@Stateless

@Interceptors(MyInterceptor.class)

public class ServiceFacadeImpl implements ServiceFacade {

@EJB

private Service service;

public void perform(){

service.doSomething();

}

}

@Stateless

public class ServiceImpl implements Service {

@Resource

private TransactionSynchronizationRegistry tsr;

public void doSomething() {

Object someObject = tsr.getResource("xxxKey");

...

}

}