Hibernate 入門記 セッションその8 後始末とテンプレ

お待たせいたしました.今回はついに「9.7. Ending a Session」へと進みます.待望の,セッションの後始末です!!
いやぁ,たかが後始末とはいえ,入門記開始以来○ヶ月にわたってセッションの後始末をすることなく過ごしてきましたからねぇ.まさに「心より恥じる」ですよ.
しかし,それも昨日までの話.今日からは,きれいに後始末するのだ.残念!!!!
ということで,まずはセッションを終了する過程は次のようになっているとのことです.

  1. セッションのフラッシュ.
  2. トランザクションのコミット.
  3. セッションのクローズ.
  4. 例外のハンドリング.

なぁんだ,セッションのクローズ以外はもうやってますね.残念!!!!
…イヤべつに残念でもなんでもないのですが


そんなわけでまずは最初のステップ,「9.7.1. Flushing the Session」です.昨日学習したばっかりなのですが,再度登場です.
なんでも,Transaction を使ってトランザクションをコミットする場合には,セッションのフラッシュを明示的にしなくてもよいのだとか.それはおそらく,セッションのフラッシュモードが AUTO または COMMIT の場合の話だと思いますが.
そうでなければ Session#flush() を呼び出せとのこと.らじゃ.


そして次のステップは「9.7.2. Committing the database transaction」です.
トランザクションをコミットまたはロールバックする方法は,二通りあります.
一つめは Transaction を使う方法で,次のメソッドを呼び出します.

    • void commit()
    • void rollback()

もう一つは JDBC コネクションを使う方法です.JDBC コネクションは,Session#connection() により取得することが出来ます.
そうかぁ,Session#connection() は単に JDBC コネクションを返すのですね.それを直接使って commit()rollback() しても,Hibernate は介入しないので,勝手にフラッシュしてくれたりはしなかったのかぁ.残念!!!!
ということは,基本的に Transaction を使うべきということでしょう.こちらを使った場合は,JTA を利用した分散トランザクションにも対応できるとのことです.
なお,トランザクションロールバックした場合は,即座にセッションを終了しろとのことです.そうすれば,Hibernate の内部状態の一貫性が保証されるとのこと.らじゃ.


そしてついに!! 「9.7.3. Closing the Session」です.
セッションを終了するには,Session

    • Connection close()

を使用します.それだけですか.こんな簡単なことを延々今日まで放置してきたのか... 無念だ.
ちなみにこの Session#close() ですが,Hibernate が管理する JDBC コネクションを使っている場合には,JDBC コネクションはプールに戻されます.SessionFactory#openSession(Connection)JDBC コネクションを与えた場合は,Session#close() は何もしないで,元の JDBC コネクションが返されます.そのコネクションのクローズは自分でやれということですね.


そして最後のステップ,「9.7.4. Exception handling」です.
もしセッションが例外をスローしたら,トランザクションロールバックして,セッションをクローズして,セッションのインスタンスを破棄しろとのことです.あらら,そうだったんだ...
そんなわけで,Transaction を使った場合,JDBC コネクションのトランザクションを使った場合,JTA を使った場合の推奨イディオムが掲載されています.テンプレですね.


ということで早速お試ししましょう.
今回もテーブル,モデルの永続クラス,マッピングファイルは前々回と同じです.
手抜きじゃありませんよぉ〜.
そんなわけで (いみふめ),今回はテンプレを作りましょう.
まずはテンプレからコールバックされる interface

package study;
import net.sf.hibernate.Session;

public interface HibernateCallback {
    Object doProcess(Session session) throws Exception;
}

そしてテンプレート.

package study;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.Transaction;
import net.sf.hibernate.cfg.Configuration;

public class HibernateTemplate {
    SessionFactory factory;

    public HibernateTemplate() throws HibernateException {
        this(new Configuration());
    }
    public HibernateTemplate(Configuration config) throws HibernateException {
        factory = config.configure().buildSessionFactory();
    }

    public Object process(HibernateCallback callback) throws Exception {
         Session session = factory.openSession();
        try {
            Transaction tx = session.beginTransaction();
            try {
                Object result = callback.doProcess(session);
                tx.commit();
                return result;
            }
            catch (Exception e) {
                tx.rollback();
                throw e;
            }
        }
        finally {
            session.close();
        }
    }
}

名前がパクリなのは気にしない気にしない.
リファレンスのテンプレとは微妙に異なっていますが,自分はこんな風に,リソース (この場合は SessionTransaction) を獲得するたびに try ブロックを書くのが好きです.多少入れ子が深くなっても (深すぎると思ったらメソッド分割!!).
Transaction#commit() の呼び出しでセッションのフラッシュが行われることを前提に,Session#flush() は明示的に呼び出していません.もし,HibernateCallback(Session) の中でフラッシュモードを変更した場合は,自己責任でフラッシュするものとしました.
ということで,このテンプレを使った実行用のクラス.

package study;
import java.util.Iterator;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;

public class Main {
    public static void main(String[] args) {
        try {
            HibernateTemplate template = new HibernateTemplate();

            System.out.println("*** first session ***");
            template.process(new HibernateCallback() {
                public Object doProcess(Session session) throws Exception {
                    Model yuri = new Model("Yuri Ebihara", "CanCam");
                    session.save(yuri);
                    Model yu = new Model("Asami Usuda", "CanCam");
                    session.save(yu);
                    Model sayo = new Model("Sayo Aizawa", "ViVi");
                    session.save(sayo);
                    return null;
                }
            });

            System.out.println("*** second session ***");
            template.process(new HibernateCallback() {
                public Object doProcess(Session session) throws Exception {
                    Iterator it = session.find("from study.Model").iterator();
                    while (it.hasNext()) {
                        System.out.println(it.next());
                    }
                    return null;
                }
            });
        }
        catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

最初のセッションで永続オブジェクトを作成して,次のセッションでそれを問い合せしているだけです.
こいつを実行!!!!

*** first session ***
onSave() : Yuri Ebihara
Hibernate: insert into Model (name, magazine, id) values (?, ?, null)
Hibernate: call identity()
onSave() : Asami Usuda
Hibernate: insert into Model (name, magazine, id) values (?, ?, null)
Hibernate: call identity()
onSave() : Sayo Aizawa
Hibernate: insert into Model (name, magazine, id) values (?, ?, null)
Hibernate: call identity()
*** second session ***
Hibernate: select model0_.id as id, model0_.name as name, model0_.magazine as magazine 
           from Model model0_
onLoad() : Yuri Ebihara
onLoad() : Asami Usuda
onLoad() : Sayo Aizawa
Yuri Ebihara (CanCam)
Asami Usuda (CanCam)
Sayo Aizawa (ViVi)

おっけー.
ふぅー,これでやっとこさ後ろめたさがなくなって気分すっきり♪
にしても,なんでここまで引っ張る羽目になったんだっけ? 覚えてないなぁ.まいっか.