This is an automated email from the ASF dual-hosted git repository.

borinquenkid pushed a commit to branch 8.0.x-hibernate7-dev
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit f71eac0999a3d8bae37c830042979d776229077d
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Mon Mar 2 14:36:25 2026 -0600

    refactor: CompositeIdentifierToManyToOneBinder
---
 .../CompositeIdentifierToManyToOneBinder.java      | 196 ++++++++++-----------
 .../hibernate/GrailsHibernatePersistentEntity.java |   9 +
 ...CompositeIdentifierToManyToOneBinderSpec.groovy |  18 +-
 3 files changed, 111 insertions(+), 112 deletions(-)

diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CompositeIdentifierToManyToOneBinder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CompositeIdentifierToManyToOneBinder.java
index aa485a9740..785403f21d 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CompositeIdentifierToManyToOneBinder.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CompositeIdentifierToManyToOneBinder.java
@@ -20,130 +20,124 @@ package org.grails.orm.hibernate.cfg.domainbinding.binder;
 
 import static 
org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsDomainBinder.UNDERSCORE;
 
+import java.util.Arrays;
 import java.util.List;
-import org.grails.datastore.mapping.model.PersistentEntity;
-import org.grails.datastore.mapping.model.PersistentProperty;
-import org.grails.datastore.mapping.model.types.ToOne;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
 import org.grails.orm.hibernate.cfg.ColumnConfig;
 import org.grails.orm.hibernate.cfg.CompositeIdentity;
 import org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy;
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity;
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToOneProperty;
 import org.grails.orm.hibernate.cfg.domainbinding.util.BackticksRemover;
 import 
org.grails.orm.hibernate.cfg.domainbinding.util.DefaultColumnNameFetcher;
 import 
org.grails.orm.hibernate.cfg.domainbinding.util.ForeignKeyColumnCountCalculator;
+
 import org.hibernate.boot.spi.MetadataBuildingContext;
 import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
 import org.hibernate.mapping.SimpleValue;
 
 @SuppressWarnings("PMD.DataflowAnomalyAnalysis")
 public class CompositeIdentifierToManyToOneBinder {
-  private final ForeignKeyColumnCountCalculator 
foreignKeyColumnCountCalculator;
-  private final PersistentEntityNamingStrategy namingStrategy;
-  private final DefaultColumnNameFetcher defaultColumnNameFetcher;
-  private final BackticksRemover backticksRemover;
-  private final SimpleValueBinder simpleValueBinder;
 
-  public CompositeIdentifierToManyToOneBinder(
-      ForeignKeyColumnCountCalculator foreignKeyColumnCountCalculator,
-      PersistentEntityNamingStrategy namingStrategy,
-      DefaultColumnNameFetcher defaultColumnNameFetcher,
-      BackticksRemover backticksRemover,
-      SimpleValueBinder simpleValueBinder) {
-    this.foreignKeyColumnCountCalculator = foreignKeyColumnCountCalculator;
-    this.namingStrategy = namingStrategy;
-    this.defaultColumnNameFetcher = defaultColumnNameFetcher;
-    this.backticksRemover = backticksRemover;
-    this.simpleValueBinder = simpleValueBinder;
-  }
+    private final ForeignKeyColumnCountCalculator 
foreignKeyColumnCountCalculator;
+    private final PersistentEntityNamingStrategy namingStrategy;
+    private final DefaultColumnNameFetcher defaultColumnNameFetcher;
+    private final BackticksRemover backticksRemover;
+    private final SimpleValueBinder simpleValueBinder;
 
-  public CompositeIdentifierToManyToOneBinder(
-      MetadataBuildingContext metadataBuildingContext,
-      PersistentEntityNamingStrategy namingStrategy,
-      JdbcEnvironment jdbcEnvironment) {
-    this(
-        new ForeignKeyColumnCountCalculator(),
-        namingStrategy,
-        new DefaultColumnNameFetcher(namingStrategy),
-        new BackticksRemover(),
-        new SimpleValueBinder(metadataBuildingContext, namingStrategy, 
jdbcEnvironment));
-  }
+    public CompositeIdentifierToManyToOneBinder(
+            ForeignKeyColumnCountCalculator foreignKeyColumnCountCalculator,
+            PersistentEntityNamingStrategy namingStrategy,
+            DefaultColumnNameFetcher defaultColumnNameFetcher,
+            BackticksRemover backticksRemover,
+            SimpleValueBinder simpleValueBinder) {
+        this.foreignKeyColumnCountCalculator = foreignKeyColumnCountCalculator;
+        this.namingStrategy = namingStrategy;
+        this.defaultColumnNameFetcher = defaultColumnNameFetcher;
+        this.backticksRemover = backticksRemover;
+        this.simpleValueBinder = simpleValueBinder;
+    }
 
-  public void bindCompositeIdentifierToManyToOne(
-      HibernatePersistentProperty property,
-      SimpleValue value,
-      CompositeIdentity compositeId,
-      PersistentEntity refDomainClass,
-      String path) {
-    String[] propertyNames = compositeId.getPropertyNames();
+    public CompositeIdentifierToManyToOneBinder(
+            MetadataBuildingContext metadataBuildingContext,
+            PersistentEntityNamingStrategy namingStrategy,
+            JdbcEnvironment jdbcEnvironment) {
+        this(
+                new ForeignKeyColumnCountCalculator(),
+                namingStrategy,
+                new DefaultColumnNameFetcher(namingStrategy),
+                new BackticksRemover(),
+                new SimpleValueBinder(metadataBuildingContext, namingStrategy, 
jdbcEnvironment));
+    }
 
-    List<ColumnConfig> columns = property.getMappedForm().getColumns();
-    int i = columns.size();
-    int expectedForeignKeyColumnLength =
-        foreignKeyColumnCountCalculator.calculateForeignKeyColumnCount(
-            refDomainClass, propertyNames);
-    if (i != expectedForeignKeyColumnLength) {
-      int j = 0;
-      for (String propertyName : propertyNames) {
-        final ColumnConfig cc;
-        // if a column configuration exists in the mapping use it
-        if (j < i) {
-          cc = columns.get(j++);
+    public void bindCompositeIdentifierToManyToOne(
+            HibernatePersistentProperty property,
+            SimpleValue value,
+            CompositeIdentity compositeId,
+            GrailsHibernatePersistentEntity refDomainClass,
+            String path) {
+        String[] propertyNames = compositeId.getPropertyNames();
+        List<ColumnConfig> columns = property.getMappedForm().getColumns();
+        int existingCount = columns.size();
+        if (existingCount != 
foreignKeyColumnCountCalculator.calculateForeignKeyColumnCount(refDomainClass, 
propertyNames)) {
+            String prefix = refDomainClass.getTableName(namingStrategy);
+            IntStream.range(0, propertyNames.length)
+                    .boxed()
+                    .flatMap(idx -> {
+                        ColumnConfig cc = idx < existingCount ? 
columns.get(idx) : new ColumnConfig();
+                        if (cc.getName() != null) {
+                            return Stream.empty();
+                        }
+                        String propertyName = propertyNames[idx];
+                        HibernatePersistentProperty ref = 
refDomainClass.getHibernatePropertyByName(propertyName);
+                        return tryExpandNestedComposite(prefix, propertyName, 
ref)
+                                .orElseGet(() -> singleColumn(prefix, 
propertyName, ref, cc));
+                    })
+                    .forEach(columns::add);
         }
-        // otherwise create a new one to represent the composite column
-        else {
-          cc = new ColumnConfig();
+        simpleValueBinder.bindSimpleValue(property, null, value, path);
+    }
+
+    /**
+     * If {@code ref} is a to-one whose associated entity has a composite 
identity, returns a stream
+     * of one named {@link ColumnConfig} per composite-identity property. 
Returns empty otherwise.
+     */
+    private Optional<Stream<ColumnConfig>> tryExpandNestedComposite(
+            String prefix, String propertyName, HibernatePersistentProperty 
ref) {
+        if (!(ref instanceof HibernateToOneProperty toOne)) {
+            return Optional.empty();
         }
-        // if the name is null then configure the name by convention
-        if (cc.getName() == null) {
-          // use the referenced table name as a prefix
-          String prefix =
-              refDomainClass instanceof GrailsHibernatePersistentEntity ghpe
-                  ? ghpe.getTableName(namingStrategy)
-                  : refDomainClass.getName();
-          PersistentProperty referencedProperty = 
refDomainClass.getPropertyByName(propertyName);
+        HibernatePersistentProperty[] nestedComposite =
+                toOne.getHibernateAssociatedEntity().getCompositeIdentity();
+        if (nestedComposite == null) {
+            return Optional.empty();
+        }
+        return Optional.of(Arrays.stream(nestedComposite)
+                .map(cip -> namedColumn(join(prefix, 
namingStrategy.resolveColumnName(propertyName),
+                        defaultColumnNameFetcher.getDefaultColumnName(cip)))));
+    }
 
-          // if the referenced property is a ToOne and it has a composite id
-          // then a column is needed for each property that forms the 
composite id
-          if (referencedProperty instanceof ToOne toOne) {
-            PersistentProperty[] compositeIdentity =
-                toOne.getAssociatedEntity().getCompositeIdentity();
-            if (compositeIdentity != null) {
-              for (PersistentProperty cip : compositeIdentity) {
-                // for each property of a composite id by default we use the 
table name and the
-                // property name as a prefix
-                String string = 
namingStrategy.resolveColumnName(referencedProperty.getName());
-                String compositeIdPrefix =
-                    backticksRemover.apply(prefix) + UNDERSCORE + 
backticksRemover.apply(string);
+    private Stream<ColumnConfig> singleColumn(
+            String prefix, String propertyName, HibernatePersistentProperty 
ref, ColumnConfig cc) {
+        String suffix = ref != null
+                ? defaultColumnNameFetcher.getDefaultColumnName(ref)
+                : propertyName;
+        cc.setName(join(prefix, suffix));
+        return Stream.of(cc);
+    }
 
-                String suffix =
-                    cip instanceof HibernatePersistentProperty ghpp
-                        ? defaultColumnNameFetcher.getDefaultColumnName(ghpp)
-                        : cip.getName();
-                String finalColumnName =
-                    backticksRemover.apply(compositeIdPrefix)
-                        + UNDERSCORE
-                        + backticksRemover.apply(suffix);
-                ColumnConfig newCc = new ColumnConfig();
-                newCc.setName(finalColumnName);
-                columns.add(newCc);
-              }
-              continue;
-            }
-          }
+    private ColumnConfig namedColumn(String name) {
+        ColumnConfig cc = new ColumnConfig();
+        cc.setName(name);
+        return cc;
+    }
 
-          String suffix =
-              referencedProperty instanceof HibernatePersistentProperty ghpp
-                  ? defaultColumnNameFetcher.getDefaultColumnName(ghpp)
-                  : referencedProperty.getName();
-          String finalColumnName =
-              backticksRemover.apply(prefix) + UNDERSCORE + 
backticksRemover.apply(suffix);
-          cc.setName(finalColumnName);
-          columns.add(cc);
-        }
-      }
+    private String join(String... parts) {
+        return 
Arrays.stream(parts).map(backticksRemover::apply).collect(Collectors.joining(String.valueOf(UNDERSCORE)));
     }
-    // set type
-    simpleValueBinder.bindSimpleValue(property, null, value, path);
-  }
 }
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/GrailsHibernatePersistentEntity.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/GrailsHibernatePersistentEntity.java
index 20ac8e2c7b..0337ab1acb 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/GrailsHibernatePersistentEntity.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/GrailsHibernatePersistentEntity.java
@@ -156,6 +156,15 @@ public interface GrailsHibernatePersistentEntity extends 
PersistentEntity {
   @Override
   HibernatePersistentProperty getVersion();
 
+  /**
+   * Returns the persistent property with the given name cast to {@link 
HibernatePersistentProperty},
+   * or {@code null} if no such property exists.
+   */
+  default HibernatePersistentProperty getHibernatePropertyByName(String name) {
+    var property = getPropertyByName(name);
+    return property instanceof HibernatePersistentProperty hpp ? hpp : null;
+  }
+
   /**
    * @param parentType The type of the parent entity
    * @return The parent property if it exists
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CompositeIdentifierToManyToOneBinderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CompositeIdentifierToManyToOneBinderSpec.groovy
index ca337b414b..71cc4ed150 100644
--- 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CompositeIdentifierToManyToOneBinderSpec.groovy
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CompositeIdentifierToManyToOneBinderSpec.groovy
@@ -1,8 +1,7 @@
 package org.grails.orm.hibernate.cfg.domainbinding
 
 
-import org.grails.datastore.mapping.model.PersistentProperty
-import org.grails.datastore.mapping.model.types.ToOne
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToOneProperty
 import org.grails.orm.hibernate.cfg.ColumnConfig
 import org.grails.orm.hibernate.cfg.CompositeIdentity
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity
@@ -34,8 +33,7 @@ class CompositeIdentifierToManyToOneBinderSpec extends 
Specification {
         def binder = new CompositeIdentifierToManyToOneBinder(calculator, 
namingStrategy, columnNameFetcher, backticksRemover, simpleValueBinder)
 
         // 2. Set up stubs for the method arguments
-        def association = Mock(ToOne)
-        association.asType(HibernatePersistentProperty) >> association
+        def association = Mock(HibernatePersistentProperty)
         def value = Mock(SimpleValue)
         def refDomainClass = Mock(GrailsHibernatePersistentEntity)
         def path = "/test"
@@ -51,17 +49,16 @@ class CompositeIdentifierToManyToOneBinderSpec extends 
Specification {
 
         calculator.calculateForeignKeyColumnCount(refDomainClass, 
propertyNames) >> 2
 
-        def nestedEntityProp = Mock(ToOne)
-        nestedEntityProp.asType(HibernatePersistentProperty) >> 
nestedEntityProp
-        refDomainClass.getPropertyByName("nestedEntity") >> nestedEntityProp
+        def nestedEntityProp = Mock(HibernateToOneProperty)
+        refDomainClass.getHibernatePropertyByName("nestedEntity") >> 
nestedEntityProp
         nestedEntityProp.name >> "nestedEntity"
 
         def nestedAssociatedEntity = Mock(GrailsHibernatePersistentEntity)
-        nestedEntityProp.getAssociatedEntity() >> nestedAssociatedEntity
+        nestedEntityProp.getHibernateAssociatedEntity() >> 
nestedAssociatedEntity
 
         def nestedPartA = Mock(HibernatePersistentProperty)
         def nestedPartB = Mock(HibernatePersistentProperty)
-        def perArray = [nestedPartA, nestedPartB] as PersistentProperty[]
+        def perArray = [nestedPartA, nestedPartB] as 
HibernatePersistentProperty[]
         nestedAssociatedEntity.getCompositeIdentity() >> perArray
 
         // 4. Mock the behavior of the dependency methods
@@ -100,8 +97,7 @@ class CompositeIdentifierToManyToOneBinderSpec extends 
Specification {
         def binder = new CompositeIdentifierToManyToOneBinder(calculator, 
namingStrategy, columnNameFetcher, backticksRemover, simpleValueBinder)
 
         // 2. Set up arguments
-        def association = Mock(ToOne)
-        association.asType(HibernatePersistentProperty) >> association
+        def association = Mock(HibernatePersistentProperty)
         def value = Mock(SimpleValue)
         def compositeId = new CompositeIdentity()
         compositeId.setPropertyNames(["prop1", "prop2"] as String[])

Reply via email to