1.2.6. オブジェクトのロードと格納

1.2.6. オブジェクトのロードと格納

ついにオブジェクトのロードと格納に Hibernate を使うことができます。 main() メソッドを持つ EventManager クラスを書きます:

package events;
import org.hibernate.Session;

import java.util.Date;

import util.HibernateUtil;

public class EventManager {

    public static void main(String[] args) {
        EventManager mgr = new EventManager();

        if (args[0].equals("store")) {
            mgr.createAndStoreEvent("My Event", new Date());
        }

        HibernateUtil.getSessionFactory().close();
    }

    private void createAndStoreEvent(String title, Date theDate) {

        Session session = HibernateUtil.getSessionFactory().getCurrentSession();

        session.beginTransaction();

        Event theEvent = new Event();
        theEvent.setTitle(title);
        theEvent.setDate(theDate);

        session.save(theEvent);

        session.getTransaction().commit();
    }

}

新しい Event オブジェクトを生成し、それを Hibernate に渡します。 Hibernate が SQL を処理し、データベースで INSERT を実行します。実行前にl SessionTransaction を処理するコードを確認してください。

Session は1つの作業単位 (Unit of Work) です。当分の間、話を簡単にするために、 SessionTransaction の対応を一対一と仮定します。使用しているトランザクションシステム(このケースでは JTA と共存できる単純な JDBC)からコードを保護するために、 Hibernate Session 上で利用可能な Transaction API を使用します。

sessionFactory.getCurrentSession() は何をするのでしょうか?まず、いったん SessionFactory を取得し保持すれば ( HibernateUtil のおかげで簡単です)、このメソッドを何度でも、どこからでも呼び出すことができます。 getCurrentSession() メソッドは常に「現在の」作業単位(Unit of Work)を返します。 hibernate.cfg.xml のこの機能の設定で、"thread"を指定したことを思い出してください。このため現在の作業単位のスコープは、このアプリケーションを実行する現在の Java スレッドです。しかしこれで全てではありません。作業単位の開始時と終了時にはスコープも考慮する必要があります。

Session は最初に必要となったとき、つまり最初に getCurrentSession() が呼ばれたときに開始します。そのとき Hibernate により現在のスレッドに結び付けられます。トランザクションが終了(コミットもしくはロールバック)したとき、 Hibernate もスレッドから Session を切り離し、クローズします。再び getCurrentSession() を呼ぶと、新しい Session を取得して新しい作業単位をスタートできます。この thread-bound プログラミングモデルは、コードのフレキシブルなレイヤリングを可能にするので、 Hibernate を利用する上で最も人気があります (このチュートリアルで後ほど触れますがトランザクション境界コードはデータアクセスコードとは区別されます)。

作業単位 (Unit of Work) の範囲に関して、 Hibernate の Session は1つまたはいくつかのデータベースオペレーションを実行するために使用されるべきでしょうか?上記の例は、1つのオペレーションで1つの Session を使用します。これは純粋な偶然で、例はその他のアプローチを示すほど込み入っていません。 Hibernate の Session の範囲は柔軟ですが、 全ての データベースオペレーションのために新しい Hibernate Session を使用するようにアプリケーションをデザインするべきではありません。従って、もしそれを以下の (普通の) 例で何度か見たとしても、アンチパターンである オペレーション毎の Session を考慮してください。実際の (ウェブ) アプリケーションは、このチュートリアルで後に見ることができます。

トランザクションの扱いと境界の詳しい情報については、 章 11. トランザクションと並行性 を見てください。この例ではエラー処理やロールバックも割愛します。

この最初のルーチンを実行するには、 Ant のビルドファイルに呼び出し可能なターゲットを追加しなければなりません:

<target name="run" depends="compile">
    <java fork="true" classname="events.EventManager" classpathref="libraries">
        <classpath path="${targetdir}"/>
        <arg value="${action}"/>
    </java>
</target>

action 引数の値は、ターゲットを呼ぶときにコマンドラインで設定します:

C:\hibernateTutorial\>ant run -Daction=store

コンパイルすると、 Hibernate がスタートし、設定によりますが、多くのログ出力があるはずです。その最後には以下の行があるでしょう:

[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)

これは Hibernate が実行する INSERT で、クエスチョンマークは JDBC バインドパラメータを表しています。引数としてバインドされる値を見るため、あるいはログの冗長性を減らすためには、 log4j.properties をチェックしてください。

それでは同じように格納されたイベントの一覧を見ようと思います。そのためメインメソッドにオプションを追加します:

if (args[0].equals("store")) {
    mgr.createAndStoreEvent("My Event", new Date());
}
else if (args[0].equals("list")) {
    List events = mgr.listEvents();
    for (int i = 0; i < events.size(); i++) {
        Event theEvent = (Event) events.get(i);
        System.out.println("Event: " + theEvent.getTitle() +
                           " Time: " + theEvent.getDate());
    }
}

新しい listEvents()メソッド も追加します。

private List listEvents() {

    Session session = HibernateUtil.getSessionFactory().getCurrentSession();

    session.beginTransaction();

    List result = session.createQuery("from Event").list();

    session.getTransaction().commit();

    return result;
}

ここですることは、データベースから存在するすべての Event オブジェクトをロードする HQL (Hibernate Query Language) クエリを使うことです。 Hibernate は適切な SQL を生成し、それをデータベースに送り、そのデータを使って Event オブジェクトを生成します。当然 HQL でさらに複雑なクエリを作成できます。

以下のステップで、すべての実行とテストを行います:

-Daction=list と指定して Ant を呼ぶと、これまで格納したイベントが見えるはずです。 store アクションを数回以上呼ぶことも可能です。

注記:初めて Hibernate に触れる人々の多くがここで失敗するため、 Table not found エラーメッセージに関する質問を定期的に見かけます。しかし上記のステップに従えば、 hbm2ddl が最初に実行されたときにデータベーススキーマを作成し、その後の実行においてもこのスキーマを使用するので、問題は起こらないでしょう。マッピングやデータベーススキーマを変更したときは、もう一度 hbm2ddl を有効にしてください。