JPA OneToMany:List vs Set

sha*_*ltc 19 java hibernate jpa

我有两个实体:UserAccountNotification.这些关系如下所示.

 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
 }
Run Code Online (Sandbox Code Playgroud)

equals()hashcode()被覆盖(通过与业务键/主键IDE生成).

给定a 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
Run Code Online (Sandbox Code Playgroud)

每一个都发生同样的事情UserAccount.如果我更换ListSet,出现正常的INSERT.我在阅读本文档博客后找到了原因.

来自文档的观察

  • 在单向@OneToMany关联中,Set是优选的.

应该清楚的是,索引CollectionsSets允许在添加,删除和更新元素方面进行最有效的操作.

  • 在双向@OneToMany关系(@ManyToOne管理)中,List并且Bags是有效的.

Bags并且Lists是最有效的逆Collections.


话虽如此,更可取的是:

  1. 一个SetList一个单向@OneToMany映射?

  2. 或者,我是否必须通过添加双向关系来调整我的域模型以使用a List,尤其是当存在重复时?

kwi*_*atz 11

不久前我遇到了这个问题......

我发现这篇文章:Hibernate中一对多关联的性能反模式https://fedcsis.org/proceedings/2013/pliks/322.pdf

简而言之:

  • 包语义 - > List/ Collection+ @OneToMany- >添加了一个元素:1个删除,N个插入,删除了一个元素:1个删除,N个插入
  • 列表语义 - > List+ @OneToMany+ @IndexColumn/ @OrderColumn- >添加了一个元素:1个插入,M个更新,删除了一个元素:1个删除,M个更新
  • 设置语义 - > Set+ @OneToMany- >添加一个元素:1个插入,删除一个元素:1删除

对我来说:是的,这意味着你必须改变你ListSet单向@OneToMany.所以我改变了我的模型以符合Hibernate的期望,这导致了很多问题,因为应用程序的视图部分List主要依赖于......

一方面,这Set对我来说是一个合乎逻辑的选择,因为没有重复,另一方面List更容易处理.

所以JPA/Hibernate强迫我改变模型对象,这不是第一次,当你使用时,@EmbededId你做了一些你可能不会在没有JPA/Hibernate的情况下做同样的事情.当你必须要注意HibernateProxy所有的应用程序,尤其是equals方法else if(object instanceof HibernateProxy) {......时,你会注意到JPA/Hibernate persitence层在其他层中有点干扰.

但是,当我使用directely JDBC时,我也会使用更改模型或商务方法来促进持久性...层次隔离可能是一个梦想或成本太高而不能100%完成?

Set如果它们与注释SortedSet一样TreeSet,您可以订购@OrderBy

这带来一个问题,当一些代码依赖List,不能改变(如JSF/PrimeFaces <dataTable><repeat>组件),所以你必须改变你的Set进入List和返回Set,但如果你这样做setNotifications(new HashSet<>(notificationList)),你将有额外的查询,因为该集是org.hibernate.collection.PersistentSet由Hibernate管理...所以我使用addAll()removeAll()不是setter:

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) ;
}
Run Code Online (Sandbox Code Playgroud)

记住你的方法equals()hashCode()方法@Entity......

另一个问题是你需要掌握JPA和Hibernate,如果你想使用JPA和Hibernate作为实现,因为Set/List/Bag语义来自Hibernate而不是来自JPA(如果我错了,请纠正我)

规范用于将实现抽象为不依赖于特定供应商.尽管大多数JavaEE规范都成功了,但JPA对我来说失败了,而且我放弃了独立于Hibernate


dee*_*aut 1

列表:允许其中有重复的元素。

Set:所有元素都应该是唯一的。

现在,删除可能会发生,因为您覆盖了列表中的元素,因此当您修改 UserAccount 类型的持久实体时,它会删除先前在列表中的实体。

  • 不..事实并非如此。请阅读我列出的文档。删除的原因是有的。 (2认同)