jpa中批量删除的最佳做法是什么

时间:2015-03-17 16:31:16

标签: java jpa eclipselink jpa-2.1

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

所以似乎只有我要做的解决方案是先选择,然后单独删除每个元素。这对我来说似乎没有错。

有人有更好的想法如何进行批量删除吗?这实际上是更好的方式吗?

如果它有助于我使用eclipselink 2.5.2。

4 个答案:

答案 0 :(得分:7)

选项包括:

  1. 使用cascade.Remove设置映射,加载实体 并在每个
  2. 上调用em.remove
  3. 在主要实体上使用批量删除,并使用" ON DELETE CASCADE"数据库选项设置,以便数据库级联 删除你。 EclipseLink有一个@CascadeOnDelete注释 让它知道" ON DELETE CASCADE"是建立在一种关系上,或是 如果使用JPA生成DDL,则创建它:http://eclipse.org/eclipselink/documentation/2.5/jpa/extensions/a_cascadeondelete.htm
  4. 使用多个批量删除删除在删除主实体之前可能引用的子项。例如:"删除FROM Child c,其中c.parent =(从父P中选择p,其中[delete-conditions])"和"从父母p中删除[delete-conditions]"有关详细信息,请参阅http://docs.oracle.com/middleware/1212/toplink/OTLCG/queries.htm#OTLCG94370的第10.2.4节。

答案 1 :(得分:5)

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_comment行的post记录。

执行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;

有关使用Criteria API批量更新和删除查询的更多详细信息,请查看this article

答案 2 :(得分:3)

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

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

您甚至可以使用查询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

答案 3 :(得分:2)

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