【问题标题】:How do I issue a modifying query to remove a set of entities by their IDs with Spring Data JPA?如何使用 Spring Data JPA 发出修改查询以通过其 ID 删除一组实体?
【发布时间】:2015-07-31 11:29:14
【问题描述】:

我正在尝试从我的数据库中删除大量记录。每条记录都有一个外键级联,因此,通过 JPA 一次删除一条记录时,每次删除大约需要 5 秒。

所以我决定写一个这样的 JPQL 查询:

@Repository
public interface MyTableRepository extends JpaRepository<MyTable, Long> {
    @Query("delete from MyTable where id in :ids")
    void deleteAllIds(@Param("ids") Long[] ids);
}

当我运行它或等效地运行 void deleteAllIds(@Param("ids") Set&lt;Long&gt; ids); 时,我得到以下信息:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations [delete from com.lh.clte.domain.content.business.Hotel where id in (:ids_0_, :ids_1_, :ids_2_, :ids_3_, ... for the entire array size)]
    org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:293)
    org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:403)
    org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
    org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
    org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:111)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    com.sun.proxy.$Proxy72.deleteAllId(Unknown Source)

我怎样才能达到我的目标?

【问题讨论】:

  • 不能直接调用删除查询。你必须得到一个父对象列表,遍历它们,调用 session.delete(list.item.1)。然后在 for 循环之外,刷新会话。
  • 我要这样做: Set objectsToDelete = myTableRepository.findByIdIn(idSet); myTableRepository.delete(objectsToDelete);
  • 我刚刚为您发布了一个答案。检查一下。
  • @WeareBorg - 这是完全不正确的。你当然可以直接发出删除。
  • @Oliver 下面的回答是正确的。您只是在 delete 方法中缺少 @Modifying。添加它,你就准备好了。

标签: java spring jpa spring-data spring-data-jpa


【解决方案1】:

要执行修改查询,您需要使用@Modifying 注释方法,如reference documentation 中所述。

请注意,发出删除查询不会导致受影响实体的任何生命周期回调被调用,因为该语句是直接针对数据库执行的。要获得调用的回调,请发出 findAll(…) 给定标识符列表,并将结果通过管道传输到 deleteAll(…)。

如果您不在上述任何层中管理事务,请确保您还使用@Transactional,以便查询执行在 JPA 要求的事务中运行。

【讨论】:

  • 你能给出一些关于用户提出问题的代码是如何实现的吗...我也有兴趣知道。
  • 答案准确地描述了需要做的事情:为方法添加注释。参考资料的链接部分有一个示例。
  • 另外,delete 方法必须用 @Transactional 注释。
  • 谢谢 Oliver,这完全解决了我的性能问题:在几秒钟内删除了 70k 个实体,并带走了大约 500k 个链接实体。
【解决方案2】:

您可能想要编写一个单独的服务或类,您可以在其中编写一些原始 sql。JPA 确实允许您编写本机 sql,但如果您打算使用它,您应该只编写一些原始 sql并自行处理,确保删除 FK 适用的其他表中的记录和任何关联记录。

【讨论】:

  • 谢谢,但结构过于复杂,无法手动处理 FK。所以,这不是我的解决方案。
  • 感谢它真的很有帮助和更清洁的方法。
猜你喜欢
  • 1970-01-01
  • 2019-01-07
  • 1970-01-01
  • 2020-10-19
  • 2015-02-21
  • 1970-01-01
  • 2013-11-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多