JPA OneToMany : List vs Set

2022-09-01 12:13:22

我有两个实体:和 .它们之间的关系如下所示。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
 }

和 都会被覆盖(由 IDE 使用业务键/主键生成)。equals()hashcode()

给定一个 ,当我添加第一个时,它会产生一个 INSERT 语句。但是在进一步添加相同的 ,它首先删除,然后插入:UserAccountNotificationUserAccount

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

每个 .如果我用 替换 ,则会出现正常的 INSERT。在阅读本文档和博客后,我找到了原因。UserAccountListSet

来自文档的观察结果

  • 在单向关联中,是首选。@OneToManySet

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

  • 在双向关系(管理),并且是有效的。@OneToMany@ManyToOneListBags

Bags并且是最有效的反向。ListsCollections


话虽如此,这更可取:

  1. A over a in a 单向映射?SetList@OneToMany

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


答案 1

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

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

总之:

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

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

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

所以JPA/Hibernate迫使我改变模型对象,这不是第一次,当你使用时,你做了一些如果没有JPA/Hibernate你可能不会以同样的方式做的事情。当你必须注意在所有应用程序中,特别是在相等的方法中......时,你会注意到JPA/Hibernate peritence层在其他层中有点侵入性。@EmbededIdHibernateProxyelse if(object instanceof HibernateProxy) {

但是当我直接使用JDBC时,我也使用更改模型或业务方法来促进持久性......层隔离可能是一个梦想或成本太高,无法在100%的情况下完成?

你可以订购一个,如果他们像注释一样SetSortedSetTreeSet@OrderBy

当某些代码依赖于并且无法更改(例如JSF / PrimeFaces或组件)时,这会带来问题,因此您必须将其更改为并返回,但是如果您这样做,您将有额外的查询,因为该集合是由Hibernate管理的...所以我用而不是二传手:List<dataTable><repeat>SetListSetsetNotifications(new HashSet<>(notificationList))org.hibernate.collection.PersistentSetaddAll()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

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

制定规范是为了抽象实现,使其不依赖于一个特定的供应商。虽然大多数JavaEE规范都成功了,但JPA对我来说失败了,我放弃了独立于Hibernate。


答案 2

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

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

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


推荐