This is an automated email from the ASF dual-hosted git repository. borinquenkid pushed a commit to branch 8.0.x-hibernate7 in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 2a650f1bcfb0938d45b879cc29208cf07bac8ee3 Author: Walter B Duque de Estrada <[email protected]> AuthorDate: Sat Jan 10 22:23:57 2026 -0600 Map 'save-update' cascade to 'persist,merge' for Hibernate 7 compatibility --- .../orm/hibernate/cfg/GrailsDomainBinder.java | 8 ++++++-- .../cfg/domainbinding/CascadeBehavior.java | 4 ++-- .../cfg/domainbinding/CascadeBehaviorFetcher.java | 8 ++++---- .../mapping/HibernateMappingBuilderTests.groovy | 10 +++++----- .../domainbinding/CascadeBehaviorFetcherSpec.groovy | 19 ++++++++++++++----- .../CascadeBehaviorPersisterSpec.groovy | 21 +++++++++++---------- 6 files changed, 42 insertions(+), 28 deletions(-) diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java index 5ff60e052e..77c8e0443a 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java @@ -128,7 +128,7 @@ public class GrailsDomainBinder private static final String EMPTY_PATH = ""; public static final char UNDERSCORE = '_'; public static final String CASCADE_ALL = "all"; - public static final String CASCADE_SAVE_UPDATE = "save-update"; + public static final String CASCADE_SAVE_UPDATE = "persist,merge"; public static final String CASCADE_NONE = "none"; public static final String BACKTICK = "`"; @@ -1176,11 +1176,15 @@ public class GrailsDomainBinder for (String cascadeProp : cascades) { String trimmedProp = cascadeProp.trim(); - if (CASCADE_SAVE_UPDATE.equals(trimmedProp) || CASCADE_ALL.equals(trimmedProp) || CASCADE_ALL_DELETE_ORPHAN.equals(trimmedProp)) { + if (CASCADE_SAVE_UPDATE.equals(trimmedProp) || CASCADE_ALL.equals(trimmedProp) || CASCADE_ALL_DELETE_ORPHAN.equals(trimmedProp) || "save-update".equals(trimmedProp)) { return true; } } + if (cascade.contains("persist") && cascade.contains("merge")) { + return true; + } + return false; } diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehavior.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehavior.java index a055af348b..f383c143f3 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehavior.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehavior.java @@ -14,9 +14,9 @@ public enum CascadeBehavior { ALL("all"), /** - * Cascades save and update operations. Maps to "save-update". + * Cascades save and update operations. Maps to "persist,merge". */ - SAVE_UPDATE("save-update"), + SAVE_UPDATE("persist,merge"), /** * Cascades the merge operation. Maps to "merge". diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorFetcher.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorFetcher.java index f61d9d0404..b48faaa982 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorFetcher.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorFetcher.java @@ -54,12 +54,12 @@ public class CascadeBehaviorFetcher { return ALL; } else if (association.isOneToOne()) { - return ALL; + return association.isOwningSide() ? ALL : SAVE_UPDATE; } else if (association.isOneToMany()) { - return ALL; + return association.isOwningSide() ? ALL : SAVE_UPDATE; } else if (association.isManyToMany()) { - return association.isCorrectlyOwned() || association.isCircular() ? ALL :NONE; + return association.isCorrectlyOwned() || association.isCircular() ? SAVE_UPDATE :NONE; } else if (association.isManyToOne()) { if ( association.isCorrectlyOwned() && !association.isCircular()) { @@ -76,7 +76,7 @@ public class CascadeBehaviorFetcher { return ALL; } else if (Map.class.isAssignableFrom(association.getType())) { - return ALL; + return association.isCorrectlyOwned() ? ALL :SAVE_UPDATE; } else { throw new MappingException("Unrecognized association type " + association.getType() ); } diff --git a/grails-data-hibernate7/core/src/test/groovy/grails/gorm/hibernate/mapping/HibernateMappingBuilderTests.groovy b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/hibernate/mapping/HibernateMappingBuilderTests.groovy index 048bfaa6f9..b95889a1b1 100644 --- a/grails-data-hibernate7/core/src/test/groovy/grails/gorm/hibernate/mapping/HibernateMappingBuilderTests.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/hibernate/mapping/HibernateMappingBuilderTests.groovy @@ -278,7 +278,7 @@ class HibernateMappingBuilderTests { def builder = new HibernateMappingBuilder("Foo") def mapping = builder.evaluate { columns { - things cascade:'save-update' + things cascade:'persist,merge' } } @@ -298,11 +298,11 @@ class HibernateMappingBuilderTests { def builder = new HibernateMappingBuilder("Foo") def mapping = builder.evaluate { columns { - things cascade:'save-update' + things cascade:'persist,merge' } } - assertEquals 'save-update',mapping.getPropertyConfig('things').cascade + assertEquals 'persist,merge',mapping.getPropertyConfig('things').cascade } @Test @@ -345,9 +345,9 @@ class HibernateMappingBuilderTests { void testCascadesWithColumnsBlock() { def builder = new HibernateMappingBuilder("Foo") def mapping = builder.evaluate { - things cascade:'save-update' + things cascade:'persist,merge' } - assertEquals 'save-update',mapping.getPropertyConfig('things').cascade + assertEquals 'persist,merge',mapping.getPropertyConfig('things').cascade } @Test diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorFetcherSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorFetcherSpec.groovy index 11f865c48a..1b5d712cb6 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorFetcherSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorFetcherSpec.groovy @@ -1,14 +1,23 @@ package org.grails.orm.hibernate.cfg.domainbinding -import grails.gorm.annotation.Entity -import grails.gorm.specs.HibernateGormDatastoreSpec import jakarta.persistence.Embeddable + import org.hibernate.MappingException import spock.lang.Shared import spock.lang.Unroll +import grails.gorm.annotation.Entity +import grails.gorm.specs.HibernateGormDatastoreSpec -import static org.grails.orm.hibernate.cfg.domainbinding.CascadeBehavior.* +import static org.grails.orm.hibernate.cfg.domainbinding.CascadeBehavior.ALL +import static org.grails.orm.hibernate.cfg.domainbinding.CascadeBehavior.DELETE +import static org.grails.orm.hibernate.cfg.domainbinding.CascadeBehavior.EVICT +import static org.grails.orm.hibernate.cfg.domainbinding.CascadeBehavior.LOCK +import static org.grails.orm.hibernate.cfg.domainbinding.CascadeBehavior.MERGE +import static org.grails.orm.hibernate.cfg.domainbinding.CascadeBehavior.NONE +import static org.grails.orm.hibernate.cfg.domainbinding.CascadeBehavior.PERSIST +import static org.grails.orm.hibernate.cfg.domainbinding.CascadeBehavior.REPLICATE +import static org.grails.orm.hibernate.cfg.domainbinding.CascadeBehavior.SAVE_UPDATE class CascadeBehaviorFetcherSpec extends HibernateGormDatastoreSpec { @@ -18,7 +27,7 @@ class CascadeBehaviorFetcherSpec extends HibernateGormDatastoreSpec { private static final List cascadeMetadataTestData = [ // --- UNIDIRECTIONAL hasMany (should be supported in Hibernate 6+) --- ["uni: explicit 'all'", AW_All_Uni, "books", BookUni, ALL.getValue()], - ["uni: explicit 'save-update'", AW_SaveUpdate_Uni, "books", BookUni, SAVE_UPDATE.getValue()], + ["uni: explicit 'persist,merge'", AW_SaveUpdate_Uni, "books", BookUni, SAVE_UPDATE.getValue()], ["uni: explicit 'merge'", AW_Merge_Uni, "books", BookUni, MERGE.getValue()], ["uni: explicit 'delete'", AW_Delete_Uni, "books", BookUni, DELETE.getValue()], ["uni: explicit 'lock'", AW_Lock_Uni, "books", BookUni, LOCK.getValue()], @@ -127,7 +136,7 @@ class CascadeBehaviorFetcherSpec extends HibernateGormDatastoreSpec { @Entity class BookUni { String title } @Entity class AW_All_Uni { static hasMany = [books: BookUni]; static mapping = { books cascade: 'all' } } -@Entity class AW_SaveUpdate_Uni { static hasMany = [books: BookUni]; static mapping = { books cascade: 'save-update' } } +@Entity class AW_SaveUpdate_Uni { static hasMany = [books: BookUni]; static mapping = { books cascade: 'persist,merge' } } @Entity class AW_Merge_Uni { static hasMany = [books: BookUni]; static mapping = { books cascade: 'merge' } } @Entity class AW_Delete_Uni { static hasMany = [books: BookUni]; static mapping = { books cascade: 'delete' } } @Entity class AW_Lock_Uni { static hasMany = [books: BookUni]; static mapping = { books cascade: 'lock' } } diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorPersisterSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorPersisterSpec.groovy index e73e2a57c2..4e3a312595 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorPersisterSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorPersisterSpec.groovy @@ -1,14 +1,14 @@ package org.grails.orm.hibernate.cfg.domainbinding -import grails.gorm.annotation.Entity -import grails.gorm.transactions.Rollback -import org.grails.orm.hibernate.HibernateDatastore -import org.springframework.transaction.PlatformTransactionManager import spock.lang.AutoCleanup import spock.lang.Shared import spock.lang.Specification -import java.io.Serializable +import org.springframework.transaction.PlatformTransactionManager + +import grails.gorm.annotation.Entity +import grails.gorm.transactions.Rollback +import org.grails.orm.hibernate.HibernateDatastore /** * Tests the persistence behavior of various one-to-many cascade settings in GORM. @@ -81,7 +81,7 @@ class CascadeBehaviorPersisterSpec extends Specification { } @Rollback - void "test unidirectional 'save-update' cascade persists child"() { + void "test unidirectional 'persist,merge' cascade persists child"() { when: "A new owner is saved after adding a child" def owner = new Owner_SaveUpdate_Uni_P(name: "Owner") owner.addToChildren(new ChildPersister(title: "Child")) @@ -132,7 +132,7 @@ class CascadeBehaviorPersisterSpec extends Specification { } @Rollback - void "test bidirectional 'save-update' cascade persists child"() { + void "test bidirectional 'persist,merge' cascade persists child"() { when: "A new owner is saved after adding a child" def owner = new Owner_SaveUpdate_Bi_P(name: "Owner") owner.addToChildren(new Child_BT_SaveUpdate_P(title: "Child")) @@ -226,7 +226,7 @@ class CascadeBehaviorPersisterSpec extends Specification { } @Rollback - void "test map without belongsTo cascade persists child"() { + void "test map without belongsTo 'persist,merge' cascade persists child"() { when: "A new owner with a map entry is saved" def owner = new MapParentP_SaveUpdate(name: "Owner") owner.settings = [foo: new MapChildP_SaveUpdate(childValue: "bar")] @@ -311,7 +311,7 @@ class Owner_All_Uni_P { class Owner_SaveUpdate_Uni_P { String name static hasMany = [children: ChildPersister] - static mapping = { children cascade: 'save-update' } + static mapping = { children cascade: 'persist,merge' } } @Entity @@ -390,7 +390,7 @@ class Owner_SaveUpdate_Bi_P { String name Set<Child_BT_SaveUpdate_P> children static hasMany = [children: Child_BT_SaveUpdate_P] - static mapping = { children cascade: 'save-update' } + static mapping = { children cascade: 'persist,merge' } } @Entity @@ -523,6 +523,7 @@ class MapParentP_SaveUpdate { String name static hasMany = [settings: MapChildP_SaveUpdate] Map<String, MapChildP_SaveUpdate> settings + static mapping = { settings cascade: 'persist,merge' } } @Entity
