Skip to content

Commit d6e5f5d

Browse files
committed
HHH-19730 Avoid bulk-delete collection cleanup when delete cascaded
1 parent 8bb7c25 commit d6e5f5d

File tree

4 files changed

+44
-12
lines changed

4 files changed

+44
-12
lines changed

hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMutationStrategyHelper.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,8 @@ private static void visitCollectionTableDeletes(
174174
final SessionFactoryImplementor sessionFactory = attributeMapping.getCollectionDescriptor().getFactory();
175175
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
176176

177-
if ( separateCollectionTable == null ) {
178-
// one-to-many - update the matching rows in the associated table setting the fk column(s) to null
179-
// not yet implemented - do nothing
180-
}
181-
else {
177+
// Skip deleting rows in collection tables if cascade delete is enabled
178+
if ( separateCollectionTable != null && !attributeMapping.getCollectionDescriptor().isCascadeDeleteEnabled() ) {
182179
// element-collection or many-to-many - delete the collection-table row
183180

184181
final NamedTableReference tableReference = new NamedTableReference(

hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ protected void addDmlCtes(
7777
SqmMutationStrategyHelper.visitCollectionTables(
7878
(EntityMappingType) mutatingTableGroup.getModelPart(),
7979
pluralAttribute -> {
80-
if ( pluralAttribute.getSeparateCollectionTable() != null ) {
80+
// Skip deleting rows in collection tables if cascade delete is enabled
81+
if ( pluralAttribute.getSeparateCollectionTable() != null
82+
&& !pluralAttribute.getCollectionDescriptor().isCascadeDeleteEnabled() ) {
8183
// Ensure that the FK target columns are available
8284
final boolean useFkTarget = !pluralAttribute.getKeyDescriptor()
8385
.getTargetPart().isEntityIdentifierMapping();

hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineDeleteHandler.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ protected InlineDeleteHandler(
7070
SqmMutationStrategyHelper.visitCollectionTables(
7171
getEntityDescriptor(),
7272
pluralAttribute -> {
73-
if ( pluralAttribute.getSeparateCollectionTable() != null ) {
73+
// Skip deleting rows in collection tables if cascade delete is enabled
74+
if ( pluralAttribute.getSeparateCollectionTable() != null
75+
&& !pluralAttribute.getCollectionDescriptor().isCascadeDeleteEnabled() ) {
7476
// this collection has a separate collection table, meaning it is one of:
7577
// 1) element-collection
7678
// 2) many-to-many

hibernate-core/src/test/java/org/hibernate/orm/test/ondeletecascade/OnDeleteCollectionTest.java

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@
77
import jakarta.persistence.ElementCollection;
88
import jakarta.persistence.Entity;
99
import jakarta.persistence.Id;
10-
import jakarta.persistence.Inheritance;
11-
import jakarta.persistence.InheritanceType;
1210
import org.hibernate.Hibernate;
1311
import org.hibernate.annotations.OnDelete;
1412
import org.hibernate.annotations.OnDeleteAction;
1513
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
14+
import org.hibernate.testing.orm.junit.Jira;
1615
import org.hibernate.testing.orm.junit.Jpa;
1716
import org.junit.jupiter.api.Test;
1817

@@ -26,10 +25,12 @@
2625
@Jpa(annotatedClasses =
2726
{OnDeleteCollectionTest.A.class},
2827
useCollectingStatementInspector = true)
29-
//@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsCascadeDeleteCheck.class)
3028
class OnDeleteCollectionTest {
31-
@Test void test(EntityManagerFactoryScope scope) {
29+
@Test
30+
void test(EntityManagerFactoryScope scope) {
3231
var inspector = scope.getCollectingStatementInspector();
32+
inspector.clear();
33+
3334
scope.inTransaction( em -> {
3435
A a = new A();
3536
a.id = 2;
@@ -66,8 +67,38 @@ class OnDeleteCollectionTest {
6667
});
6768
}
6869

70+
@Test
71+
@Jira("https://hibernate.atlassian.net/browse/HHH-19730")
72+
void testBulk(EntityManagerFactoryScope scope) {
73+
var inspector = scope.getCollectingStatementInspector();
74+
inspector.clear();
75+
76+
scope.inTransaction( em -> {
77+
A a = new A();
78+
a.id = 2;
79+
a.bs.add( "b" );
80+
em.persist( a );
81+
} );
82+
inspector.assertExecutedCount( 2 );
83+
inspector.clear();
84+
85+
scope.inTransaction( em -> {
86+
em.createQuery( "delete from A" ).executeUpdate();
87+
inspector.assertExecutedCount( scope.getDialect().supportsCascadeDelete() ? 1 : 2 );
88+
} );
89+
inspector.clear();
90+
91+
scope.inTransaction( em -> {
92+
assertEquals( 0,
93+
em.createNativeQuery( "select count(*) from A_bs", Integer.class )
94+
.getSingleResultOrNull() );
95+
assertEquals( 0,
96+
em.createNativeQuery( "select count(*) from A", Integer.class )
97+
.getSingleResultOrNull() );
98+
});
99+
}
100+
69101
@Entity(name = "A")
70-
@Inheritance(strategy = InheritanceType.JOINED)
71102
static class A {
72103
@Id
73104
long id;

0 commit comments

Comments
 (0)