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

Reply via email to