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 ae067982e535d979c2fc73d4c053e505189c265f
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Fri Feb 6 19:30:32 2026 -0600

    progress
---
 .../orm/hibernate/cfg/GrailsDomainBinder.java      |  24 ++---
 .../cfg/domainbinding/CollectionBinder.java        |  82 +-------------
 .../cfg/domainbinding/ListSecondPassBinder.java    | 118 +++++++++++++++++++++
 .../domainbinding/secondpass/ListSecondPass.java   |   5 -
 .../domainbinding/ListSecondPassBinderSpec.groovy  |  83 +++++++++++++++
 5 files changed, 211 insertions(+), 101 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 9e8769918c..67d5275179 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
@@ -48,7 +48,6 @@ import org.hibernate.mapping.Column;
 import org.hibernate.mapping.DependantValue;
 import org.hibernate.mapping.Formula;
 import org.hibernate.mapping.JoinedSubclass;
-import org.hibernate.mapping.OneToMany;
 import org.hibernate.mapping.PersistentClass;
 import org.hibernate.mapping.Property;
 import org.hibernate.mapping.RootClass;
@@ -305,7 +304,7 @@ public class GrailsDomainBinder
             children.forEach(sub -> bindSubClass(sub, root, mappings, 
sessionFactoryBeanName, finalMapping,mappingCacheHolder ));
         }
 
-        addMultiTenantFilterIfNecessary(entity, root, mappings, 
sessionFactoryBeanName);
+        addMultiTenantFilterIfNecessary(entity, root, mappings);
 
         mappings.addEntityBinding(root);
     }
@@ -320,14 +319,13 @@ public class GrailsDomainBinder
     /**
      * Add a Hibernate filter for multitenancy if the persistent class is 
multitenant
      *
-     * @param entity target persistent entity for get tenant information
+     * @param entity          target persistent entity for get tenant 
information
      * @param persistentClass persistent class for add the filter and get 
tenant property info
-     * @param mappings mappings to add the filter
-     * @param sessionFactoryBeanName the session factory bean name
+     * @param mappings        mappings to add the filter
      */
     private void addMultiTenantFilterIfNecessary(
             @Nonnull GrailsHibernatePersistentEntity entity, PersistentClass 
persistentClass,
-            @Nonnull InFlightMetadataCollector mappings, String 
sessionFactoryBeanName) {
+            @Nonnull InFlightMetadataCollector mappings) {
 
         if (entity.isMultiTenant()) {
             TenantId tenantId = entity.getTenantId();
@@ -380,7 +378,7 @@ public class GrailsDomainBinder
         parent.addSubclass(subClass);
         mappings.addEntityBinding(subClass);
 
-        addMultiTenantFilterIfNecessary(sub, subClass, mappings, 
sessionFactoryBeanName);
+        addMultiTenantFilterIfNecessary(sub, subClass, mappings);
 
         var children = sub.getChildEntities(dataSourceName);
         if (!children.isEmpty()) {
@@ -674,10 +672,7 @@ public class GrailsDomainBinder
         new 
NaturalIdentifierBinder().bindNaturalIdentifier(domainClass.getMappedForm(), 
persistentClass);
     }
 
-    private void 
bindOneToMany(org.grails.datastore.mapping.model.types.OneToMany 
currentGrailsProp, OneToMany one, @Nonnull InFlightMetadataCollector mappings) {
-        
one.setReferencedEntityName(currentGrailsProp.getAssociatedEntity().getName());
-        one.setIgnoreNotFound(true);
-    }
+
 
     public MetadataBuildingContext getMetadataBuildingContext() {
         return metadataBuildingContext;
@@ -691,13 +686,6 @@ public class GrailsDomainBinder
         return collectionHolder;
     }
 
-    public EnumTypeBinder getEnumTypeBinder() {
-        return enumTypeBinder;
-    }
-
-    public ComponentPropertyBinder getComponentPropertyBinder() {
-        return componentPropertyBinder;
-    }
 
     public PropertyFromValueCreator getPropertyFromValueCreator() {
         return propertyFromValueCreator;
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CollectionBinder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CollectionBinder.java
index c969a7f25b..5641a71411 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CollectionBinder.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CollectionBinder.java
@@ -36,7 +36,6 @@ import org.hibernate.mapping.Collection;
 import org.hibernate.mapping.Column;
 import org.hibernate.mapping.Component;
 import org.hibernate.mapping.DependantValue;
-import org.hibernate.mapping.IndexBackref;
 import org.hibernate.mapping.IndexedCollection;
 import org.hibernate.mapping.KeyValue;
 import org.hibernate.mapping.ManyToOne;
@@ -64,7 +63,6 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.StringTokenizer;
 
-import static java.util.Optional.ofNullable;
 import static org.grails.orm.hibernate.cfg.GrailsDomainBinder.*;
 
 /**
@@ -77,11 +75,13 @@ public class CollectionBinder {
     private final MetadataBuildingContext metadataBuildingContext;
     private final GrailsDomainBinder grailsDomainBinder;
     private final PersistentEntityNamingStrategy namingStrategy;
+    private final ListSecondPassBinder listSecondPassBinder;
 
     public CollectionBinder(MetadataBuildingContext metadataBuildingContext, 
GrailsDomainBinder grailsDomainBinder, PersistentEntityNamingStrategy 
namingStrategy) {
         this.metadataBuildingContext = metadataBuildingContext;
         this.grailsDomainBinder = grailsDomainBinder;
         this.namingStrategy = namingStrategy;
+        this.listSecondPassBinder = new 
ListSecondPassBinder(metadataBuildingContext, this);
     }
 
     /**
@@ -316,81 +316,7 @@ public class CollectionBinder {
 
     public void bindListSecondPass(HibernateToManyProperty property, @Nonnull 
InFlightMetadataCollector mappings,
                                    Map<?, ?> persistentClasses, 
org.hibernate.mapping.List list, String sessionFactoryBeanName) {
-
-        bindCollectionSecondPass(property, mappings, persistentClasses, list, 
sessionFactoryBeanName);
-
-        String columnName = getIndexColumnName(property);
-        final boolean isManyToMany = property instanceof ManyToMany;
-
-        if (isManyToMany && !property.isOwningSide()) {
-            throw new MappingException("Invalid association [" + property +
-                    "]. List collection types only supported on the owning 
side of a many-to-many relationship.");
-        }
-
-        Table collectionTable = list.getCollectionTable();
-        SimpleValue iv = new BasicValue(metadataBuildingContext, 
collectionTable);
-        String type = ((GrailsHibernatePersistentProperty) 
property).getIndexColumnType("integer");
-        new SimpleValueColumnBinder().bindSimpleValue(iv, type, columnName, 
true);
-        iv.setTypeName(type);
-        list.setIndex(iv);
-        list.setBaseIndex(0);
-        list.setInverse(false);
-
-        Value v = list.getElement();
-        v.createForeignKey();
-
-        if (property.isBidirectional()) {
-
-            String entityName;
-            Value element = list.getElement();
-            if (element instanceof ManyToOne) {
-                ManyToOne manyToOne = (ManyToOne) element;
-                entityName = manyToOne.getReferencedEntityName();
-            } else {
-                entityName = ((OneToMany) element).getReferencedEntityName();
-            }
-
-            PersistentClass referenced = mappings.getEntityBinding(entityName);
-
-            boolean compositeIdProperty = 
property.getInverseSide().isCompositeIdProperty();
-            if (!compositeIdProperty) {
-                Backref prop = new Backref();
-                final PersistentEntity owner = property.getOwner();
-                prop.setEntityName(owner.getName());
-                String s2 = property.getName();
-                prop.setName(UNDERSCORE + new 
BackticksRemover().apply(owner.getJavaClass().getSimpleName()) + UNDERSCORE + 
new BackticksRemover().apply(s2) + "Backref");
-                prop.setSelectable(false);
-                prop.setUpdatable(false);
-                if (isManyToMany) {
-                    prop.setInsertable(false);
-                }
-                prop.setCollectionRole(list.getRole());
-                prop.setValue(list.getKey());
-
-                DependantValue value = (DependantValue) prop.getValue();
-                if (!property.isCircular()) {
-                    value.setNullable(false);
-                }
-                value.setUpdateable(true);
-                prop.setOptional(false);
-
-                referenced.addProperty(prop);
-            }
-
-            if ((!list.getKey().isNullable() && !list.isInverse()) || 
compositeIdProperty) {
-                IndexBackref ib = new IndexBackref();
-                ib.setName(UNDERSCORE + property.getName() + "IndexBackref");
-                ib.setUpdatable(false);
-                ib.setSelectable(false);
-                if (isManyToMany) {
-                    ib.setInsertable(false);
-                }
-                ib.setCollectionRole(list.getRole());
-                ib.setEntityName(list.getOwner().getEntityName());
-                ib.setValue(list.getIndex());
-                referenced.addProperty(ib);
-            }
-        }
+        listSecondPassBinder.bindListSecondPass(property, mappings, 
persistentClasses, list, sessionFactoryBeanName);
     }
 
     public void bindMapSecondPass(HibernateToManyProperty property, @Nonnull 
InFlightMetadataCollector mappings,
@@ -501,7 +427,7 @@ public class CollectionBinder {
                 tableName, null, false, metadataBuildingContext));
     }
 
-    private String getIndexColumnName(PersistentProperty property) {
+    public String getIndexColumnName(PersistentProperty property) {
         PropertyConfig pc = property instanceof 
GrailsHibernatePersistentProperty ghpp ? ghpp.getMappedForm() : new 
PropertyConfig();
         if (pc.getIndexColumn() != null && pc.getIndexColumn().getColumn() != 
null) {
             return pc.getIndexColumn().getColumn();
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ListSecondPassBinder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ListSecondPassBinder.java
new file mode 100644
index 0000000000..08627e1410
--- /dev/null
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ListSecondPassBinder.java
@@ -0,0 +1,118 @@
+package org.grails.orm.hibernate.cfg.domainbinding;
+
+import jakarta.annotation.Nonnull;
+import org.grails.datastore.mapping.model.PersistentEntity;
+import org.grails.datastore.mapping.model.types.ManyToMany;
+import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentProperty;
+import org.grails.orm.hibernate.cfg.HibernateToManyProperty;
+import org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy;
+import org.hibernate.MappingException;
+import org.hibernate.boot.spi.InFlightMetadataCollector;
+import org.hibernate.boot.spi.MetadataBuildingContext;
+import org.hibernate.mapping.Backref;
+import org.hibernate.mapping.BasicValue;
+import org.hibernate.mapping.DependantValue;
+import org.hibernate.mapping.IndexBackref;
+import org.hibernate.mapping.ManyToOne;
+import org.hibernate.mapping.OneToMany;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.SimpleValue;
+import org.hibernate.mapping.Table;
+import org.hibernate.mapping.Value;
+
+import java.util.Map;
+
+import static org.grails.orm.hibernate.cfg.GrailsDomainBinder.UNDERSCORE;
+
+/**
+ * Refactored from CollectionBinder to handle list second pass binding.
+ */
+public class ListSecondPassBinder {
+
+    private final MetadataBuildingContext metadataBuildingContext;
+    private final CollectionBinder collectionBinder;
+
+    public ListSecondPassBinder(MetadataBuildingContext 
metadataBuildingContext, CollectionBinder collectionBinder) {
+        this.metadataBuildingContext = metadataBuildingContext;
+        this.collectionBinder = collectionBinder;
+    }
+
+    public void bindListSecondPass(HibernateToManyProperty property, @Nonnull 
InFlightMetadataCollector mappings,
+                                   Map<?, ?> persistentClasses, 
org.hibernate.mapping.List list, String sessionFactoryBeanName) {
+
+        collectionBinder.bindCollectionSecondPass(property, mappings, 
persistentClasses, list, sessionFactoryBeanName);
+
+        String columnName = collectionBinder.getIndexColumnName(property);
+        final boolean isManyToMany = property instanceof ManyToMany;
+
+        if (isManyToMany && !property.isOwningSide()) {
+            throw new MappingException("Invalid association [" + property +
+                    "]. List collection types only supported on the owning 
side of a many-to-many relationship.");
+        }
+
+        Table collectionTable = list.getCollectionTable();
+        SimpleValue iv = new BasicValue(metadataBuildingContext, 
collectionTable);
+        String type = ((GrailsHibernatePersistentProperty) 
property).getIndexColumnType("integer");
+        new SimpleValueColumnBinder().bindSimpleValue(iv, type, columnName, 
true);
+        iv.setTypeName(type);
+        list.setIndex(iv);
+        list.setBaseIndex(0);
+        list.setInverse(false);
+
+        Value v = list.getElement();
+        v.createForeignKey();
+
+        if (property.isBidirectional()) {
+
+            String entityName;
+            Value element = list.getElement();
+            if (element instanceof ManyToOne) {
+                ManyToOne manyToOne = (ManyToOne) element;
+                entityName = manyToOne.getReferencedEntityName();
+            } else {
+                entityName = ((OneToMany) element).getReferencedEntityName();
+            }
+
+            PersistentClass referenced = mappings.getEntityBinding(entityName);
+
+            boolean compositeIdProperty = 
property.getInverseSide().isCompositeIdProperty();
+            if (!compositeIdProperty) {
+                Backref prop = new Backref();
+                final PersistentEntity owner = property.getOwner();
+                prop.setEntityName(owner.getName());
+                String s2 = property.getName();
+                prop.setName(UNDERSCORE + new 
BackticksRemover().apply(owner.getJavaClass().getSimpleName()) + UNDERSCORE + 
new BackticksRemover().apply(s2) + "Backref");
+                prop.setSelectable(false);
+                prop.setUpdatable(false);
+                if (isManyToMany) {
+                    prop.setInsertable(false);
+                }
+                prop.setCollectionRole(list.getRole());
+                prop.setValue(list.getKey());
+
+                DependantValue value = (DependantValue) prop.getValue();
+                if (!property.isCircular()) {
+                    value.setNullable(false);
+                }
+                value.setUpdateable(true);
+                prop.setOptional(false);
+
+                referenced.addProperty(prop);
+            }
+
+            if ((!list.getKey().isNullable() && !list.isInverse()) || 
compositeIdProperty) {
+                IndexBackref ib = new IndexBackref();
+                ib.setName(UNDERSCORE + property.getName() + "IndexBackref");
+                ib.setUpdatable(false);
+                ib.setSelectable(false);
+                if (isManyToMany) {
+                    ib.setInsertable(false);
+                }
+                ib.setCollectionRole(list.getRole());
+                ib.setEntityName(list.getOwner().getEntityName());
+                ib.setValue(list.getIndex());
+                referenced.addProperty(ib);
+            }
+        }
+    }
+}
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPass.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPass.java
index fcb12b9bfb..bf5f8edf7f 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPass.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPass.java
@@ -20,11 +20,6 @@ public class ListSecondPass extends 
GrailsCollectionSecondPass {
         super(grailsDomainBinder, property, mappings, coll, 
sessionFactoryBeanName);
     }
 
-    @Override
-    public void doSecondPass(Map<?, ?> persistentClasses, Map<?, ?> 
inheritedMetas) throws MappingException {
-        grailsDomainBinder.bindListSecondPass(property, mappings, 
persistentClasses,
-                (org.hibernate.mapping.List) collection, 
sessionFactoryBeanName);
-    }
 
     @SuppressWarnings("rawtypes")
     @Override
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ListSecondPassBinderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ListSecondPassBinderSpec.groovy
new file mode 100644
index 0000000000..8f872682d4
--- /dev/null
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ListSecondPassBinderSpec.groovy
@@ -0,0 +1,83 @@
+package org.grails.orm.hibernate.cfg.domainbinding
+
+import grails.gorm.annotation.Entity
+import grails.gorm.specs.HibernateGormDatastoreSpec
+import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentEntity
+import org.grails.orm.hibernate.cfg.HibernateToManyProperty
+
+import org.hibernate.mapping.RootClass
+import org.hibernate.mapping.SimpleValue
+
+class ListSecondPassBinderSpec extends HibernateGormDatastoreSpec {
+
+    void setupSpec() {
+        manager.addAllDomainClasses([
+                ListBinderAuthor,
+                ListBinderBook
+        ])
+    }
+
+    void "Test bind list second pass"() {
+        given:
+        def collector = getCollector()
+        def binder = getGrailsDomainBinder()
+        def collectionBinder = binder.getCollectionBinder()
+        def listSecondPassBinder = new 
ListSecondPassBinder(binder.getMetadataBuildingContext(), collectionBinder)
+
+        def authorEntity = getPersistentEntity(ListBinderAuthor) as 
GrailsHibernatePersistentEntity
+        def bookEntity = getPersistentEntity(ListBinderBook) as 
GrailsHibernatePersistentEntity
+
+        // Register referenced entity in Hibernate
+        binder.bindRoot(bookEntity, collector, "sessionFactory")
+
+        // Manually create RootClass for the main entity
+        def rootClass = new RootClass(binder.getMetadataBuildingContext())
+        rootClass.setEntityName(authorEntity.name)
+        rootClass.setJpaEntityName(authorEntity.name)
+        rootClass.setTable(collector.addTable(null, null, 
"LIST_BINDER_AUTHOR", null, false, binder.getMetadataBuildingContext()))
+        
+        // Add a primary key to avoid NPE
+        def pk = new org.hibernate.mapping.PrimaryKey(rootClass.table)
+        def idCol = new org.hibernate.mapping.Column("id")
+        rootClass.table.addColumn(idCol)
+        pk.addColumn(idCol)
+        rootClass.table.setPrimaryKey(pk)
+        collector.addEntityBinding(rootClass)
+
+        def booksProp = authorEntity.getPropertyByName("books") as 
HibernateToManyProperty
+        def list = new 
org.hibernate.mapping.List(binder.getMetadataBuildingContext(), rootClass)
+        list.setRole(authorEntity.name + ".books")
+        
+        // Initial first pass binding needed for second pass to work
+        collectionBinder.bindCollection(booksProp, list, rootClass, collector, 
"", "sessionFactory")
+
+        // Prepare persistentClasses map
+        Map persistentClasses = [
+            (authorEntity.name): rootClass,
+            (bookEntity.name): collector.getEntityBinding(bookEntity.name)
+        ]
+
+        when:
+        listSecondPassBinder.bindListSecondPass(booksProp, collector, 
persistentClasses, list, "sessionFactory")
+        collector.processSecondPasses(binder.getMetadataBuildingContext())
+
+        then:
+        list.index != null
+        list.index instanceof SimpleValue
+        ((SimpleValue)list.index).typeName == "integer"
+        list.element != null
+    }
+}
+
+@Entity
+class ListBinderAuthor {
+    Long id
+    java.util.List<ListBinderBook> books
+    static hasMany = [books: ListBinderBook]
+}
+
+@Entity
+class ListBinderBook {
+    Long id
+    String title
+}

Reply via email to