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 d35d831b72a0c4d8f3c359e2e99b98b7655905c1
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Mon Mar 2 08:26:15 2026 -0600

    Refactor CollectionSecondPassBinder and ManyToOneBinder for typed dispatch
    
    Extract bindCollectionSecondPass into focused private methods:
    - bindOneToManyAssociation: handles OneToMany collection setup
    - applyMultiTenantFilter: applies tenant filter condition
    - bindCollectionKey: handles bidirectional/unidirectional key binding
    - bindCollectionElement: dispatches element binding by property type
    - bindManyToManyElement: typed ManyToMany element binding
    - bindBidirectionalMapElement: typed bidirectional map element binding
    
    Replace ManyToOneBinder.bindManyToOne(HibernateAssociation) with
    two typed overloads that eliminate instanceof guards:
    - bindManyToOne(HibernateToOneProperty): handles direct to-one
      associations; extracts bindOneToOneUniqueKey for the OneToOne case
    - bindManyToOne(HibernateManyToManyProperty): handles inverse side
      of ManyToMany; extracts prepareCircularManyToMany
    
    Add HibernateManyToManyProperty.getHibernateInverseSide() covariant
    override returning HibernateManyToManyProperty, enabling 
CollectionSecondPassBinder
    to call the typed overload without a cast.
    
    Update ManyToOneBinderSpec to call typed overloads directly
    (removing 'as HibernateAssociation' casts).
    
    Co-authored-by: Copilot <[email protected]>
---
 .../cfg/domainbinding/binder/ManyToOneBinder.java  | 114 ++++++++++++---------
 .../hibernate/HibernateManyToManyProperty.java     |   5 +
 .../secondpass/CollectionSecondPassBinder.java     | 113 +++++++++++++-------
 .../cfg/domainbinding/ManyToOneBinderSpec.groovy   |  13 ++-
 4 files changed, 152 insertions(+), 93 deletions(-)

diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ManyToOneBinder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ManyToOneBinder.java
index f24160a3a3..f0cde199da 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ManyToOneBinder.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ManyToOneBinder.java
@@ -77,66 +77,82 @@ public class ManyToOneBinder {
         new SimpleValueColumnFetcher());
   }
 
-  /** Binds a many-to-one relationship to the */
-  @SuppressWarnings("unchecked")
+  /** Binds a to-one (many-to-one or one-to-one) association. */
+  public ManyToOne bindManyToOne(
+      HibernateToOneProperty property, org.hibernate.mapping.Table table, 
String path) {
+    GrailsHibernatePersistentEntity refDomainClass = 
property.getHibernateAssociatedEntity();
+    boolean isComposite = isCompositeIdentifier(refDomainClass);
+    ManyToOne manyToOne = doBind(property, refDomainClass, isComposite, table, 
path);
+    if (property instanceof HibernateOneToOneProperty oneToOne && 
!isComposite) {
+      bindOneToOneUniqueKey(oneToOne, manyToOne);
+    }
+    return manyToOne;
+  }
+
+  /** Binds the inverse side of a many-to-many association as a collection 
element. */
   public ManyToOne bindManyToOne(
-      HibernateAssociation property, org.hibernate.mapping.Table table, String 
path) {
+      HibernateManyToManyProperty property, org.hibernate.mapping.Table table, 
String path) {
+    GrailsHibernatePersistentEntity refDomainClass = 
property.getHibernateOwner();
+    boolean isComposite = isCompositeIdentifier(refDomainClass);
+    if (!isComposite && property.isCircular()) {
+      prepareCircularManyToMany(property, refDomainClass.getMappedForm());
+    }
+    return doBind(property, refDomainClass, isComposite, table, path);
+  }
+
+  private static boolean isCompositeIdentifier(GrailsHibernatePersistentEntity 
entity) {
+    Mapping mapping = entity.getMappedForm();
+    return mapping != null && mapping.hasCompositeIdentifier();
+  }
+
+  @SuppressWarnings("unchecked")
+  private ManyToOne doBind(
+      HibernateAssociation property,
+      GrailsHibernatePersistentEntity refDomainClass,
+      boolean isComposite,
+      org.hibernate.mapping.Table table,
+      String path) {
     ManyToOne manyToOne = new ManyToOne(metadataBuildingContext, table);
     manyToOneValuesBinder.bindManyToOneValues(property, manyToOne);
-    GrailsHibernatePersistentEntity refDomainClass =
-        (property instanceof HibernateManyToManyProperty
-            ? property.getHibernateOwner()
-            : property.getHibernateAssociatedEntity());
-    Mapping mapping = refDomainClass.getMappedForm();
-
-    boolean isComposite = mapping != null && mapping.hasCompositeIdentifier();
     if (isComposite) {
+      Mapping mapping = refDomainClass.getMappedForm();
       CompositeIdentity ci = (CompositeIdentity) mapping.getIdentity();
       compositeIdentifierToManyToOneBinder.bindCompositeIdentifierToManyToOne(
           property, manyToOne, ci, refDomainClass, path);
     } else {
-      if (property.isCircular() && (property instanceof 
HibernateManyToManyProperty)) {
-        PropertyConfig pc = property.getMappedForm();
-
-        if (mapping != null && pc.getColumns().isEmpty()) {
-          mapping.getColumns().put(property.getName(), pc);
-        }
-        if (!pc.hasJoinKeyMapping()) {
-          JoinTable jt = new JoinTable();
-          final ColumnConfig columnConfig = new ColumnConfig();
-          columnConfig.setName(
-              namingStrategy.resolveColumnName(property.getName()) + 
FOREIGN_KEY_SUFFIX);
-          jt.setKey(columnConfig);
-          pc.setJoinTable(jt);
-        }
-        // set type
-        simpleValueBinder.bindSimpleValue(property, null, manyToOne, path);
-      } else {
-        // bind column
-        // set type
-        simpleValueBinder.bindSimpleValue(property, null, manyToOne, path);
-      }
+      simpleValueBinder.bindSimpleValue(property, null, manyToOne, path);
     }
+    return manyToOne;
+  }
 
+  private void bindOneToOneUniqueKey(HibernateOneToOneProperty property, 
ManyToOne manyToOne) {
     PropertyConfig config = property.getMappedForm();
-    boolean isOneToOne = property instanceof HibernateOneToOneProperty;
-    boolean notComposite = !isComposite;
-    if (isOneToOne && notComposite) {
-      manyToOne.setAlternateUniqueKey(true);
-      Column c = simpleValueColumnFetcher.getColumnForSimpleValue(manyToOne);
-      if (c == null) {
-        throw new MappingException("There is no column for property [" + 
property.getName() + "]");
-      }
-      if (!config.isUniqueWithinGroup()) {
-        c.setUnique(config.isUnique());
-      } else {
-        if (property.isBidirectional()
-            && property.getHibernateInverseSide() instanceof 
HibernateToOneProperty inverseSide
-            && inverseSide.isHibernateOneToOne()) {
-          c.setUnique(true);
-        }
-      }
+    manyToOne.setAlternateUniqueKey(true);
+    Column c = simpleValueColumnFetcher.getColumnForSimpleValue(manyToOne);
+    if (c == null) {
+      throw new MappingException("There is no column for property [" + 
property.getName() + "]");
+    }
+    if (!config.isUniqueWithinGroup()) {
+      c.setUnique(config.isUnique());
+    } else if (property.isBidirectional()
+        && property.getHibernateInverseSide() instanceof 
HibernateToOneProperty inverseSide
+        && inverseSide.isHibernateOneToOne()) {
+      c.setUnique(true);
+    }
+  }
+
+  private void prepareCircularManyToMany(HibernateManyToManyProperty property, 
Mapping mapping) {
+    PropertyConfig pc = property.getMappedForm();
+    if (mapping != null && pc.getColumns().isEmpty()) {
+      mapping.getColumns().put(property.getName(), pc);
+    }
+    if (!pc.hasJoinKeyMapping()) {
+      JoinTable jt = new JoinTable();
+      ColumnConfig columnConfig = new ColumnConfig();
+      columnConfig.setName(
+          namingStrategy.resolveColumnName(property.getName()) + 
FOREIGN_KEY_SUFFIX);
+      jt.setKey(columnConfig);
+      pc.setJoinTable(jt);
     }
-    return manyToOne;
   }
 }
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateManyToManyProperty.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateManyToManyProperty.java
index 947ce10fd7..d5d57d6e54 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateManyToManyProperty.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateManyToManyProperty.java
@@ -36,4 +36,9 @@ public class HibernateManyToManyProperty extends 
ManyToManyWithMapping<PropertyC
   public GrailsHibernatePersistentEntity getHibernateAssociatedEntity() {
     return (GrailsHibernatePersistentEntity) super.getAssociatedEntity();
   }
+
+  @Override
+  public HibernateManyToManyProperty getHibernateInverseSide() {
+    return (HibernateManyToManyProperty) getInverseSide();
+  }
 }
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java
index 2d0f524947..cb3ce556f7 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java
@@ -33,6 +33,7 @@ import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToManyP
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateOneToManyProperty;
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty;
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToOneProperty;
 import 
org.grails.orm.hibernate.cfg.domainbinding.util.DefaultColumnNameFetcher;
 import org.grails.orm.hibernate.cfg.domainbinding.util.OrderByClauseBuilder;
 import org.hibernate.MappingException;
@@ -93,16 +94,41 @@ public class CollectionSecondPassBinder {
       @Nonnull Collection collection) {
 
     PersistentClass associatedClass = bindOrderBy(property, collection, 
persistentClasses);
+    bindOneToManyAssociation(property, associatedClass, collection);
 
-    if (collection.isOneToMany()) {
-      OneToMany oneToMany = (OneToMany) collection.getElement();
-      oneToMany.setAssociatedClass(associatedClass);
-      if (property.shouldBindWithForeignKey()) {
-        collection.setCollectionTable(associatedClass.getTable());
-      }
-      
collectionForPropertyConfigBinder.bindCollectionForPropertyConfig(collection, 
property);
+    applyMultiTenantFilter(property, collection);
+
+    if (property.isSorted()) {
+      collection.setSorted(true);
+    }
+
+    DependantValue key = 
primaryKeyValueCreator.createPrimaryKeyValue(collection);
+    collection.setKey(key);
+    bindCollectionKey(property, key, associatedClass, collection);
+
+    Optional.ofNullable(property.getMappedForm().getCache())
+        .ifPresent(cacheConfig -> 
collection.setCacheConcurrencyStrategy(cacheConfig.getUsage()));
+
+    bindCollectionElement(property, mappings, collection);
+    collectionKeyColumnUpdater.forceNullableAndCheckUpdatable(key, property);
+  }
+
+  private void bindOneToManyAssociation(
+      HibernateToManyProperty property,
+      PersistentClass associatedClass,
+      Collection collection) {
+    if (!collection.isOneToMany()) {
+      return;
+    }
+    OneToMany oneToMany = (OneToMany) collection.getElement();
+    oneToMany.setAssociatedClass(associatedClass);
+    if (property.shouldBindWithForeignKey()) {
+      collection.setCollectionTable(associatedClass.getTable());
     }
+    
collectionForPropertyConfigBinder.bindCollectionForPropertyConfig(collection, 
property);
+  }
 
+  private void applyMultiTenantFilter(HibernateToManyProperty property, 
Collection collection) {
     Optional.ofNullable(property.getHibernateAssociatedEntity())
         .filter(
             referenced ->
@@ -126,21 +152,19 @@ public class CollectionSecondPassBinder {
                     Collections.emptyMap());
               }
             });
+  }
 
-    if (property.isSorted()) {
-      collection.setSorted(true);
-    }
-
-    DependantValue key = 
primaryKeyValueCreator.createPrimaryKeyValue(collection);
-    collection.setKey(key);
-
+  private void bindCollectionKey(
+      HibernateToManyProperty property,
+      DependantValue key,
+      PersistentClass associatedClass,
+      Collection collection) {
     if (property.isBidirectional()) {
-      if (property.getHibernateInverseSide()
-              instanceof org.grails.datastore.mapping.model.types.ToOne
+      var inverseSide = property.getHibernateInverseSide();
+      if (inverseSide instanceof org.grails.datastore.mapping.model.types.ToOne
           && property.shouldBindWithForeignKey()) {
-        bidirectionalOneToManyLinker.link(
-            collection, associatedClass, key, 
property.getHibernateInverseSide());
-      } else if (property.getHibernateInverseSide() instanceof 
HibernateManyToManyProperty
+        bidirectionalOneToManyLinker.link(collection, associatedClass, key, 
inverseSide);
+      } else if (inverseSide instanceof HibernateManyToManyProperty
           || java.util.Map.class.isAssignableFrom(property.getType())) {
         dependentKeyValueBinder.bind(property, key);
       }
@@ -152,29 +176,44 @@ public class CollectionSecondPassBinder {
         dependentKeyValueBinder.bind(property, key);
       }
     }
+  }
 
-    Optional.ofNullable(property.getMappedForm().getCache())
-        .ifPresent(cacheConfig -> 
collection.setCacheConcurrencyStrategy(cacheConfig.getUsage()));
-
-    if (property instanceof HibernateManyToManyProperty || 
property.isBidirectionalOneToManyMap()) {
-      if (property.isBidirectional()) {
-        var otherSide = property.getHibernateInverseSide();
-        ManyToOne element =
-            manyToOneBinder.bindManyToOne(otherSide, 
collection.getCollectionTable(), EMPTY_PATH);
-        element.setReferencedEntityName(otherSide.getOwner().getName());
-        collection.setElement(element);
-        
collectionForPropertyConfigBinder.bindCollectionForPropertyConfig(collection, 
property);
-        if (property.isCircular()) {
-          collection.setInverse(false);
-        }
-      }
+  private void bindCollectionElement(
+      HibernateToManyProperty property,
+      InFlightMetadataCollector mappings,
+      Collection collection) {
+    if (property instanceof HibernateManyToManyProperty manyToMany && 
manyToMany.isBidirectional()) {
+      bindManyToManyElement(manyToMany, collection);
+    } else if (property.isBidirectionalOneToManyMap() && 
property.isBidirectional()) {
+      bindBidirectionalMapElement(property, collection);
     } else if (property.isUnidirectionalOneToMany()) {
-      unidirectionalOneToManyBinder.bind(
-          (HibernateOneToManyProperty) property, collection);
+      unidirectionalOneToManyBinder.bind((HibernateOneToManyProperty) 
property, collection);
     } else if (property.supportsJoinColumnMapping()) {
       collectionWithJoinTableBinder.bindCollectionWithJoinTable(property, 
mappings, collection);
     }
-    collectionKeyColumnUpdater.forceNullableAndCheckUpdatable(key, property);
+  }
+
+  private void bindManyToManyElement(
+      HibernateManyToManyProperty manyToMany, Collection collection) {
+    HibernateManyToManyProperty otherSide = 
manyToMany.getHibernateInverseSide();
+    ManyToOne element =
+        manyToOneBinder.bindManyToOne(otherSide, 
collection.getCollectionTable(), EMPTY_PATH);
+    element.setReferencedEntityName(otherSide.getOwner().getName());
+    collection.setElement(element);
+    
collectionForPropertyConfigBinder.bindCollectionForPropertyConfig(collection, 
manyToMany);
+    if (manyToMany.isCircular()) {
+      collection.setInverse(false);
+    }
+  }
+
+  private void bindBidirectionalMapElement(
+      HibernateToManyProperty property, Collection collection) {
+    HibernateToOneProperty otherSide = (HibernateToOneProperty) 
property.getHibernateInverseSide();
+    ManyToOne element =
+        manyToOneBinder.bindManyToOne(otherSide, 
collection.getCollectionTable(), EMPTY_PATH);
+    element.setReferencedEntityName(otherSide.getOwner().getName());
+    collection.setElement(element);
+    
collectionForPropertyConfigBinder.bindCollectionForPropertyConfig(collection, 
property);
   }
 
   PersistentClass bindOrderBy(
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ManyToOneBinderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ManyToOneBinderSpec.groovy
index 7b54f81ad3..789054f2e7 100644
--- 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ManyToOneBinderSpec.groovy
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ManyToOneBinderSpec.groovy
@@ -1,7 +1,6 @@
 package org.grails.orm.hibernate.cfg.domainbinding
 
 import grails.gorm.specs.HibernateGormDatastoreSpec
-import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateAssociation
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToOneProperty
 import org.grails.orm.hibernate.cfg.CompositeIdentity
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity
@@ -49,11 +48,11 @@ class ManyToOneBinderSpec extends 
HibernateGormDatastoreSpec {
         mapping.setIdentity(hasCompositeId ? new CompositeIdentity() : null)
 
         when:
-        def result = binder.bindManyToOne(association as HibernateAssociation, 
null, path)
+        def result = binder.bindManyToOne(association, null, path)
 
         then:
         result instanceof ManyToOne
-        1 * manyToOneValuesBinder.bindManyToOneValues(association as 
HibernateAssociation, _ as ManyToOne)
+        1 * manyToOneValuesBinder.bindManyToOneValues(association, _ as 
ManyToOne)
         compositeBinderCalls * 
compositeBinder.bindCompositeIdentifierToManyToOne(association as 
HibernatePersistentProperty, _ as ManyToOne, _, refDomainClass, path)
         simpleValueBinderCalls * simpleValueBinder.bindSimpleValue(association 
as HibernatePersistentProperty, null, _ as ManyToOne, path)
 
@@ -89,11 +88,11 @@ class ManyToOneBinderSpec extends 
HibernateGormDatastoreSpec {
         namingStrategy.resolveColumnName("myCircularProp") >> 
"my_circular_prop"
 
         when:
-        def result = binder.bindManyToOne(property as HibernateAssociation, 
null, "/test")
+        def result = binder.bindManyToOne(property, null, "/test")
 
         then:
         result instanceof ManyToOne
-        1 * manyToOneValuesBinder.bindManyToOneValues(property as 
HibernateAssociation, _ as ManyToOne)
+        1 * manyToOneValuesBinder.bindManyToOneValues(property, _ as ManyToOne)
         1 * simpleValueBinder.bindSimpleValue(property as 
HibernatePersistentProperty, null, _ as ManyToOne, "/test")
         def resultConfig = mapping.getColumns().get("myCircularProp")
         resultConfig != null
@@ -132,7 +131,7 @@ class ManyToOneBinderSpec extends 
HibernateGormDatastoreSpec {
         inverseSide.isHibernateOneToOne() >> isInverseHasOne
 
         when:
-        def result = binder.bindManyToOne(property as HibernateAssociation, 
null, "/test")
+        def result = binder.bindManyToOne(property, null, "/test")
 
         then:
         result.isAlternateUniqueKey()
@@ -174,7 +173,7 @@ class ManyToOneBinderSpec extends 
HibernateGormDatastoreSpec {
         columnFetcher.getColumnForSimpleValue(_ as ManyToOne) >> null
 
         when:
-        binder.bindManyToOne(property as HibernateAssociation, null, "/test")
+        binder.bindManyToOne(property, null, "/test")
 
         then:
         thrown(MappingException)

Reply via email to