SqlSession CLOSE忘れを回避するには、、(1)

iBATIS3(mybatis) の SqlSession は、インスタンスの管理を1箇所にすべきである。
 (長いので3回に分けて投稿)
DB接続セッションをCLOSE し忘れるコーディングの可能性があるからだ。
1人で開発してるなら自分だけ注意すればその心配はないが、人によってはCLOSEを忘れる人がいる
CLOSE 忘れはとんでもないことになってしまう。
そこで、過去書いたものを修正することに、、、
http://blog.zaq.ne.jp/oboe2uran/article/415/
ここで、インターセプタをトランザクションあるメソッドに限定していた。
クエリしかない、コミットの必要がないメソッドのインターセプタを用意する。
計測してみたが、インターセプタをしてもほとんど影響ないスピードだった。

:過去からの変更点、

クエリのみの実行のインターセプタを別に用意すること。

@Transaction は、フィールドに付けることができないようにしてメソッド
アノテーションのみにする。

SqlSession もしくは、SqlMapperインタフェース フィールド宣言に付ける
アノテーションを、用意する。→ @ControleSession
TransactionExecutor と、新しく作るクエリ用のインターセプタ は、
ControleSession を見つけ、
SqlSession を
Injectするようにする。このため、インターセプトされるクラスはSqlSessionを
もつための抽象クラスを用意した。

import org.apache.ibatis.session.SqlSession;
public abstract class IBatisDao{
   @ControleSession private SqlSession sqlSession;
   public SqlSession getSqlSession(){
      return this.sqlSession;
   }
   public <T> T getMapper(Class<T> t){
      return this.sqlSession.getMapper(t);
   }
}

ControleSession をpublic にしない!IBatisDao だけが使うことにする
ことで ControleSession を意識させない
ControleSession と IBatisDao は同じpackage 上にある


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface ControleSession{
}

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Transaction{
}

これらでトランザクションインターセプタは、以下のようになる

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import com.google.inject.Inject;
/**
 * トランザクション インターセプター.
 */

final class TransactionExecutor implements MethodInterceptor{
   private SqlSessionFactory sqlSessionFactory;
   @Inject
   public TransactionExecutor(SqlSessionFactory sqlSessionFactory){
      this.sqlSessionFactory = sqlSessionFactory;
   }
   @Override
   public Object invoke(MethodInvocation m) throws Throwable{

      Object rtn = null;
      SqlSession session = null;
      try{
      session = this.sqlSessionFactory.openSession(false);
      Field setField = this.getAnotatedField(ControleSession.class
,m.getMethod().getDeclaringClass());
      setField.setAccessible(true);
      setField.set(m.getThis(),session);
      rtn = m.proceed();
      session.commit();
      }catch(Exception e){
         if (session != null){
            session.rollback();
         }
         throw e;
      }finally{
         if (session != null) session.close();
      }
      return rtn;
   }
   
private Field getAnotatedField(Class<? extends Annotation> a,Class<?> cls){
      Field rtn=null;
      Class<?> tcls = cls;
      while(!tcls.equals(java.lang.Object.class)){
         Field fls = tcls.getDeclaredFields();
         for(int i=0;i < fls.length;i++){
            Annotation
 as = fls[i].getAnnotations();
            if (as != null){
               for(int k=0;k < as.length;k++){
                  if (as[k].annotationType().equals(a)){
                     rtn = fls[i];
                     i = fls.length;
                     break;
                  }
               }
            }
         }
         if (rtn != null) return rtn;
         tcls = tcls.getSuperclass();
      }
      throw new RuntimeException("must be IBatisDao extends");
   }

}

次回はクエリ用のインターセプタとGoogle guice のバインド定義