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 3e2f7d5e5fc70fee9c83e7cdff1ecd44cca97d5e
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Sun Feb 15 17:48:04 2026 -0600

    Refactor NaturalId to return Optional<UniqueKey> and delegate side effects 
to the binder.
---
 .../org/grails/orm/hibernate/cfg/NaturalId.groovy  |  31 +++++
 .../binder/NaturalIdentifierBinder.java            |  29 +---
 .../grails/orm/hibernate/cfg/NaturalIdSpec.groovy  |  93 +++++++++++++
 .../NaturalIdentifierBinderSpec.groovy             | 148 +++------------------
 4 files changed, 151 insertions(+), 150 deletions(-)

diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/NaturalId.groovy
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/NaturalId.groovy
index 852d254033..dbc8ffdf04 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/NaturalId.groovy
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/NaturalId.groovy
@@ -18,6 +18,9 @@ package org.grails.orm.hibernate.cfg
 import groovy.transform.CompileStatic
 import groovy.transform.builder.Builder
 import groovy.transform.builder.SimpleStrategy
+import org.grails.orm.hibernate.cfg.domainbinding.util.UniqueNameGenerator
+import org.hibernate.mapping.PersistentClass
+import org.hibernate.mapping.UniqueKey
 
 /**
  * @author Graeme Rocher
@@ -34,4 +37,32 @@ class NaturalId {
      * Whether the natural id is mutable
      */
     boolean mutable = false
+
+    /**
+     * Creates the unique key for the natural identifier
+     * @param persistentClass The persistent class
+     * @return An Optional containing the UniqueKey if properties were found, 
otherwise empty
+     */
+    Optional<UniqueKey> createUniqueKey(PersistentClass persistentClass) {
+        if (propertyNames == null || propertyNames.isEmpty()) {
+            return Optional.empty()
+        }
+
+        UniqueKey uk = new UniqueKey(persistentClass.table)
+        int pks = 0
+        for (String propertyName in propertyNames) {
+            if (persistentClass.hasProperty(propertyName)) {
+                def property = persistentClass.getProperty(propertyName)
+                property.setNaturalIdentifier(true)
+                property.setUpdatable(mutable)
+                uk.addColumns(property.value)
+                pks++
+            }
+        }
+
+        if (pks > 0) {
+            return Optional.of(uk)
+        }
+        return Optional.empty()
+    }
 }
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 b27f4c1e8d..e932f253e8 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
@@ -28,29 +28,10 @@ public class NaturalIdentifierBinder {
     public void bindNaturalIdentifier(Mapping mapping, PersistentClass 
persistentClass) {
         Optional.ofNullable(mapping.getIdentity())
                 .map(HibernateIdentity::getNatural)
-                .ifPresent(naturalId -> {
-                    if(CollectionUtils.isEmpty(naturalId.getPropertyNames())) {
-                        return;
-                    }
-                    var uk = new UniqueKey();
-                    uk.setTable(persistentClass.getTable());
-                    Stream<String> stringStream = naturalId.getPropertyNames()
-                            .stream()
-                            .filter(persistentClass::hasProperty);
-                    List<String> list = stringStream.toList();
-                    Integer pks = list.stream()
-                            .map(persistentClass::getProperty)
-                            .map(property -> {
-                                property.setNaturalIdentifier(true);
-                                property.setUpdatable(naturalId.isMutable());
-                                uk.addColumns(property.getValue());
-                                return 1;
-                            })
-                            .reduce(0, Integer::sum);
-                    if (pks > 0) {
-                        uniqueNameGenerator.setGeneratedUniqueName(uk);
-                        persistentClass.getTable().addUniqueKey(uk);
-                    }
-        });
+                .flatMap(naturalId -> 
naturalId.createUniqueKey(persistentClass))
+                .ifPresent(uk -> {
+                    uniqueNameGenerator.setGeneratedUniqueName(uk);
+                    persistentClass.getTable().addUniqueKey(uk);
+                });
     }
 }
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/NaturalIdSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/NaturalIdSpec.groovy
new file mode 100644
index 0000000000..12e0883a51
--- /dev/null
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/NaturalIdSpec.groovy
@@ -0,0 +1,93 @@
+package org.grails.orm.hibernate.cfg
+
+import grails.gorm.specs.HibernateGormDatastoreSpec
+import org.grails.orm.hibernate.cfg.domainbinding.util.UniqueNameGenerator
+import org.hibernate.mapping.Column
+import org.hibernate.mapping.Property
+import org.hibernate.mapping.RootClass
+import org.hibernate.mapping.Table
+import org.hibernate.mapping.UniqueKey
+import org.hibernate.mapping.Value
+
+class NaturalIdSpec extends HibernateGormDatastoreSpec {
+
+    void "test createUniqueKey with a single property"() {
+        given:
+        def naturalId = new NaturalId(propertyNames: ["id1"], mutable: true)
+        def property = new Property()
+        property.name = "id1"
+        def value = Mock(Value)
+        property.value = value
+        def column = new Column("id1")
+        def table = new Table("test_table")
+        def rootClass = new 
RootClass(getGrailsDomainBinder().getMetadataBuildingContext())
+        rootClass.addProperty(property)
+        rootClass.table = table
+        value.getSelectables() >> [column]
+        value.hasAnyUpdatableColumns() >> true
+
+        when:
+        def result = naturalId.createUniqueKey(rootClass)
+
+        then:
+        result.isPresent()
+        def uk = result.get()
+        uk.table == table
+        uk.columnSpan == 1
+        property.isNaturalIdentifier()
+        property.isUpdateable()
+    }
+
+    void "test createUniqueKey with composite property"() {
+        given:
+        def naturalId = new NaturalId(propertyNames: ["id1", "id2"], mutable: 
false)
+        def property1 = new Property()
+        property1.name = "id1"
+        def value1 = Mock(Value)
+        property1.value = value1
+        def column1 = new Column("id1")
+        
+        def property2 = new Property()
+        property2.name = "id2"
+        def value2 = Mock(Value)
+        property2.value = value2
+        def column2 = new Column("id2")
+        
+        def table = new Table("test_table")
+        def rootClass = new 
RootClass(getGrailsDomainBinder().getMetadataBuildingContext())
+        rootClass.addProperty(property1)
+        rootClass.addProperty(property2)
+        rootClass.table = table
+        value1.getSelectables() >> [column1]
+        value1.hasAnyUpdatableColumns() >> false
+        value2.getSelectables() >> [column2]
+        value2.hasAnyUpdatableColumns() >> false
+
+        when:
+        def result = naturalId.createUniqueKey(rootClass)
+
+        then:
+        result.isPresent()
+        def uk = result.get()
+        uk.table == table
+        uk.columnSpan == 2
+        property1.isNaturalIdentifier()
+        !property1.isUpdateable()
+        property2.isNaturalIdentifier()
+        !property2.isUpdateable()
+    }
+
+    void "test createUniqueKey with empty property names"() {
+        given:
+        def naturalId = new NaturalId(propertyNames: [], mutable: false)
+        def table = new Table("test_table")
+        def rootClass = new 
RootClass(getGrailsDomainBinder().getMetadataBuildingContext())
+        rootClass.table = table
+
+        when:
+        def result = naturalId.createUniqueKey(rootClass)
+
+        then:
+        result.isEmpty()
+    }
+}
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 6abb9c8a30..103ecb81b6 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
@@ -2,150 +2,53 @@ 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
-
-import org.hibernate.mapping.Column
-import org.hibernate.mapping.Property
+import 
org.grails.orm.hibernate.cfg.domainbinding.binder.NaturalIdentifierBinder
+import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateIdentity
+import org.grails.orm.hibernate.cfg.domainbinding.util.UniqueNameGenerator
 import org.hibernate.mapping.RootClass
 import org.hibernate.mapping.Table
 import org.hibernate.mapping.UniqueKey
-import org.hibernate.mapping.Value
-import spock.lang.Unroll
-
-import 
org.grails.orm.hibernate.cfg.domainbinding.binder.NaturalIdentifierBinder
-import org.grails.orm.hibernate.cfg.domainbinding.util.UniqueNameGenerator
 
 class NaturalIdentifierBinderSpec extends HibernateGormDatastoreSpec {
 
-    @Unroll("test bindNaturalIdentifier with a single property and 
mutable=#isMutable")
-    void "test bindNaturalIdentifier with a single property"(boolean 
isMutable) {
+    void "test bindNaturalIdentifier calls NaturalId.createUniqueKey and 
handles result"() {
         given:
-        def mapping = Mock(Mapping.class)
-        def identity = Mock(Identity)
+        def mapping = Mock(Mapping)
+        def identity = Mock(HibernateIdentity)
         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() >> isMutable
+        def uk = Mock(UniqueKey)
+        def table = Mock(Table)
         def rootClass = new 
RootClass(getGrailsDomainBinder().getMetadataBuildingContext())
-        rootClass.setIdentifierProperty(property)
         rootClass.setTable(table)
-        value.getSelectables() >> []
-        value.hasAnyUpdatableColumns() >> isMutable
         def uniqueNameGenerator = Mock(UniqueNameGenerator)
         def binder = new NaturalIdentifierBinder(uniqueNameGenerator)
 
-        when:
-        binder.bindNaturalIdentifier(mapping, rootClass)
-
-        then:
-        1 * uniqueNameGenerator.setGeneratedUniqueName(_)
-        property.isNaturalIdentifier()
-        property.isUpdatable() == isMutable
-        1 * table.addUniqueKey(_)
-
-        where:
-        isMutable << [true, false]
-    }
-
-    void "test bindNaturalIdentifier with a composite (multi-property) natural 
id"() {
-        given:
-        def mapping = Mock(Mapping.class)
-        def identity = Mock(Identity)
-        def naturalId = Mock(NaturalId)
-        def property1 =new Property()
-        property1.setName("id1")
-        def value1 = Mock(Value)
-        property1.setValue(value1)
-        def column1 = new Column('id1')
-        def property2 = new Property()
-        property2.setName("id2")
-        def value2 = Mock(Value)
-        property2.setValue(value2)
-        def column2 = new Column('id2')
-        Table table = Mock(Table)
-
         mapping.getIdentity() >> identity
         identity.getNatural() >> naturalId
-        naturalId.getPropertyNames() >> ["id1", "id2"]
-        naturalId.isMutable() >> true
-
-        def rootClass = new 
RootClass(getGrailsDomainBinder().getMetadataBuildingContext())
-        rootClass.setTable(table)
-        rootClass.addProperty(property1)
-        rootClass.addProperty(property2)
-        rootClass.getTable() >> table
-
-        value1.getSelectables() >> [column1]
-        value2.getSelectables() >> [column2]
-
-        def uniqueNameGenerator = Mock(UniqueNameGenerator)
-        def binder = new NaturalIdentifierBinder(uniqueNameGenerator)
+        naturalId.createUniqueKey(rootClass) >> Optional.of(uk)
 
         when:
         binder.bindNaturalIdentifier(mapping, rootClass)
 
         then:
-        property1.isNaturalIdentifier()
-        property2.isNaturalIdentifier()
-        1 * table.addUniqueKey(_) >> { uks ->
-            def uk = uks.get(0) as UniqueKey
-            assert uk.getColumnSpan() == 2
-            assert uk.getColumns().get(0) == column1
-            assert  uk.getColumns().get(1)  == column2
-        }
+        1 * uniqueNameGenerator.setGeneratedUniqueName(uk)
+        1 * table.addUniqueKey(uk)
     }
 
-    void "test bindNaturalIdentifier with CompositeIdentity"() {
+    void "test bindNaturalIdentifier when NaturalId returns empty result"() {
         given:
-        def mapping = Mock(Mapping.class)
-        def identity = Mock(CompositeIdentity)
+        def mapping = Mock(Mapping)
+        def identity = Mock(HibernateIdentity)
         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)
-        def identity = Mock(Identity)
         def rootClass = new 
RootClass(getGrailsDomainBinder().getMetadataBuildingContext())
         def uniqueNameGenerator = Mock(UniqueNameGenerator)
         def binder = new NaturalIdentifierBinder(uniqueNameGenerator)
 
         mapping.getIdentity() >> identity
-        identity.getNatural() >> null
+        identity.getNatural() >> naturalId
+        naturalId.createUniqueKey(rootClass) >> Optional.empty()
 
         when:
         binder.bindNaturalIdentifier(mapping, rootClass)
@@ -154,26 +57,19 @@ class NaturalIdentifierBinderSpec extends 
HibernateGormDatastoreSpec {
         0 * uniqueNameGenerator._
     }
 
-    void "test bindNaturalIdentifier when property not found"() {
+    void "test bindNaturalIdentifier when no identity is defined"() {
         given:
-        def mapping = Mock(Mapping.class)
-        def identity = Mock(Identity)
-        def naturalId = Mock(NaturalId)
+        def mapping = Mock(Mapping)
         def rootClass = new 
RootClass(getGrailsDomainBinder().getMetadataBuildingContext())
         def uniqueNameGenerator = Mock(UniqueNameGenerator)
         def binder = new NaturalIdentifierBinder(uniqueNameGenerator)
-        Table table = Mock(Table)
-        mapping.getIdentity() >> identity
-        identity.getNatural() >> naturalId
-        naturalId.getPropertyNames() >> ["nonExistentProperty"]
-        rootClass.setTable(table)
+
+        mapping.getIdentity() >> null
 
         when:
         binder.bindNaturalIdentifier(mapping, rootClass)
 
         then:
-        0 * uniqueNameGenerator.setGeneratedUniqueName(_)
-        0 * table.addUniqueKey(_)
+        0 * uniqueNameGenerator._
     }
-
-}
\ No newline at end of file
+}

Reply via email to