繁体   English   中英

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

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

我正在尝试在我的实体中进行批量删除,最好的解决方案是使用CriteriaDelete 但是CriteriaDelete不会级联(至少对我来说不是)。

因此,似乎我唯一的解决方案是先选择并分别删除每个元素。 这对我来说似乎没有错。

有人对如何进行批量删除有更好的了解吗? 它实际上是更好的方法吗?

如果有帮助,我正在使用 EclipseLink 2.5.2。

选项是:

  1. 在映射上使用cascade.Remove 设置,加载实体并在每个实体上调用em.remove
  2. 在主实体上使用批量删除并设置“ON DELETE CASCADE”数据库选项,以便数据库为您级联删除。 EclipseLink 有一个 @CascadeOnDelete 注释,让它知道在关系上设置了“ON DELETE CASCADE”,或者如果使用 JPA 生成 DDL 则创建它: http : //eclipse.org/eclipselink/documentation/2.5/jpa/extensions /a_cascadeondelete.htm
  3. 在删除主实体之前,使用多次批量删除来删除可能被引用的子项。 例如:“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了解详情。

JPA CriteriaDelete 如何工作

JPA CriteriaDelete语句生成 JPQL 批量删除语句,该语句被解析为 SQL 批量删除语句。

因此,以下 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();

生成此 SQL 删除查询:

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

因此,没有实体级级联,因为删除是使用 SQL 语句完成的,而不是通过EntityManager

批量删除级联

要在执行批量删除时启用级联,您需要在声明 FK 约束时使用 DDL 级级联。

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

现在,当执行以下批量删除语句时:

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

数据库将删除引用被删除的post行的post_comment记录。

执行 DDL 的最佳方法是通过自动模式迁移工具,如Flyway ,因此外键定义应驻留在迁移脚本中。

如果您使用 HBM2DLL 工具生成迁移脚本,那么在PostComment类中,您可以使用以下映射来生成上述 DDL 语句:

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

如果您真的关心执行此批量删除所需的时间,我建议您使用 JPQL 删除您的实体。 当您发出DELETE JPQL 查询时,它将直接对这些实体发出删除操作,而不会首先检索它们。

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

您甚至可以使用 Query API 根据这些实体的某些参数进行条件删除,如下所示

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

一旦操作完成, executeUpdate将返回删除的行数。

如果您的实体中有适当的级联类型,例如CascadeType.ALL (或) CascadeType.REMOVE ,那么上述查询将为您解决问题。

@Entity
class Employee {

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

}

有关更多详细信息,请查看thisthis

JPQL BULK DELETE (无论是使用基于字符串的 JPQL 还是使用 Criteria JPQL)都不是为了级联(即遵循字段的级联类型设置)。 如果您想要级联,那么您要么设置数据存储以使用真正的FOREIGN KEY s,要么拉回对象以删除并调用EntityManager.remove()

暂无
暂无

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

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