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.1.1. Implement a no-argument constructor(引数のないコンストラクタを実装する)
4.1.2. Provide an identifier property (optional)( 識別子プロパティを用意する)(オプション)
4.1.3. Prefer non-final classes (optional)( finalクラスにしない)(オプション)
4.1.4. Declare accessors and mutators for persistent fields (optional)( 永続フィールドに対するアクセサとミューテータを定義する)(オプション)
4.2. Implementing inheritance(継承と実装)
4.3. Implementing equals() and hashCode()(equals()とhashCode()の実装)
4.4. Dynamic models(動的モデル)
4.5. Tuplizers (Tuplizers)
Chapter 4. Persistent Classes(第4章:永続クラス)
Persistent classes are classes in an application that implement the entities of the business problem (e.g. Customer and Order in an E-commerce application). Not all instances of a persistent class are considered to be in the persistent state - an instance may instead be transient or detached.
永続クラスはビジネス上の問題のエンティティ(例えば、Eコマースアプリケーションの顧客や注文)を実装するアプリケーションのクラスです。永続クラスのすべてのインスタンスが永続状態であると見なされるわけではありません。インスタンスは逆に一時的(transient)であったり、分離状態(detached)であったりするかもしれません。
Hibernate works best if these classes follow some simple rules, also known as the Plain Old Java Object (POJO) programming model. However, none of these rules are hard requirements. Indeed, Hibernate3 assumes very little about the nature of your persistent objects. You may express a domain model in other ways: using trees of Map instances, for example.
Plain Old Java Object (POJO)プログラミングモデルとしても知られるいくつかの単純なルールに従うなら、Hibernateは最もよく働きます。しかしこれらのルールは難しいものではありません。実際Hibernate3は永続オブジェクトの性質にほとんど何の前提も置いていません。ドメインモデルは他の方法で表現することもできます。例えば<literal>Map</literal> インスタンスのツリーを使う方法があります。
4.1. A simple POJO example(単純なPOJO例)
Most Java applications require a persistent class representing felines.
ほとんどのJavaアプリケーションにはネコ科の動物を表現する永続クラスが必要です。
package eg;
import java.util.Set;
import java.util.Date;
public class Cat {
private Long id; // identifier
private Date birthdate;
private Color color;
private char sex;
private float weight;
private int litterId;
private Cat mother;
private Set kittens = new HashSet();
private void setId(Long id) {
this.id=id;
}
public Long getId() {
return id;
}
void setBirthdate(Date date) {
birthdate = date;
}
public Date getBirthdate() {
return birthdate;
}
void setWeight(float weight) {
this.weight = weight;
}
public float getWeight() {
return weight;
}
public Color getColor() {
return color;
}
void setColor(Color color) {
this.color = color;
}
void setSex(char sex) {
this.sex=sex;
}
public char getSex() {
return sex;
}
void setLitterId(int id) {
this.litterId = id;
}
public int getLitterId() {
return litterId;
}
void setMother(Cat mother) {
this.mother = mother;
}
public Cat getMother() {
return mother;
}
void setKittens(Set kittens) {
this.kittens = kittens;
}
public Set getKittens() {
return kittens;
}
// addKitten not needed by Hibernate
public void addKitten(Cat kitten) {
kitten.setMother(this);
kitten.setLitterId( kittens.size() );
kittens.add(kitten);
}
}
There are four main rules to follow here:
従うべき4つのルールがあります:
4.1.1. Implement a no-argument constructor(引数のないコンストラクタを実装する)
Cat has a no-argument constructor. All persistent classes must have a default constructor (which may be non-public) so that Hibernate can instantiate them usingConstructor.newInstance(). We strongly recommend having a default constructor with at least package visibility for runtime proxy generation in Hibernate.
Catには引数のないコンストラクタがあります。Hibernateが Constructor.newInstance()を使って永続クラスのインスタンス化を行えるように、すべての永続クラスにはデフォルトコンストラクタ(publicでなくても構いません)がなければなりません。Hibernateの実行時プロキシ生成のために、少なくともpackageの可視性を持つデフォルトコンストラクタを強くお勧めします。
4.1.2. Provide an identifier property (optional)( 識別子プロパティを用意する)(オプション)
Cat has a property called id. This property maps to the primary key column of a database table. The property might have been called anything, and its type might have been any primitive type, any primitive "wrapper" type, java.lang.String orjava.util.Date. (If your legacy database table has composite keys, you can even use a user-defined class with properties of these types - see the section on composite identifiers later.)
Catには idというプロパティがあります。このプロパティはデータベーステーブルの主キーカラムへマッピングされます。このプロパティの名前は何でも構いませんし、型はどのようなプリミティブ型でも、プリミティブの「ラッパー」型でも、java.lang.Stringやjava.util.Dateでも構いません。(もしレガシーデータベーステーブルが複合キーを持つならば、今述べたような型のプロパティを持つユーザ定義のクラスを使うことさえ可能です。後で複合識別子の節を見てください。)
The identifier property is strictly optional. You can leave them off and let Hibernate keep track of object identifiers internally. We do not recommend this, however.
識別子プロパティは厳密にはオプションです。これを省略して、Hibernateに内部的にオブジェクトの識別子を追跡させることは可能です。しかしおすすめはしません。
In fact, some functionality is available only to classes which declare an identifier property:
実際に、識別子プロパティを宣言するクラスだけが利用可能な機能がいくつかあります:
? Transitive reattachment for detached objects (cascade update or cascade merge) - see Section 10.11, "Transitive persistence"
分離オブジェクトの連鎖的な再追加(カスケード更新やカスケードマージ)。Section 10.11, "Transitive persistence"を参照してください。
? Session.saveOrUpdate()
? Session.merge()
We recommend you declare consistently-named identifier properties on persistent classes. We further recommend that you use a nullable (ie. non-primitive) type.
永続クラスには、一貫した名前の識別子プロパティを定義することをお勧めします。さらにnull値を取れる(つまりプリミティブではない)型を使った方がよいでしょう。
4.1.3. Prefer non-final classes (optional)( finalクラスにしない)(オプション)
A central feature of Hibernate, proxies, depends upon the persistent class being either non-final, or the implementation of an interface that declares all public methods.
Hibernateの中心的な特徴であるプロキシは、永続クラスがfinalでないこと、またはメソッドを全部publicで宣言しているインターフェイスが実装されているかに依存しています。
You can persist final classes that do not implement an interface with Hibernate, but you won't be able to use proxies for lazy association fetching - which will limit your options for performance tuning.
Hibernateでインターフェイスを実装していない finalクラスを永続化することはできますが、遅延関連フェッチに対してプロキシを使うことはできなくなります。これはパフォーマンスチューニングへの選択肢を狭めることになります。
You should also avoid declaring public final methods on the non-final classes. If you want to use a class with a public final method, you must explicitly disable proying by setting lazy="false".
finalではないクラスで public finalメソッドを定義することも避けるべきです。public finalメソッドを持つクラスを使いたければ、lazy="false"と設定して明示的にプロキシを無効にしなければなりません。
4.1.4. Declare accessors and mutators for persistent fields (optional)( 永続フィールドに対するアクセサとミューテータを定義する)(オプション)
Cat declares accessor methods for all its persistent fields. Many other ORM tools directly persist instance variables. We believe it is better to provide an indirection between the relational schema and internal data structures of the class. By default, Hibernate persists JavaBeans style properties, and recognizes method names of the form getFoo,isFoo and setFoo. You may switch to direct field access for particular properties, if needed.
Catではすべての永続フィールドに対してアクセサメソッドを定義しています。他の多くのORMツールは、永続インスタンス変数を直接永続化します。私たちはリレーショナルスキーマとクラスの内部構造を分離する方が良いと信じています。デフォルトでは、HibernateはJavaBeanスタイルのプロパティを永続化し、getFoo, isFoo, setFoo形式のメソッド名を認識します。しかし必要なら、特定のプロパティに対して、直接のフィールドアクセスに切り替えることは可能です。
Properties need not be declared public - Hibernate can persist a property with a default,protected or private get / set pair.
プロパティはpublicで宣言する必要はありません。Hibernateはデフォルト、protectedもしくは privateのget / setのペアを持つプロパティを永続化することができます。
4.2. Implementing inheritance(継承の実装)
A subclass must also observe the first and second rules. It inherits its identifier property from the superclass, Cat.
サブクラスも1番目と2番目のルールを守らなければなりません。サブクラスはスーパークラス Catから識別子プロパティを継承します。
package eg;
public class DomesticCat extends Cat {
private String name;
public String getName() {
return name;
}
protected void setName(String name) {
this.name=name;
}
}
4.3. Implementing equals() and hashCode()(equals()とhashCode()の実装)
You have to override the equals() and hashCode() methods if you
以下の条件の場合、equals()と hashCode()メソッドをオーバーライドしなければなりません、
? intend to put instances of persistent classes in a Set (the recommended way to represent many-valued associations) and
永続クラスのインスタンスを Setに置く場合。(これは多値の関連を表現するおすすめの方法です)
? intend to use reattachment of detached instances
そして、分離インスタンスをセッションへ再追加する場合。
Hibernate guarantees equivalence of persistent identity (database row) and Java identity only inside a particular session scope. So as soon as we mix instances retrieved in different sessions, we must implement equals() and hashCode() if we wish to have meaningful semantics for Sets.
Hibernateは、永続ID(データベースの行)と、特定のセッションスコープ内に限定ですがJavaIDとが等価であることを保証します。ですから異なるセッションで検索したインスタンスを組み合わせる場合、Setに意味のあるセマンティクスを持たせようと思っているならすぐにequals()と hashCode()を実装しなければなりません。
The most obvious way is to implement equals()/hashCode() by comparing the identifier value of both objects. If the value is the same, both must be the same database row, they are therefore equal (if both are added to a Set, we will only have one element in the Set). Unfortunately, we can't use that approach with generated identifiers! Hibernate will only assign identifier values to objects that are persistent, a newly created instance will not have any identifier value! Furthermore, if an instance is unsaved and currently in a Set, saving it will assign an identifier value to the object. Ifequals() and hashCode() are based on the identifier value, the hash code would change, breaking the contract of the Set. See the Hibernate website for a full discussion of this problem. Note that this is not a Hibernate issue, but normal Java semantics of object identity and equality.
最も明白な方法は、両方のオブジェクトの識別子の値の比較によって equals()とhashCode()を実装する方法です。値が同じなら、両者はデータベースの同じ行でなければならないため等しくなります。(両者が Setに追加されても、Setには1個の要素しかないことになります)残念なことに、生成された識別子にはこのアプローチを使うことができません。Hibernateは永続化されたオブジェクトへ識別子の値を代入するだけであり、新しく作成されたインスタンスはどのような識別子の値も持っていません。さらに、インスタンスがセーブされておらず、現在 Setの中にあれば、セーブするとオブジェクトへ識別子の値を代入することになります。もし equals()と hashCode()が識別子の値に基づいているなら、ハッシュコードが変更されると Setの規約が破られます。この問題についての完全な議論は、Hibernateのウェブサイトを見てください。これはHibernateの問題ではなく、オブジェクトの同一性と等価性についての、通常のJavaのセマンティクスであることに注意してください。
We recommend implementing equals() and hashCode() using Business key equality. Business key equality means that the equals() method compares only the properties that form the business key, a key that would identify our instance in the real world (anatural candidate key):
ビジネスキーの等価性を使って、equals()と hashCode()を実装することをお勧めします。ビジネスキーの等価性とは、equals()メソッドが、ビジネスキー、つまり現実の世界においてインスタンスを特定するキー(自然候補キー)を形成するプロパティだけを比較することを意味します。
public class Cat {
...
public boolean equals(Object other) {
if (this == other) return true;
if ( !(other instanceof Cat) ) return false;
final Cat cat = (Cat) other;
if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
if ( !cat.getMother().equals( getMother() ) ) return false;
return true;
}
public int hashCode() {
int result;
result = getMother().hashCode();
result = 29 * result + getLitterId();
return result;
}
}
Note that a business key does not have to be as solid as a database primary key candidate (see Section 11.1.3, "Considering object identity"). Immutable or unique properties are usually good candidates for a business key.
ビジネスキーはデータベースの主キー候補ほど安定である必要はないことに注意してください。(Section 11.1.3, "Considering object identity"を見てください)。更新不可なプロパティやユニークなプロパティは、通常ビジネスキーのよい候補です。
4.4. Dynamic models(動的モデル)
Note that the following features are currently considered experimental and may change in the near future.
以下の機能は現在実験段階にあると見なされており、近い将来変更される可能性があることに注意してください。
Persistent entities don't necessarily have to be represented as POJO classes or as JavaBean objects at runtime. Hibernate also supports dynamic models (using Maps ofMaps at runtime) and the representation of entities as DOM4J trees. With this approach, you don't write persistent classes, only mapping files.
永続エンティティは、必ずしも実行時にPOJOクラスやJavaBeanオブジェクトで表現する必要はありません。Hibernateは(実行時に Mapの Mapを使う)動的モデルと、DOM4Jツリーとしてのエンティティの表現もサポートします。このアプローチを使うと永続クラスを書かず、マッピングファイルだけを書くことになります。
By default, Hibernate works in normal POJO mode. You may set a default entity representation mode for a particular SessionFactory using the default_entity_modeconfiguration option (see Table 3.3, "Hibernate Configuration Properties".
デフォルトでは、Hibernateは通常のPOJOモードで動作します。default_entity_mode設定オプションを使って、特定の SessionFactoryに対するデフォルトのエンティティ表現モードを設定することができます(Table 3.3, "Hibernate Configuration Properties"を見てください)。
The following examples demonstrates the representation using Maps. First, in the mapping file, an entity-name has to be declared instead of (or in addition to) a class name:
以下の例では Mapを使った表現を紹介します。まずマッピングファイルで、クラス名の代わりに(またはそれに加えて)entity-nameを定義しなければなりません:
<hibernate-mapping>
<class entity-name="Customer">
<id name="id"
type="long"
column="ID">
<generator class="sequence"/>
</id>
<property name="name"
column="NAME"
type="string"/>
<property name="address"
column="ADDRESS"
type="string"/>
<many-to-one name="organization"
column="ORGANIZATION_ID"
class="Organization"/>
<bag name="orders"
inverse="true"
lazy="false"
cascade="all">
<key column="CUSTOMER_ID"/>
<one-to-many class="Order"/>
</bag>
</class>
</hibernate-mapping>
Note that even though associations are declared using target class names, the target type of an associations may also be a dynamic entity instead of a POJO.
関連がターゲットのクラス名を使って定義していたとしても、関連のターゲット型もPOJOではなく動的なエンティティでも構わないことに注意してください。
After setting the default entity mode to dynamic-map for the SessionFactory, we can at runtime work with Maps of Maps:
SessionFactoryに対してデフォルトのエンティティモードをdynamic-mapに設定した後実行時に Mapの Mapを使うことができます:
Session s = openSession();
Transaction tx = s.beginTransaction();
Session s = openSession();
// Create a customer
Map david = new HashMap();
david.put("name", "David");
// Create an organization
Map foobar = new HashMap();
foobar.put("name", "Foobar Inc.");
// Link both
david.put("organization", foobar);
// Save both
s.save("Customer", david);
s.save("Organization", foobar);
tx.commit();
s.close();
The advantages of a dynamic mapping are quick turnaround time for prototyping without the need for entity class implementation. However, you lose compile-time type checking and will very likely deal with many exceptions at runtime. Thanks to the Hibernate mapping, the database schema can easily be normalized and sound, allowing to add a proper domain model implementation on top later on.
動的なマッピングの利点は、エンティティクラスの実装を必要としないため、プロトタイピングに要するターンアラウンドタイムが早いということです。しかしコンパイル時の型チェックがないので、実行時に非常に多くの例外処理を扱わなければならないでしょう。Hibernateマッピングのおかげで、データベーススキーマは容易に正規化でき、健全になり、後で適切なドメインモデルの実装を追加することが可能になります。
Entity representation modes can also be set on a per Session basis:
エンティティ表現モードは Sessionごとに設定することも可能です。
Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
// Create a customer
Map david = new HashMap();
david.put("name", "David");
dynamicSession.save("Customer", david);
...
dynamicSession.flush();
dynamicSession.close()
...
// Continue on pojoSession
Please note that the call to getSession() using an EntityMode is on the Session API, not the SessionFactory. That way, the new Session shares the underlying JDBC connection, transaction, and other context information. This means you don't have tocall flush() andclose() on the secondary Session, and also leave the transaction and connection handling to the primary unit of work.
EntityModeを使った getSession()の呼び出しは SessionFactoryではなく SessionAPIにあることに注意してください。その方法では、新しい Sessionは、ベースとなるJDBCコネクション、トランザクション、その他のコンテキスト情報を共有します。これは2番目のSessionでは flush()と close()を呼ぶ必要がないということ、そのためトランザクションとコネクションの管理を1番目の作業単位(Unit of Work)に任せることができるということです。
More information about the XML representation capabilities can be found in Chapter 18,XML Mapping.
XML表現の能力について更なる情報はChapter 18, XML Mappingで見つかります。
4.5. Tuplizers(Tuplizers)
org.hibernate.tuple.Tuplizer, and its sub-interfaces, are responsible for managing a particular representation of a piece of data, given that representation'sorg.hibernate.EntityMode. If a given piece of data is thought of as a data structure, then a tuplizer is the thing which knows how to create such a data structure and how to extract values from and inject values into such a data structure. For example, for the POJO entity mode, the correpsonding tuplizer knows how create the POJO through its constructor and how to access the POJO properties using the defined property accessors. There are two high-level types of Tuplizers, represented by theorg.hibernate.tuple.EntityTuplizer and org.hibernate.tuple.ComponentTuplizerinterfaces. EntityTuplizers are responsible for managing the above mentioned contracts in regards to entities, while ComponentTuplizers do the same for components.
org.hibernate.tuple.Tuplizerとそのサブインターフェイスは、表現のorg.hibernate.EntityModeを利用して、データ断片のある表現の管理に責任を持ちます。与えられたデータ断片をデータ構造として考えるなら、Tuplizerはそのようなデータ構造をどのように作成するかを知り、そのようなデータ構造からどのように値を抽出し、注入するかを知っています。例えばPOJOエンティティモードでは、対応するTuplizerはコンストラクタを通して、POJOをどのように作成するか、定義されたプロパティアクセサを使い、POJOプロパティにどのようにアクセスするかを知ります。Tuplizerには二つのハイレベルの型があります。それらは、org.hibernate.tuple.EntityTuplizerとorg.hibernate.tuple.ComponentTuplizerインターフェイスで表現されます。EntityTuplizerは上で述べたようなエンティティに関する契約の管理に責任を持ちます。一方、ComponentTuplizerはコンポーネントに関する契約の管理に責任を持ちます。
Users may also plug in their own tuplizers. Perhaps you require that a java.util.Mapimplementation other than java.util.HashMap be used while in the dynamic-map entity-mode; or perhaps you need to define a different proxy generation strategy than the one used by default. Both would be achieved by defining a custom tuplizer implementation. Tuplizers definitions are attached to the entity or component mapping they are meant to manage. Going back to the example of our customer entity:
ユーザは独自のTuplizerに差し替えることも可能です。おそらくdynamic-map entity-modeの際に java.util.HashMapを使うのではなく、java.util.Mapの実装が必要でしょう。もしくは、おそらくデフォルトのものではなく、別のプロキシ生成戦略の定義が必要でしょう。両者とも、カスタムのTuplizer実装を定義することで達成されます。Tuplizerの定義は、管理しようとするエンティティやコンポーネントのマッピングに結び付けられます。顧客エンティティの例に戻ると:
<hibernate-mapping>
<class entity-name="Customer">
<!--
Override the dynamic-map entity-mode
tuplizer for the customer entity
-->
<tuplizer entity-mode="dynamic-map"
class="CustomMapTuplizerImpl"/>
<id name="id" type="long" column="ID">
<generator class="sequence"/>
</id>
<!-- other properties -->
...
</class>
</hibernate-mapping>
public class CustomMapTuplizerImpl
extends org.hibernate.tuple.DynamicMapEntityTuplizer {
// override the buildInstantiator() method to plug in our custom map...
protected final Instantiator buildInstantiator(
org.hibernate.mapping.PersistentClass mappingInfo) {
return new CustomMapInstantiator( mappingInfo );
}
private static final class CustomMapInstantiator
extends org.hibernate.tuple.DynamicMapInstantitor {
// override the generateMap() method to return our custom map...
protected final Map generateMap() {
return new CustomMap();
}
}
}
TODO: Document user-extension framework in the property and proxy packages
TODO: プロパティとプロキシパッケージのユーザ拡張フレームワークを文書化すること
No comments:
Post a Comment