24

私は Hibernate を使い始めたばかりで、これまでに目にしたすべての例は、Hibernate のドキュメントのチュートリアルとほとんど同じように見えます。

package org.hibernate.tutorial.domain;
import java.util.Date;

public class Event {

    private Long id;
    private String title;
    private Date date;

    public Event() {}

    /* Accessor methods... */
}

具体的には、どのフィールドも として宣言されfinalておらず、Hibernate フレームワークがクラ​​スをインスタンス化してそのフィールドを設定できるように、引数のないコンストラクターが必要です。

しかし、ここに問題があります-私は、回避できるときはいつでもクラスを変更可能にするのが本当に好きではありません(Javaプラクティス: 不変オブジェクトは、これを行うことについてかなり強力な議論をします)。各フィールドを 'final' と宣言したとしても、Hibernate を機能させる方法はありますか?

Hibernate は Reflection を使用してそのクラスをインスタンス化するため、間違ったコンストラクターを選択したり、パラメーターの 1 つに間違った値を渡したりするリスクを負うことなく、ある種のコンストラクターを呼び出すことができる必要があることを理解しています。引数なしのコンストラクターを呼び出し、各フィールドを一度に 1 つずつ設定します。しかし、不変オブジェクトを安全にインスタンス化できるように、Hibernate に必要な情報を提供することは可能ではないでしょうか?

public class Event {

    private final Long id;
    private final String title;
    private final Date date;

    public Event(@SetsProperty("id") Long id,
        @SetsProperty("title") String title,
        @SetsProperty("date") Date date) {

        this.id = id;
        this.title = title;
        this.date = new Date(date.getTime());
    }

    /* Accessor methods... */
}

もちろん、@SetsProperty注釈は架空のものですが、手の届かないところにあるようには見えません。

4

6 に答える 6

13

不変オブジェクトとは、その状態(つまりフィールド)を変更するメソッドを持たないオブジェクトを意味します。フィールドは最終的なものである必要はありません。したがって、すべてのミューテーターを削除して、アクセサーの代わりにフィールドアクセスを使用するようにHibernateを構成するか、引数なしのコンストラクターとミューテーターを非推奨としてマークすることができます。これは少し回避策ですが、何もないよりはましです。

于 2009-05-28T11:19:54.470 に答える
13

実際、JDK 1.5+ では、hibernate は (リフレクションを通じて) final フィールドの変更を処理できます。フィールドをいくつかのデフォルト/ヌルなどに設定する保護されたデフォルトコンストラクター()を作成します... Hibernateは、オブジェクトをインスタンス化するときにこれらの値をオーバーライドできます。

これはすべて、シリアライゼーション/デシリアライゼーションを有効にするために行われた Java 1.5 メモリ モデルの変更 (ファイナルがそれほどファイナルではないことを許可する) の変更のおかげで可能になりました。

public class Event {

private final Long id;
private final String title;
private final Date date;

// Purely for serialization/deserialization
protected Event() {
    id = null;
    title = null;
    date = null;
}

public Event(Long id, String title, Data date) {
    this.id = id;
    this.title = title;
    this.date = date;
}

/* Accessor methods... */

}

于 2010-09-10T09:18:11.480 に答える
8

Hibernate が実行する多くの操作は変更可能な状態に関係するため、これは Hibernate の使用例ではないように思えます。

  • オブジェクトの結合
  • ダーティ状態チェック
  • 変更のフラッシュ

そうは言っても、不変性が心配な場合は、コピー コンストラクターを使用して、オブジェクトのラッパーを提供することを選択できます。

public class FinalEvent {
    private final Integer id;

    public FinalEvent(Event event) {
        id = event.id;
    }
}

ただし、それは余分な作業を意味します。


考えてみると、通常、休止状態のセッションはスレッドにバインドされており、これにより final フィールドの少なくとも 1 つの利点である安全なパブリケーションが無効になります。

最終フィールドのその他の利点は何ですか?

于 2009-05-28T10:34:49.243 に答える
4

これも長い間私を悩ませてきました。私が最近試しているアイデアの 1 つは、モデル クラスに読み取り専用インターフェイスを定義し、DAO と任意のファクトリが代わりにオブジェクトでそれらを返すようにすることです。つまり、実装が変更可能であっても、DAO/factory オブジェクトから離れると、微調整できなくなります。

そのようです:

public interface Grape {
  public Color getColor();
}

public class HibernateGrapeDao {
  public Grape findGrape(int grapeId) {
    HibernateGrape grape = hibernate.find(...
    return grape;
  }
}

class HibernateGrape implements Grape {
 ....
}

実装クラスを dao パッケージに対してパッケージ プライベートに保ちたい場合もあります。そのため、誰もそれらを直接いじることはできません。もう少し作業が必要ですが、長期的には物事をきれいに保つのにおそらく役立ちます。そして、明らかに、平等/アイデンティティビジネス全体に注意してください。

于 2009-12-24T15:50:29.270 に答える
2

Builder パターンを使用すると、目的の結果を達成できる場合があります。少し前に Hibernate Forums でこのアイデアについて議論している投稿を読みました(自分で実装したことはありませんが...)

于 2009-05-28T14:56:54.033 に答える