diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMutationStrategyHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMutationStrategyHelper.java index 32eb45fb848d..0352bfb705b0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMutationStrategyHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMutationStrategyHelper.java @@ -174,11 +174,8 @@ private static void visitCollectionTableDeletes( final SessionFactoryImplementor sessionFactory = attributeMapping.getCollectionDescriptor().getFactory(); final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); - if ( separateCollectionTable == null ) { - // one-to-many - update the matching rows in the associated table setting the fk column(s) to null - // not yet implemented - do nothing - } - else { + // Skip deleting rows in collection tables if cascade delete is enabled + if ( separateCollectionTable != null && !attributeMapping.getCollectionDescriptor().isCascadeDeleteEnabled() ) { // element-collection or many-to-many - delete the collection-table row final NamedTableReference tableReference = new NamedTableReference( diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java index 96f5af4ecbea..f7fb035dc28b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java @@ -77,7 +77,9 @@ protected void addDmlCtes( SqmMutationStrategyHelper.visitCollectionTables( (EntityMappingType) mutatingTableGroup.getModelPart(), pluralAttribute -> { - if ( pluralAttribute.getSeparateCollectionTable() != null ) { + // Skip deleting rows in collection tables if cascade delete is enabled + if ( pluralAttribute.getSeparateCollectionTable() != null + && !pluralAttribute.getCollectionDescriptor().isCascadeDeleteEnabled() ) { // Ensure that the FK target columns are available final boolean useFkTarget = !pluralAttribute.getKeyDescriptor() .getTargetPart().isEntityIdentifierMapping(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineDeleteHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineDeleteHandler.java index 42f712908e59..ca978cc991f2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineDeleteHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineDeleteHandler.java @@ -70,7 +70,9 @@ protected InlineDeleteHandler( SqmMutationStrategyHelper.visitCollectionTables( getEntityDescriptor(), pluralAttribute -> { - if ( pluralAttribute.getSeparateCollectionTable() != null ) { + // Skip deleting rows in collection tables if cascade delete is enabled + if ( pluralAttribute.getSeparateCollectionTable() != null + && !pluralAttribute.getCollectionDescriptor().isCascadeDeleteEnabled() ) { // this collection has a separate collection table, meaning it is one of: // 1) element-collection // 2) many-to-many diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ondeletecascade/OnDeleteCollectionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/ondeletecascade/OnDeleteCollectionTest.java index a2136d3eaebe..bbe71df08ceb 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/ondeletecascade/OnDeleteCollectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/ondeletecascade/OnDeleteCollectionTest.java @@ -7,12 +7,11 @@ import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; import jakarta.persistence.Id; -import jakarta.persistence.Inheritance; -import jakarta.persistence.InheritanceType; import org.hibernate.Hibernate; import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.orm.junit.Jpa; import org.junit.jupiter.api.Test; @@ -26,10 +25,12 @@ @Jpa(annotatedClasses = {OnDeleteCollectionTest.A.class}, useCollectingStatementInspector = true) -//@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsCascadeDeleteCheck.class) class OnDeleteCollectionTest { - @Test void test(EntityManagerFactoryScope scope) { + @Test + void test(EntityManagerFactoryScope scope) { var inspector = scope.getCollectingStatementInspector(); + inspector.clear(); + scope.inTransaction( em -> { A a = new A(); a.id = 2; @@ -66,8 +67,38 @@ class OnDeleteCollectionTest { }); } + @Test + @Jira("https://hibernate.atlassian.net/browse/HHH-19730") + void testBulk(EntityManagerFactoryScope scope) { + var inspector = scope.getCollectingStatementInspector(); + inspector.clear(); + + scope.inTransaction( em -> { + A a = new A(); + a.id = 2; + a.bs.add( "b" ); + em.persist( a ); + } ); + inspector.assertExecutedCount( 2 ); + inspector.clear(); + + scope.inTransaction( em -> { + em.createQuery( "delete from A" ).executeUpdate(); + inspector.assertExecutedCount( scope.getDialect().supportsCascadeDelete() ? 1 : 2 ); + } ); + inspector.clear(); + + scope.inTransaction( em -> { + assertEquals( 0, + em.createNativeQuery( "select count(*) from A_bs", Integer.class ) + .getSingleResultOrNull() ); + assertEquals( 0, + em.createNativeQuery( "select count(*) from A", Integer.class ) + .getSingleResultOrNull() ); + }); + } + @Entity(name = "A") - @Inheritance(strategy = InheritanceType.JOINED) static class A { @Id long id;