第4章 永続クラス

永続クラスはビジネス上の問題のエンティティ (E コマースアプリケーションの顧客や注文など) を実装するアプリケーションのクラスです。 永続クラスのすべてのインスタンスが永続ステートであるわけではありません。 例えば、 インスタンスは一時的 (transient) であったり分離 (detached) 状態であることがあります。
これらのクラスが、 POJO (Plain Old Java Object) プログラミングモデルとしても知られる単純なルールに従うと、 Hibernate は最良の状態で動作します。 しかし、 これらのルールは難しいものではありません。 実際、 Hibernate3 は永続オブジェクトの性質に関する前提をほとんど持っていません。 ドメインモデルを他の方法で表現することもできます (Map インスタンスのツリーを使用するなど)。

4.1. 単純な POJO の例

多くの 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);
    }
}
永続クラスの主な 4 つのルールは以降の項で詳細に説明します。

4.1.1. 引数のないコンストラクタを実装する

Cat には引数のないコンストラクタがあります。 Hibernate が Constructor.newInstance() を使って永続クラスのインスタンス化を行えるように、 すべての永続クラスにはデフォルトコンストラクタ (public 以外でも問題ありません) がなければなりません。 Hibernate のランタイムプロキシ生成に対し、 少なくとも package の可視性を持つデフォルトコンストラクタが推奨されます。

4.1.2. 識別子プロパティを用意する(オプション)

Cat には id というプロパティがあります。 このプロパティはデータベーステーブルの主キー列へマッピングします。このプロパティの名前は何でも構いませんし、 型はどのようなプリミティブ型でも、 プリミティブの「ラッパー」型でも、 java.lang.Stringjava.util.Date でも構いません。レガシーデータベーステーブルに複合キーがある場合、これらの型のプロパティを持つユーザー定義のクラスを使用できます (本章の後に出てくる複合識別子の項を参照してください)。
識別子プロパティは厳密にはオプションです。これを省略して、 Hibernate に内部的にオブジェクトの識別子を追跡させることは可能です。しかしお勧めはしません。
実際に、 一部の機能は識別子プロパティを宣言するクラスだけが利用できます。
  • 分離オブジェクトの推移的な再追加 (カスケード更新やカスケードマージ) については、 「連鎖的な永続化」 を参照してください。
  • Session.saveOrUpdate()
  • Session.merge()
永続クラスには、 一貫した名前の識別子プロパティを宣言し、 null 値を取れる (プリミティブでない) 型を使用することが推奨されます。

4.1.3. final クラスにしない(オプション)

Hibernate の中心的な特徴である プロキシ は、永続クラスが final でないこと、またはメソッドを全部 public で宣言しているインターフェースが実装されているかに依存しています。
Hibernate でインターフェースを実装しない final クラスを永続化することはできますが、 遅延 (lazy) 関連フェッチに対してプロキシを使用できないため、 パフォーマンスチューニングのオプションが制限されます。
また、 final ではないクラスで public final メソッドを宣言しないようにしてください。 public final メソッドでクラスを使用した場合は、 lazy="false" と設定し、 明示的にプロキシを無効にしなければなりません。

4.1.4. 永続フィールドに対するアクセサとミューテータを定義する(オプション)

Cat はすべての永続フィールドに対してアクセサメソッドを宣言します。 他にも多くの ORM ツールが直接インスタンス変数を永続化します。 リレーショナルスキーマとクラスの内部データ構造間で間接化を提供した方がよいでしょう。 デフォルトでは、 Hibernate は JavaBean スタイルのプロパティを永続化し、 getFooisFoosetFoo 形式のメソッド名を認識します。 必要な場合、 特定のプロパティに対して直接フィールドアクセスへの切り替えが可能です。
プロパティは public で宣言する必要は ありません 。 Hibernate はデフォルトで、 protected もしくは private の get / set のペアを持つプロパティを永続化することができます。