Repository navigation
[OPENJPA-2990] Remove the rows of the tables owned by bulk deleted entities - #198
Merged
Merged
Conversation
…tities A bulk delete does not cascade to related entities, but the rows of the join tables and element collection tables owned by the deleted entities are not entities themselves. Since the cascade guard was removed from the JDBC bulk delete strategy they were left behind, dangling on the primary keys of the deleted rows. When the candidate owns such a table, the delete now evaluates the criteria exactly once into a list of primary keys and deletes by those keys: the owned tables first, the entity table last. Nothing re-evaluates the criteria against a database a previous statement has already changed. Candidates with a composite primary key, or with a join foreign key that is not a single column referencing that primary key, are executed in memory instead. The compatibility option CleanupOwnedTablesOnBulkDelete (default true) suppresses the cleanup. A delete that carries no criteria at all - no where clause and no discriminator or subclass condition - matches every candidate row, so every row of every owned table belongs to a deleted candidate. Such a delete skips the key select and empties the owned tables outright, which keeps the single statement delete of a whole table a single statement per table. The table of a join foreign key is only taken for an owned table if it is neither a table of the key type nor one of the element type or of any of their joined superclasses, so an inverse key mapping never turns into a delete of entity rows. An embedded value has no table of its own - the table of an embeddable is the table it is embedded into - so the collection table of an element collection of embeddables is not mistaken for the table of a related entity. A join foreign key that carries constant columns discriminates the rows of a table that is shared by more than one mapping, so the candidates do not own every row of it: neither the key delete nor the delete without criteria is applicable and such a candidate is executed in memory.
solomax
approved these changes
Sep 28, 2026
| if (isUnfiltered(sel) && !hasConstantJoin(joins)) { | ||
| // every row of every owned table belongs to a deleted | ||
| // candidate, so there is nothing to materialize | ||
| if (owned == null) |
Contributor
There was a problem hiding this comment.
I would use {} everywhere :)
cristof
approved these changes
Oct 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A bulk JPQL
DELETEdeleted only the entity rows and left behind the rows of the tables the entity owns — join tables of uni-directional@OneToMany/@ManyToManyand@ElementCollectiontables — dangling on deleted primary keys; cascading to related entities stays absent as the spec requires. The candidate keys are now selected once and the owned rows and entity rows are deleted by key in chunkedINlists, so criteria such asSIZE(),MEMBER OFor a correlatedEXISTScannot be affected by the deletes, while a criteria-less delete skips the key select entirely; composite ids, multi-column join foreign keys and shared container tables with constant join columns fall back to the in-memory path. The newCleanupOwnedTablesOnBulkDeletecompatibility option restores the previous behaviour, and the migration guide documents the statement sequence, the fallbacks and the remaining bi-directional join table limitation.