arcanum_jp’s blog

おっさんの日記

プロジェクトでWicketを使う際にしていること・1リクエスト1トランザクション編

昔途中まで書いていたので公開してなくて後悔したので公開。

Wicketに限らずWebアプリを作るときにコネクションはJNDI経由で取るように作ると思う。でも単純に取ってしまうと1リクエストの中で複数のコネクションが使われてしまったり、効率が悪い。例えばWeb画面でボタンの処理について1リクエスト1コネクションになるようにしてコミットポイントをイベントの処理が終わった後に自動的にしたいとか(当然例外が送出されたらコミットしないとか)クローズを利用者に意識させないようにしたいとか言ったのは結構あるよね。そういう方法です。

まずコネクションを管理するクラスを作ります。このクラスはWicketでイベントが発火したときにコネクションをそのイベント用に取得し、イベントが終了するとデータベースプーリングに戻す処理をします。

public class DBAccess extends AbstractRequestCycleListener{
    private DBAccess(){
        // 封印!
    }

    public static final DBAccess getInstance(){
        return new DBAccess();  // 本当はシングルチョン(トン)!
    }

    private static final ThreadLocal<Connection> CON = new ThreadLocal<Connection>();

    @Override
    public void onBeginRequest(RequestCycle cycle) {
        CON.set(getConnectionJNDI());  // JNDIでコネクション取ってくる
    }

    @Override
    public void onEndRequest(RequestCycle cycle) {

        Connection con = null;
        try {
            con = CON.get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        finally{
            try {
                if(con!=null){
                    con.close();
                    CON.remove();
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static final Connection getConnection(){
        return CON.get();
    } 

    public static final Connection getConnectionJNDI(){

        Connection ret = null;
        try {

            InitialContext initCon = new InitialContext(); //(1)
            DataSource ds = (DataSource)initCon.lookup("java:comp/env/jdbc/" + データベース名); 
            ret = ds.getConnection(); 
            ret.setAutoCommit(false);  

        }
        catch (Exception e) {
            throw new RuntimeException("コネクション取得に失敗", e);
        }

        return ret;
    }


 これをWebApplication#init()で紐つけておきます。

public class WebApp extends WebApplication{

    boolean _init;

    @Override
    protected void init() {
        ... 省略         
        // DBコネクションのリクエスト紐付け
        getRequestCycleListeners().add(DBAccess.getInstance());
    }
}


 つぎに普通にイベント処理を記述します。そのなかで上記のクラスから取得したコネクションはリクエストを通じてすべて同じになりますので、プログラマーはイベント内でトランザクションを意識する必要がありません。あと、プログラマはコネクションをクローズする事を考える必要もなくなります。

public class HogePage extends WebPage {

    private Button _hagebutton = new Button("hagebutton"){
        public void onSubmit(){
            Connection con = DBAccess.getConnection();
            ... 処理を書く
            onHogeHage();
            con.commit();   // ※左記は例外処理を無視しているので注意
        }
    }

    private void onHogeHage(){
        // 呼び出し先でコネクションを取得しても同一トランザクション内になる
        Connection con = DBAccess.getConnection();
        ... 
    }

}