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 34866ea7fa1a024c3870bb47712ddef45b6cf0c4
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Sun Feb 15 17:09:37 2026 -0600

    Refactor natural identifier binding to support composite primary keys and 
fix mapping DSL configuration for natural identifiers.
---
 .../orm/hibernate/cfg/CompositeIdentity.groovy     | 17 ++++++++
 .../org/grails/orm/hibernate/cfg/Identity.groovy   |  3 +-
 .../binder/NaturalIdentifierBinder.java            |  5 +--
 .../domainbinding/hibernate/HibernateIdentity.java | 12 ++++++
 .../hibernate/HibernateMappingBuilder.groovy       | 46 +++++++++++-----------
 .../NaturalIdentifierBinderSpec.groovy             | 33 ++++++++++++++++
 6 files changed, 89 insertions(+), 27 deletions(-)

diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/CompositeIdentity.groovy
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/CompositeIdentity.groovy
index bdb85a70b2..b39454ddc3 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/CompositeIdentity.groovy
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/CompositeIdentity.groovy
@@ -40,4 +40,21 @@ class CompositeIdentity extends Property implements 
HibernateIdentity {
      * The composite id class
      */
     Class compositeClass
+    /**
+     * The natural id definition
+     */
+    NaturalId natural
+
+    /**
+     * Define the natural id
+     * @param naturalIdDef The callable
+     * @return This id
+     */
+    CompositeIdentity naturalId(@DelegatesTo(NaturalId) Closure naturalIdDef) {
+        this.natural = new NaturalId()
+        naturalIdDef.setDelegate(this.natural)
+        naturalIdDef.setResolveStrategy(Closure.DELEGATE_ONLY)
+        naturalIdDef.call()
+        return this
+    }
 }
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/Identity.groovy
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/Identity.groovy
index 87fdc352a4..45039669ce 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/Identity.groovy
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/Identity.groovy
@@ -80,7 +80,8 @@ class Identity extends Property implements HibernateIdentity {
      * @return This id
      */
     Identity naturalId(@DelegatesTo(NaturalId) Closure naturalIdDef) {
-        naturalIdDef.setDelegate(new NaturalId())
+        this.natural = new NaturalId()
+        naturalIdDef.setDelegate(this.natural)
         naturalIdDef.setResolveStrategy(Closure.DELEGATE_ONLY)
         naturalIdDef.call()
         return this
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/NaturalIdentifierBinder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/NaturalIdentifierBinder.java
index 0dec49abab..b27f4c1e8d 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/NaturalIdentifierBinder.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/NaturalIdentifierBinder.java
@@ -3,6 +3,7 @@ package org.grails.orm.hibernate.cfg.domainbinding.binder;
 import org.apache.commons.collections.CollectionUtils;
 import org.grails.orm.hibernate.cfg.Identity;
 import org.grails.orm.hibernate.cfg.Mapping;
+import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateIdentity;
 import org.grails.orm.hibernate.cfg.domainbinding.util.UniqueNameGenerator;
 
 import org.hibernate.mapping.PersistentClass;
@@ -26,9 +27,7 @@ public class NaturalIdentifierBinder {
 
     public void bindNaturalIdentifier(Mapping mapping, PersistentClass 
persistentClass) {
         Optional.ofNullable(mapping.getIdentity())
-                .filter(Identity.class::isInstance)
-                .map(Identity.class::cast)
-                .map(Identity::getNatural)
+                .map(HibernateIdentity::getNatural)
                 .ifPresent(naturalId -> {
                     if(CollectionUtils.isEmpty(naturalId.getPropertyNames())) {
                         return;
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateIdentity.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateIdentity.java
index 2694ce20fd..3e1355a7c9 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateIdentity.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateIdentity.java
@@ -1,7 +1,19 @@
 package org.grails.orm.hibernate.cfg.domainbinding.hibernate;
 
+import org.grails.orm.hibernate.cfg.NaturalId;
+
 /**
  * A marker interface for single and composite identity configurations in GORM 
for Hibernate.
  */
 public interface HibernateIdentity {
+    /**
+     * @return The natural id definition
+     */
+    NaturalId getNatural();
+
+    /**
+     * Sets the natural id definition
+     * @param natural The natural id definition
+     */
+    void setNatural(NaturalId natural);
 }
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingBuilder.groovy
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingBuilder.groovy
index 7fc2f09ca6..048ccc2489 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingBuilder.groovy
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingBuilder.groovy
@@ -28,6 +28,7 @@ import jakarta.persistence.AccessType
 import org.grails.orm.hibernate.cfg.CacheConfig
 import org.grails.orm.hibernate.cfg.ColumnConfig
 import org.grails.orm.hibernate.cfg.CompositeIdentity
+import org.grails.orm.hibernate.cfg.Identity
 import org.grails.orm.hibernate.cfg.JoinTable
 import org.grails.orm.hibernate.cfg.Mapping
 import org.grails.orm.hibernate.cfg.NaturalId
@@ -393,43 +394,42 @@ class HibernateMappingBuilder implements 
MappingConfigurationBuilder<Mapping, Pr
         if (args.composite) {
             mapping.identity = new 
CompositeIdentity(propertyNames:args.composite as String[])
             if (args.compositeClass) {
-                mapping.identity.compositeClass = args.compositeClass
+                ((CompositeIdentity)mapping.identity).compositeClass = 
(Class)args.compositeClass
             }
         }
         else {
             if (args?.generator) {
-                mapping.identity.generator = args.remove('generator')
+                ((Identity)mapping.identity).generator = 
args.remove('generator').toString()
             }
             if (args?.name) {
-                mapping.identity.name = args.remove('name').toString()
+                ((Identity)mapping.identity).name = 
args.remove('name').toString()
             }
             if (args?.params) {
-                def params = args.remove('params')
+                def params = (Map)args.remove('params')
                 for (entry in params) {
                     params[entry.key] = entry.value?.toString()
                 }
-                mapping.identity.params = params
+                ((Identity)mapping.identity).params = params
             }
-            if (args?.natural) {
-                def naturalArgs = args.remove('natural')
-                def propertyNames = naturalArgs instanceof Map ? 
naturalArgs.remove('properties') : naturalArgs
-
-                if (propertyNames) {
-                    def ni = new NaturalId()
-                    ni.mutable = (naturalArgs instanceof Map) && 
naturalArgs.mutable ?: false
-                    if (propertyNames instanceof List) {
-                        ni.propertyNames = propertyNames
-                    }
-                    else {
-                        ni.propertyNames = [propertyNames.toString()]
-                    }
-                    mapping.identity.natural = ni
+        }
+        if (args?.natural) {
+            def naturalArgs = args.remove('natural')
+            def propertyNames = naturalArgs instanceof Map ? 
((Map)naturalArgs).remove('properties') : naturalArgs
+
+            if (propertyNames) {
+                def ni = new NaturalId()
+                ni.mutable = (naturalArgs instanceof Map) && 
((Map)naturalArgs).mutable ?: false
+                if (propertyNames instanceof List) {
+                    ni.propertyNames = (List<String>)propertyNames
                 }
+                else {
+                    ni.propertyNames = [propertyNames.toString()]
+                }
+                mapping.identity.natural = ni
             }
-            // still more arguments?
-            if (args) {
-                handleMethodMissing("id", [args] as Object[])
-            }
+        }
+        if (!args.composite && args) {
+            handleMethodMissing("id", [args] as Object[])
         }
     }
 
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/NaturalIdentifierBinderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/NaturalIdentifierBinderSpec.groovy
index 036d04e14b..6abb9c8a30 100644
--- 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/NaturalIdentifierBinderSpec.groovy
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/NaturalIdentifierBinderSpec.groovy
@@ -1,6 +1,7 @@
 package org.grails.orm.hibernate.cfg.domainbinding
 
 import grails.gorm.specs.HibernateGormDatastoreSpec
+import org.grails.orm.hibernate.cfg.CompositeIdentity
 import org.grails.orm.hibernate.cfg.Identity
 import org.grails.orm.hibernate.cfg.Mapping
 import org.grails.orm.hibernate.cfg.NaturalId
@@ -103,6 +104,38 @@ class NaturalIdentifierBinderSpec extends 
HibernateGormDatastoreSpec {
         }
     }
 
+    void "test bindNaturalIdentifier with CompositeIdentity"() {
+        given:
+        def mapping = Mock(Mapping.class)
+        def identity = Mock(CompositeIdentity)
+        def naturalId = Mock(NaturalId)
+        def property = new Property()
+        property.setName("id1")
+        def value = Mock(Value)
+        property.setValue(value)
+        Table table = Mock(Table)
+        def id1 = "id1"
+        mapping.getIdentity() >> identity
+        identity.getNatural() >> naturalId
+        naturalId.getPropertyNames() >> [id1]
+        naturalId.isMutable() >> false
+        def rootClass = new 
RootClass(getGrailsDomainBinder().getMetadataBuildingContext())
+        rootClass.addProperty(property)
+        rootClass.setTable(table)
+        value.getSelectables() >> []
+
+        def uniqueNameGenerator = Mock(UniqueNameGenerator)
+        def binder = new NaturalIdentifierBinder(uniqueNameGenerator)
+
+        when:
+        binder.bindNaturalIdentifier(mapping, rootClass)
+
+        then:
+        1 * uniqueNameGenerator.setGeneratedUniqueName(_)
+        property.isNaturalIdentifier()
+        1 * table.addUniqueKey(_)
+    }
+
     void "test bindNaturalIdentifier when no natural id is defined"() {
         given:
         def mapping = Mock(Mapping.class)

Reply via email to