1.3.3. 関連を働かせる

1.3.3. 関連を働かせる

EventManager の新しいメソッドで人々とイベントを一緒にしましょう:

private void addPersonToEvent(Long personId, Long eventId) {

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

    Person aPerson = (Person) session.load(Person.class, personId);
    Event anEvent = (Event) session.load(Event.class, eventId);

    aPerson.getEvents().add(anEvent);

    session.getTransaction().commit();
}

PersonEvent をロードした後、普通のコレクションメソッドを使って単純にそのコレクションを修正してください。ご覧のとおり update()save() の明示的な呼び出しはありません。 Hibernate は、修正されたことにより更新する必要のあるコレクションを自動的に検知します。これは 自動ダーティチェック と呼ばれ、オブジェクトの名前や date プロパティを修正することで試すことも可能です。それらが 永続 状態にある限り、つまり特定の Hibernate Session にバインドされている限り (例えば作業単位 (Unit of Work) の中で単にロードまたはセーブされた)、 Hibernate はどんな変更もモニターし、遅延書き込み (write-behind) で SQL を実行します。通常、作業単位 (Unit of Work) の最後にだけ行われるデータベースとメモリの状態を同期させる処理は、 フラッシュ と呼ばれます。このコードでは、作業単位 (Unit of Work) はデータベーストランザクションのコミット(もしくはロールバック)で終了します。これは、 CurrentSessionContext クラスに対して thread を設定したためです。

異なる作業単位 (Unit of Work) で人々とイベントをロードすることも当然できます。そうでなければ、永続状態にないとき(以前に永続であったなら、この状態を 分離(detached) と呼びます)、 Session の外部でオブジェクトを修正します。分離されるときにはコレクションを変更することも可能です:

private void addPersonToEvent(Long personId, Long eventId) {

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

    Person aPerson = (Person) session
            .createQuery("select p from Person p left join fetch p.events where p.id = :pid")
            .setParameter("pid", personId)
            .uniqueResult(); // Eager fetch the collection so we can use it detached

    Event anEvent = (Event) session.load(Event.class, eventId);

    session.getTransaction().commit();

    // End of first unit of work

    aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached

    // Begin second unit of work

    Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
    session2.beginTransaction();

    session2.update(aPerson); // Reattachment of aPerson

    session2.getTransaction().commit();
}

update の呼び出しは分離オブジェクトを再び永続化します。これは、新しい作業単位 (Unit of Work) にバインドすると言えるでしょう。そのため分離の間に加えられたどのような修正もデータベースにセーブできます。エンティティオブジェクトのコレクションへの修正(追加・削除)も同様にセーブできます。

これは今はあまり使いみちがありませんが、自分のアプリケーションの設計に組み込むことができる重要なコンセプトです。それではこのエクササイズの最後に、 EventManager のメインメソッドに新しいアクションを追加してコマンドラインから呼び出してみましょう。人やイベントの識別子が必要なら、 save() メソッドが返してくれます (場合によっては識別子を返すためにメソッドを修正する必要があるかもしれません)。

else if (args[0].equals("addpersontoevent")) {
    Long eventId = mgr.createAndStoreEvent("My Event", new Date());
    Long personId = mgr.createAndStorePerson("Foo", "Bar");
    mgr.addPersonToEvent(personId, eventId);
    System.out.println("Added person " + personId + " to event " + eventId);
}

これは同じように重要な2つのクラス、つまり2つのエンティティ間の関連の例でした。前に述べたように、典型的なモデルには、普通「比較的重要ではない」他のクラスと型があります。これまでに見たような intString のようなものです。このようなクラスを 値型 と言います。このインスタンスは特定のエンティティに 依存 します。この型のインスタンスは独自の ID を持ちませんし、エンティティ間で共有されることもありません (ファーストネームが同じだったとしても、2人の人は同じ firstname オブジェクトを参照しません)。値型はもちろん JDK 内に見つかりますが、それだけではなく (実際、 Hibernate アプリケーションにおいてすべての JDK クラスは値型と見なせます)、 例えば AddressMonetaryAmount のような独自の依存クラスを書くこともできます。

値型のコレクションを設計することもできます。これは他のエンティティへの参照のコレクションとは概念的に非常に異なりますが、 Java ではほとんど同じように見えます。