在Spring Data JPA中具有@OneToMany关系的批量克隆实体

我的应用程序可以包含许多不同的项目,这些项目包含通过OneToOneOneToManyManyToMany关系链接的实体。

业务需求是能够克隆整个项目并创建一个新项目。有时这些实体具有基于项目名称的唯一代码,因此我需要计算新代码。

有关信息,我将 Spring 5.1.12.RELEASE Hibernate 5.3.14.Final 结合使用,并在 Java 11 上运行 Postgres 11.8

我的第一个天真解决方案是遍历每个对象,然后遍历其每个子对象以重新创建所有外键。但是,它将对每个父对象执行选择查询,这对性能非常不利。

我当前的解决方案是,对于每个实体,(使用联接)获取所有当前项目实体,然后对其进行克隆。但是,为了跟踪外键,我创建了HashMap链接将旧的父实体链接到新的父实体,因此我可以轻松地在子实体中进行设置。

例如,假设我有实体A和B是实体C的父母。

克隆实体A和B时,我会做:

oldAtoNewAMap.put((A)Hibernate.unproxy(oldA),newA);
oldBtoNewBMap.put((B)Hibernate.unproxy(oldB),newB);

然后克隆C时,我会做类似的事情:

 C newC = new C(oldC); // copy constructor copying everything but the ID
 A newA = oldAToNewAMap.get((A) Hibernate.unproxy(oldC.geta()));
 B newB = oldBToNewBMap.get((B) Hibernate.unproxy(oldC.getB()));
 newA.addC(newC); // updates the collection in the parent and sets the parent in the child
 newB.addC(newC);

当不再使用地图时,我将其清除。

我已经启用了Hibernate批处理,并且每25次迭代(其中25次是我的批处理大小)调用entityManager.flush()entityManager.clear()。我的ID是使用GenerationType.SEQUENCE生成的。

此外,我使用Spring的Streamed查询来获取当前项目的实体:

 @QueryHints(value = {
    @QueryHint(name = HINT_CACHEABLE,value = "false"),@QueryHint(name = HINT_FETCH_SIZE,value = "25"),@QueryHint(name = READ_ONLY,value = "true")
})
@Query("select c from C c inner join c.parent1 p1 inner join p1.parent2 p2 where p2.projectId = ?1")
Stream<C> findAllByProjectId(Long projectId);

我的问题是要复制很多实体(在该示例中,将有〜400,000个C实体〜6000 A实体 60个B实体)。然后这些C实体也有子...

因此,起初需要1-2秒来处理1,000个C实体。但是,时间量一直保持相当快的增长,在处理完60,000个实体之后,要花10秒的时间才能处理1000个C实体。

很明显,这将是在晚上运行的一个批处理,以避免表锁定,因此我不需要超级快,但我仍然需要第二天早上准备好我的新项目。我有什么办法可以在不运行太多选择的情况下管理内存问题?

也许最好的方法是使用其他工具来完成工作?

编辑:重新启用Hibernate的show-sql配置后,我意识到我的代码每次循环时仍对B表上的每个C实体执行一个SELECT在流上。我尝试在left join fetch的B表中添加一个@Query,但没有任何改变。

编辑2:显然,Hibernate也不会批处理我的语句吗?它显示了25条插入语句,即使spring.jpa.properties.hibernate.jdbc.batch_size设置为25,我也使用Spring的saveAll()函数。我不知道这是怎么回事...

iCMS 回答:在Spring Data JPA中具有@OneToMany关系的批量克隆实体

暂时没有好的解决方案,如果你有好的解决方案,请发邮件至:iooj@foxmail.com
本文链接:https://www.f2er.com/2214007.html

大家都在问