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 のバインド定義