简体   繁体   English

在 JPA 中批量删除行同时还将删除级联到子记录的最佳方法是什么

[英]What is the best way to bulk delete rows in JPA while also cascading the delete to child records

I'm trying to do bulk delete in my entities, and the best solution will be to go with CriteriaDelete .我正在尝试在我的实体中进行批量删除,最好的解决方案是使用CriteriaDelete But CriteriaDelete does not cascade (at least not for me).但是CriteriaDelete不会级联(至少对我来说不是)。

So, it seems like the only solution which I have is to do select first and delete each element separately.因此,似乎我唯一的解决方案是先选择并分别删除每个元素。 Which does not seems wrong to me.这对我来说似乎没有错。

Is anyone have a better idea of how to do bulk delete?有人对如何进行批量删除有更好的了解吗? Is it actually a better way?它实际上是更好的方法吗?

If it helps I'm using EclipseLink 2.5.2.如果有帮助,我正在使用 EclipseLink 2.5.2。

The options are:选项是:

  1. use the cascade.Remove setting on the mapping, loading entities and calling em.remove on each在映射上使用cascade.Remove 设置,加载实体并在每个实体上调用em.remove
  2. Use bulk delete on the main entity and have the "ON DELETE CASCADE" database option set so that the database will cascade the delete for you.在主实体上使用批量删除并设置“ON DELETE CASCADE”数据库选项,以便数据库为您级联删除。 EclipseLink has a @CascadeOnDelete annotation that lets it know the "ON DELETE CASCADE" is set on a relationship, or to create it if using JPA for DDL generation: http://eclipse.org/eclipselink/documentation/2.5/jpa/extensions/a_cascadeondelete.htm EclipseLink 有一个 @CascadeOnDelete 注释,让它知道在关系上设置了“ON DELETE CASCADE”,或者如果使用 JPA 生成 DDL 则创建它: http : //eclipse.org/eclipselink/documentation/2.5/jpa/extensions /a_cascadeondelete.htm
  3. Use multiple bulk deletes to remove children that might be referenced before removing the main entity.在删除主实体之前,使用多次批量删除来删除可能被引用的子项。 For example: "Delete FROM Child c where c.parent = (select p from Parent P where [delete-conditions])" and "Delete FROM Parent p where [delete-conditions]" See section 10.2.4 of http://docs.oracle.com/middleware/1212/toplink/OTLCG/queries.htm#OTLCG94370 for details.例如:“Delete FROM Child c where c.parent = (select p from Parent P where [delete-conditions])”和“Delete FROM Parent p where [delete-conditions]”见http://的10.2.4节docs.oracle.com/middleware/1212/toplink/OTLCG/queries.htm#OTLCG94370了解详情。

How does the JPA CriteriaDelete work JPA CriteriaDelete 如何工作

A JPA CriteriaDelete statement generates a JPQL bulk delete statement, that's parsed to an SQL bulk delete statement. JPA CriteriaDelete语句生成 JPQL 批量删除语句,该语句被解析为 SQL 批量删除语句。

So, the following JPA CriteriaDelete statement:因此,以下 JPA CriteriaDelete语句:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    
CriteriaDelete<PostComment> delete = builder.createCriteriaDelete(PostComment.class);

Root<T> root = delete.from(PostComment.class);

int daysValidityThreshold = 3;

delete.where(
    builder.and(
        builder.equal(
            root.get("status"), 
            PostStatus.SPAM
        ),
        builder.lessThanOrEqualTo(
            root.get("updatedOn"), 
            Timestamp.valueOf(
                LocalDateTime
                .now()
                .minusDays(daysValidityThreshold)
            )
        )
    )
);

int deleteCount = entityManager.createQuery(delete).executeUpdate();

generates this SQL delete query:生成此 SQL 删除查询:

DELETE FROM
    post_comment
WHERE
    status = 2 AND
    updated_on <= '2020-08-06 10:50:43.115'

So, there is no entity-level cascade since the delete is done using the SQL statement, not via the EntityManager .因此,没有实体级级联,因为删除是使用 SQL 语句完成的,而不是通过EntityManager

Bulk Delete Cascading批量删除级联

To enable cascading when executing bulk delete, you need to use DDL-level cascade when declaring the FK constraints.要在执行批量删除时启用级联,您需要在声明 FK 约束时使用 DDL 级级联。

ALTER TABLE post_comment 
ADD CONSTRAINT FK_POST_COMMENT_POST_ID
FOREIGN KEY (post_id) REFERENCES post 
ON DELETE CASCADE

Now, when executing the following bulk delete statement:现在,当执行以下批量删除语句时:

DELETE FROM
    post
WHERE
    status = 2 AND
    updated_on <= '2020-08-02 10:50:43.109'

The DB will delete the post_comment records referencing the post rows that got deleted.数据库将删除引用被删除的post行的post_comment记录。

The best way to execute DDL is via an automatic schema migration tool, like Flyway , so the Foreign Key definition should reside in a migration script.执行 DDL 的最佳方法是通过自动模式迁移工具,如Flyway ,因此外键定义应驻留在迁移脚本中。

If you are generating the migration scripts using the HBM2DLL tool, then, in the PostComment class, you can use the following mapping to generate the aforementioned DDL statement:如果您使用 HBM2DLL 工具生成迁移脚本,那么在PostComment类中,您可以使用以下映射来生成上述 DDL 语句:

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(foreignKey = @ForeignKey(name = "FK_POST_COMMENT_POST_ID"))
@OnDelete(action = OnDeleteAction.CASCADE)
private Post post;

If you really care about the time it takes to perform this bulk delete, I suggest you use JPQL to delete your entities.如果您真的关心执行此批量删除所需的时间,我建议您使用 JPQL 删除您的实体。 When you issue a DELETE JPQL query, it will directly issue a delete on those entities without retrieving them in the first place.当您发出DELETE JPQL 查询时,它将直接对这些实体发出删除操作,而不会首先检索它们。

int deletedCount = entityManager.createQuery("DELETE FROM Country").executeUpdate(); 

You can even do conditional deletes based on some parameters on those entities using Query API like below您甚至可以使用 Query API 根据这些实体的某些参数进行条件删除,如下所示

Query query = entityManager.createQuery("DELETE FROM Country c 
                              WHERE c.population < :p");
int deletedCount = query.setParameter(p, 100000).executeUpdate();

executeUpdate will return the number of deleted rows once the operation is complete.一旦操作完成, executeUpdate将返回删除的行数。

If you've proper cascading type in place in your entities like CascadeType.ALL (or) CascadeType.REMOVE , then the above query will do the trick for you.如果您的实体中有适当的级联类型,例如CascadeType.ALL (或) CascadeType.REMOVE ,那么上述查询将为您解决问题。

@Entity
class Employee {

    @OneToOne(cascade=CascadeType.REMOVE)
    private Address address;

}

For more details, have a look at this and this .有关更多详细信息,请查看thisthis

JPQL BULK DELETE (whether using string-based JPQL or using Criteria JPQL) is not intended to cascade (ie follow the cascade type settings for fields). JPQL BULK DELETE (无论是使用基于字符串的 JPQL 还是使用 Criteria JPQL)都不是为了级联(即遵循字段的级联类型设置)。 If you want cascading then you either set up the datastore to use real FOREIGN KEY s, or you pull back the objects to delete and call EntityManager.remove() .如果您想要级联,那么您要么设置数据存储以使用真正的FOREIGN KEY s,要么拉回对象以删除并调用EntityManager.remove()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM