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 ec1ca22399fc798b48eafe9de0c88ef2a631e2d2
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Tue Mar 3 10:37:50 2026 -0600

    refactor: extract bindOrderBy and applyMultiTenantFilter from 
CollectionSecondPassBinder
---
 .../cfg/domainbinding/binder/CollectionBinder.java |   5 +-
 .../CollectionMultiTenantFilterBinder.java         |  65 +++++++++
 .../secondpass/CollectionSecondPassBinder.java     |  38 +-----
 .../CollectionMultiTenantFilterBinderSpec.groovy   | 149 +++++++++++++++++++++
 .../CollectionSecondPassBinderSpec.groovy          |   2 +-
 5 files changed, 223 insertions(+), 36 deletions(-)

diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CollectionBinder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CollectionBinder.java
index 93648444f9..174e31d7da 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CollectionBinder.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CollectionBinder.java
@@ -30,6 +30,7 @@ import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentP
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyProperty;
 import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.BidirectionalOneToManyLinker;
 import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionKeyColumnUpdater;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionMultiTenantFilterBinder;
 import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionOrderByBinder;
 import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionSecondPassBinder;
 import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionWithJoinTableBinder;
@@ -114,9 +115,9 @@ public class CollectionBinder {
             new UnidirectionalOneToManyBinder(collectionWithJoinTableBinder, 
mappings),
             collectionWithJoinTableBinder,
             collectionForPropertyConfigBinder,
-            new DefaultColumnNameFetcher(namingStrategy),
             simpleValueColumnBinder,
-            new CollectionOrderByBinder());
+            new CollectionOrderByBinder(),
+            new CollectionMultiTenantFilterBinder(new 
DefaultColumnNameFetcher(namingStrategy)));
     this.listSecondPassBinder =
         new ListSecondPassBinder(
             metadataBuildingContext,
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionMultiTenantFilterBinder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionMultiTenantFilterBinder.java
new file mode 100644
index 0000000000..f228053dce
--- /dev/null
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionMultiTenantFilterBinder.java
@@ -0,0 +1,65 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.grails.orm.hibernate.cfg.domainbinding.secondpass;
+
+import java.util.Collections;
+import java.util.Optional;
+import org.grails.datastore.mapping.model.config.GormProperties;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToManyProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.DefaultColumnNameFetcher;
+import org.hibernate.mapping.Collection;
+
+/** Applies multi-tenant filters to a collection based on the associated 
entity's tenancy. */
+public class CollectionMultiTenantFilterBinder {
+
+  private final DefaultColumnNameFetcher defaultColumnNameFetcher;
+
+  /** Creates a new {@link CollectionMultiTenantFilterBinder} instance. */
+  public CollectionMultiTenantFilterBinder(DefaultColumnNameFetcher 
defaultColumnNameFetcher) {
+    this.defaultColumnNameFetcher = defaultColumnNameFetcher;
+  }
+
+  /** Applies the multi-tenant filter to the collection if the associated 
entity is multi-tenant. */
+  public void bind(HibernateToManyProperty property, Collection collection) {
+    Optional.ofNullable(property.getHibernateAssociatedEntity())
+        .filter(
+            referenced ->
+                !(property instanceof HibernateManyToManyProperty) && 
referenced.isMultiTenant())
+        .map(referenced -> 
referenced.getMultiTenantFilterCondition(defaultColumnNameFetcher))
+        .ifPresent(
+            filterCondition -> {
+              if (property.isUnidirectionalOneToMany()) {
+                collection.addManyToManyFilter(
+                    GormProperties.TENANT_IDENTITY,
+                    filterCondition,
+                    true,
+                    Collections.emptyMap(),
+                    Collections.emptyMap());
+              } else {
+                collection.addFilter(
+                    GormProperties.TENANT_IDENTITY,
+                    filterCondition,
+                    true,
+                    Collections.emptyMap(),
+                    Collections.emptyMap());
+              }
+            });
+  }
+}
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 83410c5573..69356c87f2 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
@@ -23,7 +23,6 @@ import static 
org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsDomainBind
 import jakarta.annotation.Nonnull;
 import java.util.*;
 import java.util.Map;
-import org.grails.datastore.mapping.model.config.GormProperties;
 import 
org.grails.orm.hibernate.cfg.domainbinding.binder.CollectionForPropertyConfigBinder;
 import org.grails.orm.hibernate.cfg.domainbinding.binder.ManyToOneBinder;
 import 
org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueColumnBinder;
@@ -31,7 +30,6 @@ 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.HibernateToManyProperty;
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToOneProperty;
-import 
org.grails.orm.hibernate.cfg.domainbinding.util.DefaultColumnNameFetcher;
 import org.hibernate.MappingException;
 import org.hibernate.boot.spi.InFlightMetadataCollector;
 import org.hibernate.mapping.*;
@@ -45,8 +43,8 @@ public class CollectionSecondPassBinder {
 
   private static final Logger LOG = 
LoggerFactory.getLogger(CollectionSecondPassBinder.class);
 
-  private final DefaultColumnNameFetcher defaultColumnNameFetcher;
   private final CollectionOrderByBinder collectionOrderByBinder;
+  private final CollectionMultiTenantFilterBinder 
collectionMultiTenantFilterBinder;
   private final ManyToOneBinder manyToOneBinder;
   private final PrimaryKeyValueCreator primaryKeyValueCreator;
   private final CollectionKeyColumnUpdater collectionKeyColumnUpdater;
@@ -67,9 +65,9 @@ public class CollectionSecondPassBinder {
       UnidirectionalOneToManyBinder unidirectionalOneToManyBinder,
       CollectionWithJoinTableBinder collectionWithJoinTableBinder,
       CollectionForPropertyConfigBinder collectionForPropertyConfigBinder,
-      DefaultColumnNameFetcher defaultColumnNameFetcher,
       SimpleValueColumnBinder simpleValueColumnBinder,
-      CollectionOrderByBinder collectionOrderByBinder) {
+      CollectionOrderByBinder collectionOrderByBinder,
+      CollectionMultiTenantFilterBinder collectionMultiTenantFilterBinder) {
     this.manyToOneBinder = manyToOneBinder;
     this.primaryKeyValueCreator = primaryKeyValueCreator;
     this.collectionKeyColumnUpdater = collectionKeyColumnUpdater;
@@ -78,9 +76,9 @@ public class CollectionSecondPassBinder {
     this.unidirectionalOneToManyBinder = unidirectionalOneToManyBinder;
     this.collectionWithJoinTableBinder = collectionWithJoinTableBinder;
     this.collectionForPropertyConfigBinder = collectionForPropertyConfigBinder;
-    this.defaultColumnNameFetcher = defaultColumnNameFetcher;
     this.simpleValueColumnBinder = simpleValueColumnBinder;
     this.collectionOrderByBinder = collectionOrderByBinder;
+    this.collectionMultiTenantFilterBinder = collectionMultiTenantFilterBinder;
   }
 
   /** Bind collection second pass. */
@@ -94,8 +92,7 @@ public class CollectionSecondPassBinder {
     collectionOrderByBinder.bind(property, collection, associatedClass);
     bindOneToManyAssociation(property, associatedClass, collection);
 
-    applyMultiTenantFilter(property, collection);
-
+    collectionMultiTenantFilterBinder.bind(property, collection);
     if (property.isSorted()) {
       collection.setSorted(true);
     }
@@ -126,31 +123,6 @@ public class CollectionSecondPassBinder {
     
collectionForPropertyConfigBinder.bindCollectionForPropertyConfig(collection, 
property);
   }
 
-  private void applyMultiTenantFilter(HibernateToManyProperty property, 
Collection collection) {
-    Optional.ofNullable(property.getHibernateAssociatedEntity())
-        .filter(
-            referenced ->
-                !(property instanceof HibernateManyToManyProperty) && 
referenced.isMultiTenant())
-        .map(referenced -> 
referenced.getMultiTenantFilterCondition(defaultColumnNameFetcher))
-        .ifPresent(
-            filterCondition -> {
-              if (property.isUnidirectionalOneToMany()) {
-                collection.addManyToManyFilter(
-                    GormProperties.TENANT_IDENTITY,
-                    filterCondition,
-                    true,
-                    Collections.emptyMap(),
-                    Collections.emptyMap());
-              } else {
-                collection.addFilter(
-                    GormProperties.TENANT_IDENTITY,
-                    filterCondition,
-                    true,
-                    Collections.emptyMap(),
-                    Collections.emptyMap());
-              }
-            });
-  }
 
   private void bindCollectionKey(
       HibernateToManyProperty property,
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionMultiTenantFilterBinderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionMultiTenantFilterBinderSpec.groovy
new file mode 100644
index 0000000000..7442b848db
--- /dev/null
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionMultiTenantFilterBinderSpec.groovy
@@ -0,0 +1,149 @@
+package org.grails.orm.hibernate.cfg.domainbinding.secondpass
+
+import grails.gorm.MultiTenant
+import grails.gorm.annotation.Entity
+import grails.gorm.specs.HibernateGormDatastoreSpec
+import org.grails.datastore.mapping.model.config.GormProperties
+import org.grails.datastore.mapping.multitenancy.MultiTenancySettings
+import 
org.grails.datastore.mapping.multitenancy.resolvers.SystemPropertyTenantResolver
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyProperty
+import org.grails.orm.hibernate.cfg.domainbinding.util.BackticksRemover
+import org.grails.orm.hibernate.cfg.domainbinding.util.DefaultColumnNameFetcher
+import org.hibernate.mapping.Bag
+import spock.lang.Subject
+
+class CollectionMultiTenantFilterBinderSpec extends HibernateGormDatastoreSpec 
{
+
+    @Subject
+    CollectionMultiTenantFilterBinder binder
+
+    void setupSpec() {
+        manager.addAllDomainClasses([
+            CMTBBidirectionalOwner,
+            CMTBBidirectionalItem,
+            CMTBUnidirectionalOwner,
+            CMTBUnidirectionalItem,
+            CMTBNonTenantOwner,
+            CMTBNonTenantItem,
+            CMTBManyToManyOwner,
+            CMTBManyToManyItem,
+        ])
+        manager.grailsConfig = [
+            "grails.gorm.multiTenancy.mode"               : 
MultiTenancySettings.MultiTenancyMode.DISCRIMINATOR,
+            "grails.gorm.multiTenancy.tenantResolverClass": 
SystemPropertyTenantResolver,
+        ]
+    }
+
+    void setup() {
+        def ns = getGrailsDomainBinder().getNamingStrategy()
+        binder = new CollectionMultiTenantFilterBinder(new 
DefaultColumnNameFetcher(ns, new BackticksRemover()))
+    }
+
+    private HibernateToManyProperty propertyFor(Class ownerClass, String name 
= "items") {
+        (getPersistentEntity(ownerClass) as 
GrailsHibernatePersistentEntity).getPropertyByName(name) as 
HibernateToManyProperty
+    }
+
+    def "bind adds collection filter for bidirectional one-to-many to 
multi-tenant entity"() {
+        given:
+        def property = propertyFor(CMTBBidirectionalOwner)
+        def collection = new 
Bag(getGrailsDomainBinder().getMetadataBuildingContext(), null)
+
+        when:
+        binder.bind(property, collection)
+
+        then:
+        collection.getFilters().any { it.getName() == 
GormProperties.TENANT_IDENTITY }
+        collection.getManyToManyFilters().isEmpty()
+    }
+
+    def "bind adds manyToMany filter for unidirectional one-to-many to 
multi-tenant entity"() {
+        given:
+        def property = propertyFor(CMTBUnidirectionalOwner)
+        def collection = new 
Bag(getGrailsDomainBinder().getMetadataBuildingContext(), null)
+
+        when:
+        binder.bind(property, collection)
+
+        then:
+        collection.getManyToManyFilters().any { it.getName() == 
GormProperties.TENANT_IDENTITY }
+        collection.getFilters().isEmpty()
+    }
+
+    def "bind does not add filter for ManyToMany even when associated entity 
is multi-tenant"() {
+        given:
+        def property = propertyFor(CMTBManyToManyOwner)
+        def collection = new 
Bag(getGrailsDomainBinder().getMetadataBuildingContext(), null)
+
+        when:
+        binder.bind(property, collection)
+
+        then:
+        collection.getFilters().isEmpty()
+        collection.getManyToManyFilters().isEmpty()
+    }
+
+    def "bind does not add filter when associated entity is not 
multi-tenant"() {
+        given:
+        def property = propertyFor(CMTBNonTenantOwner)
+        def collection = new 
Bag(getGrailsDomainBinder().getMetadataBuildingContext(), null)
+
+        when:
+        binder.bind(property, collection)
+
+        then:
+        collection.getFilters().isEmpty()
+        collection.getManyToManyFilters().isEmpty()
+    }
+}
+
+@Entity
+class CMTBBidirectionalOwner {
+    Long id
+    static hasMany = [items: CMTBBidirectionalItem]
+}
+
+@Entity
+class CMTBBidirectionalItem implements MultiTenant<CMTBBidirectionalItem> {
+    Long id
+    Long tenantId
+    CMTBBidirectionalOwner owner
+    static belongsTo = [owner: CMTBBidirectionalOwner]
+}
+
+@Entity
+class CMTBUnidirectionalOwner {
+    Long id
+    static hasMany = [items: CMTBUnidirectionalItem]
+}
+
+@Entity
+class CMTBUnidirectionalItem implements MultiTenant<CMTBUnidirectionalItem> {
+    Long id
+    Long tenantId
+}
+
+@Entity
+class CMTBNonTenantOwner {
+    Long id
+    static hasMany = [items: CMTBNonTenantItem]
+}
+
+@Entity
+class CMTBNonTenantItem {
+    Long id
+    String name
+}
+
+@Entity
+class CMTBManyToManyOwner {
+    Long id
+    static hasMany = [items: CMTBManyToManyItem]
+}
+
+@Entity
+class CMTBManyToManyItem implements MultiTenant<CMTBManyToManyItem> {
+    Long id
+    Long tenantId
+    static hasMany = [owners: CMTBManyToManyOwner]
+}
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinderSpec.groovy
index 09f56705c9..bce105641c 100644
--- 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinderSpec.groovy
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinderSpec.groovy
@@ -41,7 +41,7 @@ class CollectionSecondPassBinderSpec extends 
HibernateGormDatastoreSpec {
         def dcnf = new DefaultColumnNameFetcher(ns, new BackticksRemover())
         def svcb = new SimpleValueColumnBinder()
 
-        binder = new CollectionSecondPassBinder(mtob, pkvc, cku, botml, dkvb, 
uotmb, cwjtb, cfpcb, dcnf, svcb, new CollectionOrderByBinder())
+        binder = new CollectionSecondPassBinder(mtob, pkvc, cku, botml, dkvb, 
uotmb, cwjtb, cfpcb, svcb, new CollectionOrderByBinder(), new 
CollectionMultiTenantFilterBinder(dcnf))
     }
 
     protected HibernatePersistentProperty 
createTestHibernateToManyProperty(Class<?> domainClass = 
CSPBTestEntityWithMany, String propertyName = "items") {

Reply via email to