Hibernate.orgCommunity Documentation

第10章 オブジェクトを扱う

10.1. Hibernateにおけるオブジェクトの状態
10.2. オブジェクトを永続状態にする
10.3. オブジェクトのロード
10.4. クエリ
10.4.1. クエリの実行
10.4.2. コレクションのフィルタリング
10.4.3. クライテリアのクエリ
10.4.4. ネイティブSQLのクエリ
10.5. 永続オブジェクトの修正
10.6. 分離オブジェクトの修正
10.7. 自動的な状態検出
10.8. 永続オブジェクトの削除
10.9. 異なる二つのデータストア間でのオブジェクトのレプリケーション
10.10. セッションのフラッシュ
10.11. 連鎖的な永続化
10.12. メタデータの使用

Hibernate is a full object/relational mapping solution that not only shields the developer from the details of the underlying database management system, but also offers state management of objects. This is, contrary to the management of SQL statements in common JDBC/SQL persistence layers, a natural object-oriented view of persistence in Java applications.

言いかえれば、Hibernateを用いるアプリケーション開発者は、オブジェクトの 状態 については 常に意識すべきであり、SQL文の実行については必ずしもそうではありません。 この部分は、通常、Hibernateが処理し、システムのパフォーマンスをチューニングするときにだけ、 問題になってきます。

Hibernateは次のようなオブジェクトの状態を定義し、サポートしています。

We will now discuss the states and state transitions (and the Hibernate methods that trigger a transition) in more detail.

新しくインスタンス化された永続クラスのインスタンスは、 Hibernateでは 一時的(transient) と見なされます。 以下のように、セッションと関連づけることで、一時的なインスタンスを 永続状態(persistent) にできます。

DomesticCat fritz = new DomesticCat();
fritz.setColor(Color.GINGER);
fritz.setSex('M');
fritz.setName("Fritz");
Long generatedId = (Long) sess.save(fritz);

If Cat has a generated identifier, the identifier is generated and assigned to the cat when save() is called. If Cat has an assigned identifier, or a composite key, the identifier should be assigned to the cat instance before calling save(). You can also use persist() instead of save(), with the semantics defined in the EJB3 early draft.

Alternatively, you can assign the identifier using an overloaded version of save().

DomesticCat pk = new DomesticCat();
pk.setColor(Color.TABBY);
pk.setSex('F');
pk.setName("PK");
pk.setKittens( new HashSet() );
pk.addKitten(fritz);
sess.save( pk, new Long(1234) );

If the object you make persistent has associated objects (e.g. the kittens collection in the previous example), these objects can be made persistent in any order you like unless you have a NOT NULL constraint upon a foreign key column. There is never a risk of violating foreign key constraints. However, you might violate a NOT NULL constraint if you save() the objects in the wrong order.

Usually you do not bother with this detail, as you will normally use Hibernate's transitive persistence feature to save the associated objects automatically. Then, even NOT NULL constraint violations do not occur - Hibernate will take care of everything. Transitive persistence is discussed later in this chapter.

The load() methods of Session provide a way of retrieving a persistent instance if you know its identifier. load() takes a class object and loads the state into a newly instantiated instance of that class in a persistent state.

Cat fritz = (Cat) sess.load(Cat.class, generatedId);
// you need to wrap primitive identifiers
long id = 1234;
DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new Long(id) );

あるいは、以下のように、既存のインスタンスに状態をロードすることもできます。

Cat cat = new DomesticCat();
// load pk's state into cat
sess.load( cat, new Long(pkId) );
Set kittens = cat.getKittens();

Be aware that load() will throw an unrecoverable exception if there is no matching database row. If the class is mapped with a proxy, load() just returns an uninitialized proxy and does not actually hit the database until you invoke a method of the proxy. This is useful if you wish to create an association to an object without actually loading it from the database. It also allows multiple instances to be loaded as a batch if batch-size is defined for the class mapping.

If you are not certain that a matching row exists, you should use the get() method which hits the database immediately and returns null if there is no matching row.

Cat cat = (Cat) sess.get(Cat.class, id);
if (cat==null) {
    cat = new Cat();
    sess.save(cat, id);
}
return cat;

You can even load an object using an SQL SELECT ... FOR UPDATE, using a LockMode. See the API documentation for more information.

Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);

Any associated instances or contained collections will not be selected FOR UPDATE, unless you decide to specify lock or all as a cascade style for the association.

refresh() メソッドを使うことで、どんなときでも、オブジェクトやそのコレクションを リロードすることができます。 データベースのトリガがテーブルを更新した際に、 そのテーブルに対応するオブジェクトのプロパティを同期する場合、このメソッドが役に立ちます。

sess.save(cat);
sess.flush(); //force the SQL INSERT
sess.refresh(cat); //re-read the state (after the trigger executes)

How much does Hibernate load from the database and how many SQL SELECTs will it use? This depends on the fetching strategy. This is explained in 項19.1. 「フェッチ戦略」.

If you do not know the identifiers of the objects you are looking for, you need a query. Hibernate supports an easy-to-use but powerful object oriented query language (HQL). For programmatic query creation, Hibernate supports a sophisticated Criteria and Example query feature (QBC and QBE). You can also express your query in the native SQL of your database, with optional support from Hibernate for result set conversion into objects.

HQLやネイティブなSQLクエリは、 org.hibernate.Query のインスタンスとして表現されます。 このインタフェースは、パラメータバインディングやResultSetのハンドリングや クエリの実行を行うメソッドを用意しています。 通常、 Query は、以下に示すように、 その時点の Session を使って取得します。

List cats = session.createQuery(
    "from Cat as cat where cat.birthdate < ?")
    .setDate(0, date)
    .list();

List mothers = session.createQuery(
    "select mother from Cat as cat join cat.mother as mother where cat.name = ?")
    .setString(0, name)
    .list();

List kittens = session.createQuery(
    "from Cat as cat where cat.mother = ?")
    .setEntity(0, pk)
    .list();

Cat mother = (Cat) session.createQuery(
    "select cat.mother from Cat as cat where cat = ?")
    .setEntity(0, izi)
    .uniqueResult();]]

Query mothersWithKittens = (Cat) session.createQuery(
    "select mother from Cat as mother left join fetch mother.kittens");
Set uniqueMothers = new HashSet(mothersWithKittens.list());

A query is usually executed by invoking list(). The result of the query will be loaded completely into a collection in memory. Entity instances retrieved by a query are in a persistent state. The uniqueResult() method offers a shortcut if you know your query will only return a single object. Queries that make use of eager fetching of collections usually return duplicates of the root objects, but with their collections initialized. You can filter these duplicates through a Set.

Transactional persistent instances (i.e. objects loaded, saved, created or queried by the Session) can be manipulated by the application, and any changes to persistent state will be persisted when the Session is flushed. This is discussed later in this chapter. There is no need to call a particular method (like update(), which has a different purpose) to make your modifications persistent. The most straightforward way to update the state of an object is to load() it and then manipulate it directly while the Session is open:

DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );
cat.setName("PK");
sess.flush();  // changes to cat are automatically detected and persisted

Sometimes this programming model is inefficient, as it requires in the same session both an SQL SELECT to load an object and an SQL UPDATE to persist its updated state. Hibernate offers an alternate approach by using detached instances.

多くのアプリケーションでは次のことが必要になります。 それは、あるトランザクションでオブジェクトを復元し、操作するためにそれをUI層に送り、 その後に、新しいトランザクションで変更をセーブするといったことです。 並行性の高い環境で、このタイプのアプローチを使うアプリケーションでは、 "期間の長い" 作業単位の隔離性を保証するために、バージョンデータが通常使われます。

Hibernateは、 Session.update()Session.merge() メソッドを 使って、分離インスタンスを再追加することで、このモデルに対応します。

// in the first session
Cat cat = (Cat) firstSession.load(Cat.class, catId);
Cat potentialMate = new Cat();
firstSession.save(potentialMate);

// in a higher layer of the application
cat.setMate(potentialMate);

// later, in a new session
secondSession.update(cat);  // update cat
secondSession.update(mate); // update mate

識別子catId を持つ Cat が、既に secondSession でロードされていた場合は、再追加しようとしたときに、例外が投げられます。

Use update() if you are certain that the session does not contain an already persistent instance with the same identifier. Use merge() if you want to merge your modifications at any time without consideration of the state of the session. In other words, update() is usually the first method you would call in a fresh session, ensuring that the reattachment of your detached instances is the first operation that is executed.

The application should individually update() detached instances that are reachable from the given detached instance only if it wants their state to be updated. This can be automated using transitive persistence. See 項10.11. 「連鎖的な永続化」 for more information.

The lock() method also allows an application to reassociate an object with a new session. However, the detached instance has to be unmodified.

//just reassociate:
sess.lock(fritz, LockMode.NONE);
//do a version check, then reassociate:
sess.lock(izi, LockMode.READ);
//do a version check, using SELECT ... FOR UPDATE, then reassociate:
sess.lock(pk, LockMode.UPGRADE);

Note that lock() can be used with various LockModes. See the API documentation and the chapter on transaction handling for more information. Reattachment is not the only usecase for lock().

期間の長い作業単位の、その他のモデルは、??? で述べています。

Hibernateのユーザは次の2つのケースのどちらにも使える汎用的なメソッドを要求していました。 それは、新しい識別子を生成して一時的なインスタンスをセーブすることと、 その時点の識別子と関連づいている分離インスタンスを更新/再追加することのできるメソッドです。 saveOrUpdate() はこのような機能を実現したメソッドです。

// in the first session
Cat cat = (Cat) firstSession.load(Cat.class, catID);

// in a higher tier of the application
Cat mate = new Cat();
cat.setMate(mate);

// later, in a new session
secondSession.saveOrUpdate(cat);   // update existing state (cat has a non-null id)
secondSession.saveOrUpdate(mate);  // save the new instance (mate has a null id)

saveOrUpdate() の使用方法と意味は、 新しいユーザにとって混乱を招くかもしれません。 まず第一に、あるセッションで使用したインスタンスを別の新しいセッションで使おうとしない限り、 update()saveOrUpdate()merge() を使う必要はありません。 アプリケーション全体を通じて、これらのメソッドを全く使わないこともあります。

通常、 update()saveOrUpdate() は次のシナリオで 使われます。

saveOrUpdate() は以下のことを行います。

そして、 merge() は以下のようにとても異なります。

Session.delete() will remove an object's state from the database. Your application, however, can still hold a reference to a deleted object. It is best to think of delete() as making a persistent instance, transient.

sess.delete(cat);

You can delete objects in any order, without risk of foreign key constraint violations. It is still possible to violate a NOT NULL constraint on a foreign key column by deleting objects in the wrong order, e.g. if you delete the parent, but forget to delete the children.

It is sometimes useful to be able to take a graph of persistent instances and make them persistent in a different datastore, without regenerating identifier values.

//retrieve a cat from one database
Session session1 = factory1.openSession();
Transaction tx1 = session1.beginTransaction();
Cat cat = session1.get(Cat.class, catId);
tx1.commit();
session1.close();

//reconcile with a second database
Session session2 = factory2.openSession();
Transaction tx2 = session2.beginTransaction();
session2.replicate(cat, ReplicationMode.LATEST_VERSION);
tx2.commit();
session2.close();

The ReplicationMode determines how replicate() will deal with conflicts with existing rows in the database:

次のようなケースで、この機能を使用します。 異なるデータベースインスタンスに入れられたデータの同期、 製品更新時におけるシステム設定情報の更新、非ACIDトランザクションのなかで加えられた変更のロールバックなどです。

Sometimes the Session will execute the SQL statements needed to synchronize the JDBC connection's state with the state of objects held in memory. This process, called flush, occurs by default at the following points:

The SQL statements are issued in the following order:

An exception is that objects using native ID generation are inserted when they are saved.

Except when you explicitly flush(), there are absolutely no guarantees about when the Session executes the JDBC calls, only the order in which they are executed. However, Hibernate does guarantee that the Query.list(..) will never return stale or incorrect data.

It is possible to change the default behavior so that flush occurs less frequently. The FlushMode class defines three different modes: only flush at commit time when the Hibernate Transaction API is used, flush automatically using the explained routine, or never flush unless flush() is called explicitly. The last mode is useful for long running units of work, where a Session is kept open and disconnected for a long time (see 項11.3.2. 「拡張セッションと自動バージョニング」).

sess = sf.openSession();
Transaction tx = sess.beginTransaction();
sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale state

Cat izi = (Cat) sess.load(Cat.class, id);
izi.setName(iznizi);

// might return stale data
sess.find("from Cat as cat left outer join cat.kittens kitten");

// change to izi is not flushed!
...
tx.commit(); // flush occurs
sess.close();

フラッシュのとき、例外が発生するかもしれません。 (例えば、DML操作が制約を違反するような場合です。) 例外処理を理解するためには、Hibernateのトランザクションの振る舞いを理解する必要があるため、 章 11. Transactions and Concurrency で説明します。

個々のオブジェクトをセーブしたり、削除したり、再追加したりすることは かなり面倒です。特に、関連するオブジェクトを扱うような場合には際立ちます。 よくあるのは、親子関係を扱うケースです。 以下の例を考えてみましょう。

If the children in a parent/child relationship would be value typed (e.g. a collection of addresses or strings), their life cycle would depend on the parent and no further action would be required for convenient "cascading" of state changes. When the parent is saved, the value-typed child objects are saved and when the parent is deleted, the children will be deleted, etc. This works for operations such as the removal of a child from the collection. Since value-typed objects cannot have shared references, Hibernate will detect this and delete the child from the database.

Now consider the same scenario with parent and child objects being entities, not value-types (e.g. categories and items, or parent and child cats). Entities have their own life cycle and support shared references. Removing an entity from the collection does not mean it can be deleted), and there is by default no cascading of state from one entity to any other associated entities. Hibernate does not implement persistence by reachability by default.

HibernateのSessionの基本操作( persist(), merge(), saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate() が含まれます)に対して、 それぞれに対応するカスケードスタイルがあります。 それぞれのカスケードスタイルには、 create, merge, save-update, delete, lock, refresh, evict, replicate という名前がついています。 もし、関連に沿ってカスケードさせたい操作があるなら、マッピングファイルにそう指定しなければなりません。 例えば、以下のようにします。

<one-to-one name="person" cascade="persist"/>

カスケードスタイルは、組み合わせることができます。

<one-to-one name="person" cascade="persist,delete,lock"/>

You can even use cascade="all" to specify that all operations should be cascaded along the association. The default cascade="none" specifies that no operations are to be cascaded.

特殊なカスケードスタイル delete-orphan は、一対多関連にだけ 適用できます。 これは、関連から削除された子供のオブジェクトに対して、 delete() 操作が適用されることを意味します。

おすすめ:

cascade="all" でマッピングした関連(単値関連やコレクション)は、 親子 スタイルの関連とマークされます。 それは、親のセーブ/更新/削除が、子のセーブ/更新/削除を引き起こす関係のことです。

Furthermore, a mere reference to a child from a persistent parent will result in save/update of the child. This metaphor is incomplete, however. A child which becomes unreferenced by its parent is not automatically deleted, except in the case of a <one-to-many> association mapped with cascade="delete-orphan". The precise semantics of cascading operations for a parent/child relationship are as follows:

Finally, note that cascading of operations can be applied to an object graph at call time or at flush time. All operations, if enabled, are cascaded to associated entities reachable when the operation is executed. However, save-update and delete-orphan are transitive for all associated entities reachable during flush of the Session.

Hibernate requires a rich meta-level model of all entity and value types. This model can be useful to the application itself. For example, the application might use Hibernate's metadata to implement a "smart" deep-copy algorithm that understands which objects should be copied (eg. mutable value types) and which objects that should not (e.g. immutable value types and, possibly, associated entities).

Hibernate exposes metadata via the ClassMetadata and CollectionMetadata interfaces and the Type hierarchy. Instances of the metadata interfaces can be obtained from the SessionFactory.

Cat fritz = ......;
ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);

Object[] propertyValues = catMeta.getPropertyValues(fritz);
String[] propertyNames = catMeta.getPropertyNames();
Type[] propertyTypes = catMeta.getPropertyTypes();

// get a Map of all properties which are not collections or associations
Map namedValues = new HashMap();
for ( int i=0; i<propertyNames.length; i++ ) {
    if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) {
        namedValues.put( propertyNames[i], propertyValues[i] );
    }
}