Lightweight Class

hibernateのドキュメント自体は翻訳されているようですが、wikiは翻訳している人がいなさそうなので初めてみます。エンティティのマッピングを考えるのが仕事になっているので、マッピングがらみのパターンから。

翻訳

http://www.hibernate.org/41.html

免責:このパターンはhibernate界隈では一般的なものですが、hibernate3では代替手段が提供されています、

下記のような永続クラスがあるとします。

public class Document implements Node {
private Long _key;
private String _name;
private Calendar _created;
private Calendar _updated;
private Folder _folder;
private Clob _text;
public String getKey() { return _key; }
public void setKey(Long key) { _key = key; }
public String getName() { return _name; }
public void setName(String name) { _name = name; }
public Calendar getCreated() { return _created; }
public void setCreated(Calendar created) { _created = created; }
public Calendar getUpdated() { return _updated; }
public void setUpdated(Calendar updated) { _updated = updated; }
public Folder getFolder() { return _folder; }
public void setFolder(Folder folder) { _folder = folder; }
public Clob getText() { return _text; }
public void setText(Clob text) { _text = text; }
}

このクラスにおけるすべてのプロパティは、DOCUMENTSテーブルにマップされています。パフォーマンスの観点から、ドキュメントのリストを取得するとか、ドキュメントの名称を変更するとか、ドキュメントを削除するなどしたときに、CLOBを毎回ストリームから取得するようなことはしたくありません。商用のorマッパーは、生成時にあるオブジェクトをフェッチし、残りのプロパティを初回アクセス時にフェッチする、ということができます。hibernate3ではこれが実装されていますがhibernate2では実装されていません。しかし、別の手段を使えばパフォーマンスを改善することができます。

まずは、Documentクラスをlightweightな上位クラスとheavyweightな下位クラスに分割します。

public class DocumentInfo implements Node {
private Long _key;
private String _name;
private Calendar _created;
private Calendar _updated;
private Folder _folder;
private Clob _text;
public String getKey() { return _key; }
public void setKey(Long key) { _key = key; }
public String getName() { return _name; }
public void setName(String name) { _name = name; }
public Calendar getCreated() { return _created; }
public void setCreated(Calendar created) { _created = created; }
public Calendar getUpdated() { return _updated; }
public void setUpdated(Calendar updated) { _updated = updated; }
public Folder getFolder() { return _folder; }
public void setFolder(Folder folder) { _folder = folder; }
}

public class Document extends DocumentInfo {
private Clob _text;
public Clob getText() { return _text; }
public void setText(Clob text) { _text = text; }
}

マッピングは下記の通り設定します。


















これで、アプリケーションは、ドキュメントの情報を取得したければDocumentInfoのインスタンスを取得するし、テキストが必要であればDocumentを取得すればよいことになります。Documentについて、polymorphism="explicit"としてマップしているため、下記のようなクエリを書けばDocumentInfoのインスタンスが返ります。

from DocumentInfo
from Node
from java.lang.Object

下記のようにすると、Documentのインスタンスが返ります。

from d in class Document

警告:hibernate2では、物理的に同じ列を二つの異なるオブジェクトとしてロードすることができません。DocumentInfoをロードし、続けて同じIDのDocumentをロードするようなケースでは、hibernateはオブジェクトをアップキャストしません。例えば、下記のコードを実行すると、ClassCastExceptionが発生します。

DocumentInfo info = (DocumentInfo) session.load(DocumentInfo.class, new Long(1));
Document doc = (Document) session.load(Document.class, new Long(1));

もし、ある列のDocumentInfoをDocumentとして取得したければ、まず、DocumentInfoについてsession.evict()を使用する必要があります。あるいは、マッピングについて、polymorphism="explicit"を追加します。この指定によって、同じテーブルにマップされ同じ主キーを共有する異なるクラスについてロードしないようにすることができます。

思ったこと

hibernate3なら@Basic(fetch=LAZY)とすればよいのでしょうね。