HIBERNATE - Relational Persistence for Idiomatic Java
Hibernate Reference Documentation
Hibernateレファレンスドキュメンテーション
3.2.2
Table of Contents(コンテンツテーブル)
Preface(序文)
1. Introduction to Hibernate(Hibernateの紹介)
2. Architecture(アーキテクチャ)
3. Configuration(設定)
4. Persistent Classes(永続クラス)
6. Collection Mapping(コレクションのマッピング)
7. Association Mappings(関連マッピング)
8. Component Mapping(コンポーネントのマッピング)
9. Inheritance Mapping(継承マッピング)
10. Working with objects(オブジェクトを扱う)
11. Transactions And Concurrency(トランザクションと並行性)
12. Interceptors and events(インターセプタとイベント)
13. Batch processing(バッチ処理)
15. Criteria Queries(Criteriaクエリ)
17. Filtering data(データのフィルターリング)
19. Improving performance(パフォーマンスの改善)
20. Toolset Guide(ツールセットガイド)
21. Example: Parent/Child(例:親/子)
21.1. A note about collections(コレクションに関する注意)
21.2. Bidirectional one-to-many(双方向一対多)
21.3. Cascading lifecycle(ライフサイクルのカスケード)
21.5. Conclusion(結論)
Chapter 21. Example: Parent/Child(第21章:例:親/子)
One of the very first things that new users try to do with Hibernate is to model a parent / child type relationship. There are two different approaches to this. For various reasons the most convenient approach, especially for new users, is to model both Parent andChild as entity classes with a <one-to-many> association from Parent to Child. (The alternative approach is to declare the Child as a <composite-element>.) Now, it turns out that default semantics of a one to many association (in Hibernate) are much less close to the usual semantics of a parent / child relationship than those of a composite element mapping. We will explain how to use a bidirectional one to many association with cascades to model a parent / child relationship efficiently and elegantly. It's not at all difficult!
新規ユーザがHibernateを使って、最初に扱うモデルの一つに、親子型のモデル化があります。このモデル化には二つのアプローチが存在します。とりわけ新規ユーザにとって、さまざまな理由から最も便利だと思われるアプローチは、親 から 子供 への <one-to-many> 関連により 親 と 子供 の両方をエンティティクラスとしてモデリングする方法です(もう一つの方法は、子供 を <composite-element> として定義するものです)。これで(Hibernateにおける)一対多関連のデフォルトのセマンティクスが、通常の複合要素のマッピングよりも、親子関係のセマンティクスから遠いことがわかります。それでは親子関係を効率的かつエレガントにモデリングするために、カスケード操作を使った双方向一対多関連の扱い方を説明します。これはまったく難しいものではありません。
21.1. A note about collections(コレクションに関する注意)
Hibernate collections are considered to be a logical part of their owning entity; never of the contained entities. This is a crucial distinction! It has the following consequences:
Hibernateのコレクションは自身のエンティティの論理的な部分と考えられ、決して包含するエンティティのものではありません。これは致命的な違いです!これは以下のような結果になります:
? When we remove / add an object from / to a collection, the version number of the collection owner is incremented.
オブジェクトをコレクションから削除、またはコレクションに追加するとき、コレクションのオーナーのバージョン番号はインクリメントされます。
? If an object that was removed from a collection is an instance of a value type (eg, a composite element), that object will cease to be persistent and its state will be completely removed from the database. Likewise, adding a value type instance to the collection will cause its state to be immediately persistent.
もしコレクションから削除されたオブジェクトが値型のインスタンス(例えばコンポジットエレメント)だったならば、そのオブジェクトは永続的ではなくなり、その状態はデータベースから完全に削除されます。同じように、値型のインスタンスをコレクションに追加すると、その状態はすぐに永続的になります。
? On the other hand, if an entity is removed from a collection (a one-to-many or many-to-many association), it will not be deleted, by default. This behaviour is completely consistent - a change to the internal state of another entity should not cause the associated entity to vanish! Likewise, adding an entity to a collection does not cause that entity to become persistent, by default.
一方、もしエンティティがコレクション(一対多または多対多関連)から削除されても、デフォルトではそれは削除されません。この動作は完全に一貫しています。すなわち、他のエンティティの内部状態を変更しても、関連するエンティティが消滅すべきではないということです。同様に、エンティティがコレクションに追加されても、デフォルトではそのエンティティは永続的にはなりません。
Instead, the default behaviour is that adding an entity to a collection merely creates a link between the two entities, while removing it removes the link. This is very appropriate for all sorts of cases. Where it is not appropriate at all is the case of a parent / child relationship, where the life of the child is bound to the lifecycle of the parent.
その代わりに、デフォルトの動作では、エンティティをコレクションに追加すると単に二つのエンティティ間のリンクを作成し、一方エンティティを削除するとリンクも削除します。これはすべてのケースにおいて非常に適切です。これが適切でないのは親/子関係の場合です。この場合子供の生存は親のライフサイクルに制限されるからです。
21.2. Bidirectional one-to-many(双方向一対多)
Suppose we start with a simple <one-to-many> association from Parent to Child.
Parent から Child への単純な <one-to-many> 関連から始めるとします。
<set name="children">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
If we were to execute the following code
以下のコードを実行すると、
Parent p = .....;
Child c = new Child();
p.getChildren().add(c);
session.save(c);
session.flush();
Hibernate would issue two SQL statements:
Hibernateは二つのSQL文を発行します:
? an INSERT to create the record for c
cに対するレコードを生成するINSERT
? an UPDATE to create the link from p to c
pからcへのリンクを作成するUPDATE
This is not only inefficient, but also violates any NOT NULL constraint on the parent_idcolumn. We can fix the nullability constraint violation by specifying not-null="true" in the collection mapping:
これは非効率的なだけではなく、parent_id カラムにおいて NOT NULL 制約に違反します。コレクションのマッピングで not-null="true" と指定することで、null制約違反を解決することができます:
<set name="children">
<key column="parent_id" not-null="true"/>
<one-to-many class="Child"/>
</set>
However, this is not the recommended solution.
しかしこの解決策は推奨できません。
The underlying cause of this behaviour is that the link (the foreign key parent_id) fromp to c is not considered part of the state of the Child object and is therefore not created in the INSERT. So the solution is to make the link part of the Child mapping.
この動作の根本的な原因は、p から c へのリンク(外部キー parent_id)は Child オブジェクトの状態の一部とは考えられず、そのため INSERT によってリンクが生成されないことです。ですから、解決策はリンクをChildマッピングの一部にすることです。
<many-to-one name="parent" column="parent_id" not-null="true"/>
(We also need to add the parent property to the Child class.)
(また Child クラスに parent プロパティを追加する必要があります。)
Now that the Child entity is managing the state of the link, we tell the collection not to update the link. We use the inverse attribute.
それでは Child エンティティがリンクの状態を制御するようになったので、コレクションがリンクを更新しないようにしましょう。それには inverse 属性を使います。
<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
The following code would be used to add a new Child
以下のコードを使えば、新しい Child を追加することができます。
Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
c.setParent(p);
p.getChildren().add(c);
session.save(c);
session.flush();
And now, only one SQL INSERT would be issued!
これにより、SQLの INSERT 文が一つだけが発行されるようになりました!
To tighten things up a bit, we could create an addChild() method of Parent.
もう少し強化するには、Parent の addChild() メソッドを作成します。
public void addChild(Child c) {
c.setParent(this);
children.add(c);
}
Now, the code to add a Child looks like
Child を追加するコードはこのようになります。
Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.save(c);
session.flush();
21.3. Cascading lifecycle(ライフサイクルのカスケード)
The explicit call to save() is still annoying. We will address this by using cascades.
明示的に save() をコールするのはまだ煩わしいものです。これをカスケードを使って対処します。
<set name="children" inverse="true" cascade="all">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
This simplifies the code above to
これにより先ほどのコードをこのように単純化します
Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.flush();
Similarly, we don't need to iterate over the children when saving or deleting a Parent. The following removes p and all its children from the database.
同様に Parent を保存または削除するときに、子供を一つ一つ取り出して扱う必要はありません。以下のコードは p を削除し、そしてデータベースからその子供をすべて削除します。
Parent p = (Parent) session.load(Parent.class, pid);
session.delete(p);
session.flush();
However, this code
しかしこのコードは
Parent p = (Parent) session.load(Parent.class, pid);
Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c);
c.setParent(null);
session.flush();
will not remove c from the database; it will ony remove the link to p (and cause a NOT NULL constraint violation, in this case). You need to explicitly delete() the Child.
データベースから c を削除しません。p へのリンクを削除する(そしてこのケースではNOT NULL 制約違反を引き起こす)だけです。Child の delete() を明示する必要があります。
Parent p = (Parent) session.load(Parent.class, pid);
Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c);
session.delete(c);
session.flush();
Now, in our case, a Child can't really exist without its parent. So if we remove a Childfrom the collection, we really do want it to be deleted. For this, we must usecascade="all-delete-orphan".
今このケースでは実際に Child が親なしでは存在できないようになりました。そのため、もしコレクションから Child を取り除く場合、これも削除したいです。そのためにはcascade="all-delete-orphan" を使わなければなりません。
<set name="children" inverse="true" cascade="all-delete-orphan">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
Note: even though the collection mapping specifies inverse="true", cascades are still processed by iterating the collection elements. So if you require that an object be saved, deleted or updated by cascade, you must add it to the collection. It is not enough to simply call setParent().
注意:コレクションのマッピングで inverse="true" と指定しても、コレクションの要素のイテレーションによって、依然カスケードが実行されます。そのためもしカスケードでオブジェクトをセーブ、削除、更新する必要があるなら、それをコレクションに追加しなければなりません。単に setParent() を呼ぶだけでは不十分です。
21.4. Cascades and unsaved-value(カスケードと unsaved-value)
Suppose we loaded up a Parent in one Session, made some changes in a UI action and wish to persist these changes in a new session by calling update(). The Parent will contain a collection of childen and, since cascading update is enabled, Hibernate needs to know which children are newly instantiated and which represent existing rows in the database. Lets assume that both Parent and Child have genenerated identifier properties of type Long. Hibernate will use the identifier and version/timestamp property value to determine which of the children are new. (See Section 10.7, "Automatic state detection".) In Hibernate3, it is no longer necessary to specify an unsaved-valueexplicitly.
Parent が、ある Session でロードされ、UIのアクションで変更が加えられ、update() を呼んでこの変更を新しいセッションで永続化したいとします。Parent が子供のコレクションを持ち、カスケード更新が有効になっているため、Hibernateはどの子供が新しくインスタンス化されたか、どれがデータベースの既存の行に相当するのかを知る必要があります。Parent と Child の両方が java.lang.Long 型の識別プロパティを生成したとしましょう。Hibernateはどの子供が新しいものかを決定するために識別プロパティの値を使います(versionやtimestampプロパティも使えます。Section 10.7, "Automatic state detection"参照)。Hibernate3になって、明示的に unsaved-value を指定する必要はなくなりました。
The following code will update parent and child and insert newChild.
以下のコードは parent と child を更新し、newChild を挿入します。
//parent and child were both loaded in a previous session
parent.addChild(child);
Child newChild = new Child();
parent.addChild(newChild);
session.update(parent);
session.flush();
Well, that's all very well for the case of a generated identifier, but what about assigned identifiers and composite identifiers? This is more difficult, since Hibernate can't use the identifier property to distinguish between a newly instantiated object (with an identifier assigned by the user) and an object loaded in a previous session. In this case, Hibernate will either use the timestamp or version property, or will actually query the second-level cache or, worst case, the database, to see if the row exists.
これらは生成された識別子の場合には非常に良いのですが、割り当てられた識別子と複合識別子の場合はどうでしょうか?これはHibernateが、(ユーザにより割り当てられた識別子を持つ)新しくインスタンス化されたオブジェクトと、以前のSessionでロードされたオブジェクトを区別できないため、より難しいです。この場合、Hibernateはタイムスタンプかバージョンのプロパティのどちらかを使うか、二次キャッシュに問い合わせます。最悪の場合、行が存在するかどうかデータベースを見ます。
21.5. Conclusion(結論)
There is quite a bit to digest here and it might look confusing first time around. However, in practice, it all works out very nicely. Most Hibernate applications use the parent / child pattern in many places.
ここではかなりの量を要約したので、最初の頃は混乱しているように思われるかもしれません。しかし実際は、すべて非常に良く動作します。ほとんどのHibernateアプリケーションでは、多くの場面で親子パターンを使用します。
We mentioned an alternative in the first paragraph. None of the above issues exist in the case of <composite-element> mappings, which have exactly the semantics of a parent / child relationship. Unfortunately, there are two big limitations to composite element classes: composite elements may not own collections, and they should not be the child of any entity other than the unique parent.
最初の段落で代替方法について触れました。上記のような問題は <composite-element>マッピングの場合は存在せず、にもかかわらずそれは確かに親子関係のセマンティクスを持ちます。しかし残念ながら、複合要素クラスには二つの大きな制限があります:1つは複合要素はコレクションを持つことができないことです。もうひとつは、ユニークな親ではないエンティティの子供となるべきではないということです。
No comments:
Post a Comment