スキップしてメイン コンテンツに移動

コード晒し:GAEでのトランザクション


たまには自分のコードでも晒してみようかと思います。
GAE/J でトランザクションを管理している部分です。



自分は今のところ特にフレームワークを使っていないのですが
(というか、使うほどのものを作っていない)
そのためこういう足回り部分もコーディングすることになります。
でもなるべく簡単に済まそうと思って書いたコードです。


まずは処理の呼び出し部分のイメージです。
色とか付いてなくてすいません。

public class HogeService extends BaseService(※後述) {

 public static void hogehoge(String x, int y) {
  begin();
  try {
   // ここに処理
   commit();
  } finally {
   end();
  }

  ...

 }
}

begin() でトランザクション開始、
処理が全部終わったら commit() 、
そして end() 、
という流れです。

しかし途中で例外が発生したら commit() せずに end() に行く、というコードです。
あれ? rollback は?というと、次の BaseService クラスで実装しています。
( BaseService というクラス名はおそらく適当ではありませんが・・・よく分からないので許してください)

public class BaseService {

 protected static void begin() {
  TransactionManager.begin(); ※TransactionManagerは後述
 }

 protected static void commit() {
  TransactionManager.commit();
 }

 protected static void end() {
  if (TransactionManager.isActive()) {
   TransactionManager.rollback();
  }
 }

}

ここでは end() メソッドでトランザクションがコミットされているかを判定して
されていなければロールバックしています。


最後に TransactionManager クラスです。
GAEで作っているので通常のDB接続とはちょっと違いますが
参考になるでしょうか。

public class TransactionManager {

 private static ThreadLocal<DatastoreService> tlDs
    = new ThreadLocal<DatastoreService>();

 private static ThreadLocal<Transaction> tlTx
    = new ThreadLocal<Transaction>();

 public static void begin() {
  DatastoreService ds
   = DatastoreServiceFactory.getDatastoreService();

   // オプションなど必要に応じて
  TransactionOptions op
   = TransactionOptions.Builder.withXG(true);

  Transaction tx = ds.beginTransaction(op);

  tlDs.set(ds);
  tlTx.set(tx);
 }

 public static void commit() {
  tlTransaction.get().commit();
 }

 public static void rollback() {
  tlTransaction.get().rollback();
 }

 public static boolean isActive() {
  return tlTransaction.get().isActive();
 }

 public static DatastoreService getDatastoreService() {
  return tlDatastoreService.get();
 }

}

ここで ThreadLocal なるクラスが出てきます。
これはスレッドに対してオブジェクトを紐付けることのできるクラスです。

遅い遅いと言われる ThreadLocal ですが
日に何億トランザクションというアクセスでもなければ
まず全く問題ないと感じます。

この ThreadLocal を使うことで
コネクション関連のオブジェクトをあちこち受け渡しせずにコードを記述できます。
どうも一番最初の HogeService で↓こういうのを書きたくないと思うのです

Transaction tx = begin();
try {
  処理1(tx);
  処理2(tx, 引数1, 引数2);
  処理3(tx, 引数);
  tx.commit();
} finally {
  tx.end();
}


ちなみに基底となる Dao クラスを作るとしたらこんな感じです。

public class BaseDao {

 protected static DatastoreService ds() {
  return TransactionManager.getDatastoreService();
 }

 ...

}



以上、ちょっと作ってみたコードを公開してみたのですが、いかがでしょうか。
もし参考になったら+1でもしてやってください。


 

記事一覧

もっと見る