Hibernate -如何在不加载整个集合的情况下将新项持久化到集合中

6vl6ewon  于 2023-08-06  发布在  其他
关注(0)|答案(5)|浏览(158)

我的模型中有一个集合,其中包含一组根域对象的“以前版本”。因此,以前的版本是“不可变的”,我们永远不想更新它们,只想在它们出现时添加过去的版本。此外,“版本化”域对象相当复杂,并导致检索大量数据库访问。
当我有这些对象之一的新版本时,我希望将其与其他对象一起保存,而不加载整个集合。高级常见问题解答对此提供了一些建议:
当我只想添加或删除一个元素时,为什么Hibernate总是初始化一个集合?
不幸的是,集合API定义的方法返回值只能通过命中数据库来计算。有三种例外情况:Hibernate可以添加到用inverse="true"声明的<bag><idbag><list>,而无需初始化集合;返回值必须始终为真。
如果要避免额外的数据库流量(即在性能关键代码中),重构模型以仅使用多对一关联。这几乎总是有可能的。然后使用查询来代替集合访问。
我对所有这些都是新的,并不是100%确定如何重构您的模型,以便只使用多对一的关联。任何人都可以请给予我一个例子,为我指出一个教程,使我可以了解这将如何解决我的问题?

oyt4ldly

oyt4ldly1#

当你有一个基于List或Set的集合,并且你向集合中添加了一个新对象时,Hibernate总是会命中数据库,因为它在保存或更新之前使用equals实现逐个比较对象-当使用Set时-或者当使用List时通过比较索引列。由于Set和List语义,需要此行为。正因为如此,无论您是否有一堆记录,应用程序的性能都会显著降低。
解决此问题的一些变通方法

通过使用封装的Bag集合加上所需的Set或List作为属性显示的转换模式

@Entity
public class One {

    private Collection<Many> manyCollection = new ArrayList<Many>();

    @Transient
    public Set<Many> getManyCollectionAsSet() { return new HashSet<Many>(manyCollection); }
    public void setManyCollectionAsSet(Set<Many> manySet) { manyCollection = new ArrayList<Many>(manySet); }

    /**
      * Keep in mind that, unlike Hibernate, JPA specification does not allow private visibility. You should use public or protected instead
      */
    @OneToMany(cascade=ALL)
    private Collection<Many> getManyCollection() { return manyCollection; }
    private void setManyCollection(Collection<Many> manyCollection) { this.manyCollection = manyCollection; }

}

字符串

使用多对一而不是一对多

@Entity
public class One {

    /**
      * Neither cascade nor reference
      */

}

@Entity
public class Many {

    private One one;

    @ManyToOne(cascade=ALL)
    public One getOne() { return one; }
    public void setOne(One one) { this.one = one }

}

缓存-应用时,根据您的要求,您的配置可能会提高或降低应用程序的性能。看这里
SQL约束-如果您想要一个行为像Set的集合,您可以使用SQL约束,它可以应用于一列或 * 列集 *。参见here

ha5z0ras

ha5z0ras2#

我通常这样做的方式是将集合定义为“逆”。
这大致意味着:1-N关联的主要定义在“N”端完成。如果您想向集合中添加某些内容,则需要更改详细数据的关联对象。
一个小的XML示例:

<class name="common.hibernate.Person" table="person">
    <id name="id" type="long" column="PERSON_ID">
        <generator class="assigned"/>
    </id>
    <property name="name"/>
    <bag name="addresses" inverse="true">
        <key column="PERSON_ID"/>
        <one-to-many class="common.hibernate.Address"/>
    </bag>
 </class>

 <class name="common.hibernate.Address" table="ADDRESS">
    <id name="id" column="ADDRESS_ID"/>
    <property name="street"/>
    <many-to-one name="person" column="PERSON_ID"/>
   </class>

字符串
则只在地址:

Address a = ...;
a.setPerson(me);
a.setStreet("abc");
Session s = ...;
s.save(a);


成交,你甚至都没碰那些藏品将其视为只读,这对于使用HQL进行查询、迭代和显示可能非常实用。

erhoui1w

erhoui1w3#

我曾经为这种情况创建了一个原生查询,如下所示:

@Modifying
    @Transactional
    @Query(nativeQuery = true, value = "INSERT INTO categorytopic_topic(category_id, topic_id) VALUES (?1, ?2)")
    public void addTopic(long categoryId, long topicId);

字符串
它非常快,因为不需要加载集合。

hgtggwj0

hgtggwj04#

如果我理解你的需求:

  • 你有一个内存中不可变对象的只加集合
  • 如何在不初始化整个集合情况下向集合中添加项(在当前会话中)?

您是否考虑过保持收藏断开连接

  • 在应用程序启动时加载它一次
  • 添加一个元素意味着两个操作,由一个服务在一个地方完成:
  • 保存元素(快速数据库操作,不对父元素进行级联)
  • 将元素添加到现有的已断开连接的集合(无数据库操作)
bejyjqdl

bejyjqdl5#

这将最小化所选数据
@LazyCollection(LazyCollectionOption.EXTRA)
但是在foreach迭代这个集合时要小心N+1个问题。

相关问题