28

と の 2 つのエンティティがUserAccountありNotificationます。これらには下図のような関係があります。

 public class UserAccount {

    @Id
    @Column(name = "USER_NAME")
    private String emailId;

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(name = "USERS_NOTIFICATIONS", joinColumns = { @JoinColumn(name = "USER_NAME") }, inverseJoinColumns = { @JoinColumn(name = "NOTIFICATION_ID") })
    private List<Notification> notifications;

    //setters, getter, equals and hashcode
 }

equals()との両方hashcode()がオーバーライドされます (ビジネス キー/主キーを使用して IDE によって生成されます)。

を指定するとUserAccount、最初の を追加するNotificationと、INSERT ステートメントになります。しかし、同じ をさらに追加するとUserAccount、最初に削除してから挿入します。

Hibernate: delete from USERS_NOTIFICATIONS where USER_NAME=?
Hibernate: insert into USERS_NOTIFICATIONS (USER_NAME, NOTIFICATION_ID) values (?, ?)
Hibernate: insert into USERS_NOTIFICATIONS (USER_NAME, NOTIFICATION_ID) values (?, ?)
Hibernate: insert into USERS_NOTIFICATIONS (USER_NAME, NOTIFICATION_ID) values (?, ?)
//as many inserts as the notifications the user has

同じことがすべてで起こりますUserAccount。を に置き換えるListSet、通常の INSERT が発生します。このドキュメントブログを読んだ後、理由がわかりました。

ドキュメントからの観察

  • 一方向の@OneToMany関連付けでは、Setが優先されます。

Collections要素を追加、削除、および更新するという点で、インデックスが作成され、Sets最も効率的な操作が可能であることは明らかです。

  • 双方向の@OneToMany関係(@ManyToOne管理)で、List効率Bags的です。

BagsListsは最も効率的な逆関数Collectionsです。


そうは言っても、どちらがより好ましいですか:

  1. 単方向マッピングで aSetを超えていますか?List@OneToMany

  2. Listまたは、特に重複がある場合、を使用するために双方向の関係を追加してドメイン モデルを微調整する必要がありますか?

4

3 に答える 3

20

少し前にこの問題に直面しました...

この記事を見つけました: Hibernate での一対多関連のパフォーマンス アンチパターンhttps://fedcsis.org/proceedings/2013/pliks/322.pdf

要するに:

  • バッグ セマンティクス -> List/ Collection+ @OneToMany-> 1 つの要素が追加されました: 1 つの削除、N 個の挿入、1 つの要素が削除されました: 1 個の削除、N 個の挿入
  • リスト セマンティクス -> List+ @OneToMany+ @IndexColumn/ @OrderColumn-> 1 つの要素が追加されました: 1 つの挿入、M の更新、1 つの要素の削除: 1 つの削除、M の更新
  • セマンティクスの設定 -> Set+ @OneToMany-> 1 つの要素が追加されました: 1 個の挿入、1 個の要素が削除されました: 1 個の削除

私にとって:はい、それはあなたを一方向に変更する必要があることを意味ListSetます@OneToMany。そのため、Hibernate の期待に合わせてモデルを変更しましたが、アプリケーションのビュー部分がList主に依存していたため、多くの問題が発生しました...

一方でSet重複がないため、私にとっては論理的な選択であり、他方でListは対処が容易でした。

@EmbededIdそのため、JPA/Hibernate はモデル オブジェクトを変更することを余儀なくされましたが、JPA/Hibernate なしではおそらく同じようにはできないことを使用しているときは、これが初めてではありませんでした。そしてHibernateProxy、すべてのアプリケーション、特にequalsメソッドで注意else if(object instanceof HibernateProxy) {する必要がある場合... ...、JPA / Hibernate永続レイヤーが他のレイヤーに少し侵入していることに気付きます。

しかし、JDBC を直接使用するときは、永続化を促進するためにモデルやビジネス メソッドを変更するためにも使用します... レイヤーの分離は夢であるか、100% で実行するにはコストがかかりすぎるのでしょうか?

そしてSet、それらが注釈とSortedSet似ている場合は注文できますTreeSet@OrderBy

一部のコードが依存してListいて変更できない場合(JSF/PrimeFaces<dataTable><repeat>コンポーネントなど)に問題が発生するため、変更SetListて元に戻す必要Setがありますが、そうすると、セットがHibernateによって管理されるsetNotifications(new HashSet<>(notificationList))ため、余分なクエリが発生しますorg.hibernate.collection.PersistentSet...だから、セッターの代わりにaddAll()andを使用しました:removeAll()

protected <E> void updateCollection(@NonNull Collection<E> oldCollection, @NonNull Collection<E> newCollection) {
    Collection<E> toAdd = new ArrayList<>(newCollection) ;
    toAdd.removeAll(oldCollection) ;

    Collection<E> toRemove = new ArrayList<>(oldCollection) ;
    toRemove.removeAll(newCollection) ;

    oldCollection.removeAll(toRemove) ;
    oldCollection.addAll(toAdd) ;
}

あなたのequals()hashCode()方法に注意してください@Entity...

もう 1 つの問題は、実装として Hibernate で JPA を使用する場合は、JPA と Hibernate の両方をマスターする必要があることです。これは、Set/List/Bag セマンティックが JPA からではなく Hibernate からのものであるためです (間違っている場合は修正してください)。

特定のベンダーに依存しないように実装を抽象化するための仕様が作成されます。JavaEE 仕様のほとんどは成功しましたが、JPA は失敗し、Hibernate から独立することを断念しました。

于 2015-04-10T13:26:20.373 に答える
1

リスト: 要素の重複を許可します。

セット: すべての要素は一意である必要があります。

現在、リスト内の要素を上書きしているため、削除が発生している可能性があります。そのため、UserAccount タイプの永続化されたエンティティを変更すると、以前にリストにあったエンティティが削除されます。

于 2012-11-14T09:25:38.573 に答える
0

注文が不要な場合はセットを使用します。これにより集合演算も可能になります。一方、順序付けが必要な場合は List を使用します。

私の目には、コンテナーのセマンティックは、どれを使用するかを決定するのに関連する側面です。

于 2022-02-09T07:37:41.333 に答える