21

私のアプリには、これらの Hibernate マップ型があります (一般的なケース):

class RoleRule {
  private Role role;
  private PermissionAwareEntity entity; // hibernate-mapped entity for which permission is granted
  private PermissionType permissionType; // enum

  @ManyToOne
  @JoinColumn(name = "ROLE_ID")
  public Role getRole() {
    return role;
  }
  public void setRole(Role role) {
    this.role = role;
  }

}

class Role {
  private Set<RoleRule> rules = new HashSet<RoleRule>(0);

  @OneToMany(cascade=CascadeType.ALL)
  @JoinColumn(name="ROLE_ID")
  public Set<RoleRule> getRules() {
    return rules;
  }
  public void setRules(Set<RoleRule> rules) {
    this.rules = rules;
  }

}

すべてのクラスにはequals() & hashCode()オーバーライドがあります。

私のアプリケーションでは、ロールの微調整が可能で (システム管理者のみ、心配しないでください)、他のフィールドの中でも、新しいロール ルールの作成が可能です。新しいルールが作成されると、新しいRoleRuleオブジェクトを作成してロールのフィールドに挿入しようとしますrulessession.update(role)データベースに変更を適用するために呼び出します。

醜い部分が来ます... Hibernateは、トランザクションを閉じてフラッシュするときに次のことを行うことにしました:

  1. 新しいルールをデータベースに挿入します。優秀な。
  2. 他の役割フィールド (コレクションではない) を更新します。ここまでは順調ですね。
  3. 何も変更されていない場合でも、既存のルールを更新します。私はこれで暮らすことができます。
  4. 既存のルールを再度更新します。自動コメントを含むログからの貼り付けは次のとおりです。
/* 1 対多の行 Role.rules を削除します */
update ROLE_RULE set ROLE_ID=null どこで ROLE_ID=? および ROLE_RULE_ID=?

もちろん、すべてのフィールドは非 null であり、この操作は見事に失敗します。

Hibernateがこれを行う理由を誰か説明できますか??? そしてさらに重要なのは、どうすればこれを回避できるのでしょうか???

編集:マッピングに関係があると確信していたので、上司が気まぐれに削除しequals()hashCode()両方のクラスでEclipseを使用してそれらを再作成し、不思議なことにこれで問題が解決しました。

私はまだ私の質問について非常に興味があります。Hibernateがこれを行う理由を誰かが提案できますか?

4

4 に答える 4

7

私は通常、Hibernate でコレクションを更新する 2 つの方法 (1 対多の多側) を使用しました。強引な方法は、コレクションをクリアし、親で save を呼び出してから、flush を呼び出すことです。次に、すべてのコレクション メンバーを追加し直して、親に対して再度 save を呼び出します。これにより、すべてが削除され、すべてが挿入されます。挿入の前に削除を強制するため、中央のフラッシュが重要です。このメソッドは、すべてのコレクションを再挿入するため、小さなコレクションでのみ使用することをお勧めします。

2 番目の方法はコーディングが難しくなりますが、より効率的です。新しい子セットをループして、まだ残っているものを手動で変更し、そうでないものを削除してから、新しいものを追加します。疑似コードでは次のようになります。

copy the list of existing records to a list_to_delete
for each record from the form
   remove it from the list_to_delete
   if the record exists (based on equals()?  key?)
     change each field that the user can enter
   else if the record doesn't exist
     add it to the collection
end for
for each list_to_delete
  remove it
end for
save

この問題を解決する正しい方法を見つけようと、Hibernate フォーラムを何時間も検索しました。コレクションを更新して正確にしてから親を保存できるはずですが、ご存知のように、Hibernate は子を削除する前に親から子を切り離そうとし、外部キーが null でない場合は、失敗します。

于 2008-10-07T17:29:38.333 に答える
5

「 Java で equals と hashCode をオーバーライドする」という質問の回答を参照してください。

equals メソッドと hashCode メソッドをオーバーライドする方法について説明しています。

それらを誤ってオーバーライドすると、休止状態がコレクションを削除して再挿入する可能性があります。(ハッシュキーはマップのキーとして使用されるため)

于 2009-07-03T08:05:36.327 に答える
3

これが解決策かどうかはわかりませんが、試してみてください。

@OneToMany(mappedBy = "role")

@JoinColumn アノテーションがありませんか? 両方のエンティティが関連付けを「所有」しようとしていると思います。そのため、SQL が台無しになる可能性があります。

また、影響を受ける列のみが更新されるようにしたい場合は、クラスで hibernate 固有の注釈を使用できます。

@Entity
@org.hibernate.annotations.Entity(
    dynamicInsert = true, dynamicUpdate = true
)
于 2008-10-07T16:34:18.460 に答える