14

hibernateによって管理されるJavaオブジェクトのオブジェクトの同等性をどのように処理しますか?「hibernateinaction」の本では、代理キーよりもビジネスキーを優先する必要があると述べています。
ほとんどの場合、私はビジネスキーを持っていません。人にマッピングされたアドレスについて考えてみてください。アドレスはセットに保持され、Wicket RefreshingViewに表示されます(ReuseIfEquals戦略を使用)。

サロゲートIDを使用するか、equals()およびhashCode()関数のすべてのフィールドを使用することができます。
問題は、これらのフィールドがオブジェクトの存続期間中に変更されることです。ユーザーがデータを入力したか、OSIV(Open Session in View)フィルター内でJPA merge()が呼び出されたためにIDが変更されたためです。

equals()およびhashCode()コントラクトについての私の理解は、これらはオブジェクトの存続期間中に変更されるべきではないということです。

私がこれまでに試したこと:

  • equals()は、データベースID(または、idがnullの場合はsuper.hashCode())を使用するhashCode()に基づいています。問題:新しいアドレスはnull IDで始まりますが、人に接続されるとIDを取得し、この人はosiv-filterでmerge()(再接続)されます。
  • hashCode()が最初に呼び出されたときにハッシュコードをレイジー計算し、そのハッシュコードを@Transitionalにします。merge()が新しいオブジェクトを返し、ハッシュコードがコピーされないため、機能しません。

私が必要とするのは、オブジェクトの作成中に割り当てられるIDだと思います。ここでの私の選択肢は何でしょうか?追加の永続プロパティを導入したくありません。オブジェクトにIDを割り当てるようにJPAに明示的に指示する方法はありますか?

よろしく

4

5 に答える 5

16

一時エンティティにはまだIDがないため、エンティティのを使用することidはお勧めできません(一時エンティティを永続エンティティと同じにする可能性があります)。

すべてのプロパティはIDの一部ではないため、(データベース識別子を除く)すべてのプロパティを使用することもお勧めできません。

したがって、平等を実装するための好ましい(そして正しい)方法は、Hibernateを使用したJava Persistenceで説明されているように、ビジネスキーを使用することです。

ビジネスキーとの平等の実装

私たちが推奨するソリューションに到達するには、ビジネスキーの概念を理解する必要があります。ビジネスキーは、同じデータベースIDを持つインスタンスごとに一意のプロパティまたはプロパティの組み合わせです。基本的に、代わりに代理主キーを使用しなかった場合に使用するのは自然キーです。自然な主キーとは異なり、ビジネスキーが変更されないことは絶対的な要件ではありません。ほとんど変更されない限り、それで十分です。

クラスのすべてのプロパティが含まれている場合でも、基本的にすべてのエンティティクラスにビジネスキーが必要であると主張します(これは一部の不変クラスに適しています)。ビジネスキーは、ユーザーが特定のレコードを一意に識別するものと考えるものですが、代理キーは、アプリケーションとデータベースが使用するものです。

ビジネスキーの同等性とは、equals()メソッドがビジネスキーを形成するプロパティのみを比較することを意味します。これは、前述のすべての問題を回避する完璧なソリューションです。唯一の欠点は、最初に正しいビジネスキーを特定するために特別な検討が必要になることです。とにかくこの努力が必要です。データベースで制約チェックを介してデータの整合性を確保する必要がある場合は、一意のキーを特定することが重要です。

Userクラスの場合、usernameはビジネスキーの候補として最適です。nullになることはなく、データベースの制約で一意であり、変更されることはほとんどありません。

    public class User {
        ...
        public boolean equals(Object other) {
            if (this==other) return true;
            if ( !(other instanceof User) ) return false;
            final User that = (User) other;
            return this.username.equals( that.getUsername() );
        }
        public int hashCode() {
            return username.hashCode();
        }
}

何かが足りなかったのかもしれませんが、住所の場合、ビジネスキーは通常、番地、番地、市区町村、郵便番号、国で構成されます。何の問題もありません。

念のため、EqualsAndHashCodeも興味深い読み物です。

于 2010-04-27T09:52:38.863 に答える
0

多分transientプロパティはそれをしますか?そうすれば、永続性について心配する必要はありません。このような:

@Transient
private Integer otherId;
于 2010-04-27T09:12:38.463 に答える
0

私はそれをそのように行うために使用します:equalとhashcodeは、設定されているときにキーを使用します。それ以外の場合、equalsは基本実装(別名==)を使用します。0の代わりにhashcode()returnsがあれば、それも機能するはずです。super.hashcode()

@Override
public int hashCode() {
    if (code == null) {
        return 0;
    } else {
        return code.hashCode();
    }
}

@Override
public boolean equals(Object obj) {
    if (obj instanceof PersistentObject && Hibernate.getClass(obj).equals(Hibernate.getClass(this))) {
        PersistentObject po = (PersistentObject) obj;

        if (code == null) {
            return po.code == null && this == po;
        } else {
            return code.equals(po.getCode());
        }
    } else {
        return super.equals(obj);
    }
}
于 2010-04-27T09:17:33.457 に答える
0

問題は、セットまたはマップに入れる必要のある重複する可能性のある複数の未保存オブジェクトが存在する可能性がどのくらいあるかということです。私にとって、答えは事実上決してないので、保存されていないオブジェクトには代理キーとsuper.equals/hashcodeを使用します。

ビジネスキーが理にかなっている場合もありますが、問題が発生する可能性があります。たとえば、2人が同じアドレスに住んでいる場合、データベース内の1つのレコードにしたい場合は、多対多として管理し、カスケード削除する機能を失う必要があります。そこに住んでいる人は削除されます、あなたはアドレスを取り除くために余分な仕事をしなければなりません。ただし、各個人に同じアドレスを格納する場合、ビジネスキーには個人エンティティを含める必要があります。これは、equals/hashcodeメソッド内でデータベースがヒットすることを意味する場合があります。

于 2010-04-27T16:08:22.833 に答える
0

ご入力いただきありがとうございます。私は代理キーを使用し、オブジェクトの作成時にそれらを提供することにしました。このようにして、私は「めったに」変更されないものすべてを避け、アイデンティティの基盤となる堅実なものを持っています。最初のテストはかなり良さそうです。

いつもありがとうございました。残念ながら、彼が私に良い読書を提供してくれたので、私はパスカルを取る解決策として1つの答えしか受け入れることができません;)

楽しい

于 2010-04-29T06:49:45.243 に答える