How does the JPA CriteriaDelete work
A JPA CriteriaDelete
statement generates a JPQL bulk delete statement, that's parsed to an SQL bulk delete statement.
So, the following JPA CriteriaDelete
statement:
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:
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
.
Bulk Delete Cascading
To enable cascading when executing bulk delete, you need to use DDL-level cascade when declaring the FK constraints.
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.
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.
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:
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(foreignKey = @ForeignKey(name = "FK_POST_COMMENT_POST_ID"))
@OnDelete(action = OnDeleteAction.CASCADE)
private Post post;