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");
...
}
}