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 6cf5923907b2dc2da036679aab3b791a42c7aaa4
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Tue Mar 3 11:57:26 2026 -0600

    refactor: extract bindManyToManyElement into ManyToManyElementBinder
---
 .../cfg/domainbinding/binder/CollectionBinder.java |  3 +-
 .../secondpass/CollectionSecondPassBinder.java     | 24 +-----
 .../secondpass/ManyToManyElementBinder.java        | 55 ++++++++++++
 .../CollectionSecondPassBinderSpec.groovy          |  2 +-
 .../secondpass/ManyToManyElementBinderSpec.groovy  | 99 ++++++++++++++++++++++
 5 files changed, 161 insertions(+), 22 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 0ddbbfb8f8..2a9fa23bcc 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
@@ -32,6 +32,7 @@ import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.BidirectionalOneToM
 import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionKeyColumnUpdater;
 import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.BidirectionalMapElementBinder;
 import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionKeyBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.ManyToManyElementBinder;
 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;
@@ -109,7 +110,6 @@ public class CollectionBinder {
             new ColumnConfigToColumnBinder());
     this.collectionSecondPassBinder =
         new CollectionSecondPassBinder(
-            manyToOneBinder,
             new PrimaryKeyValueCreator(metadataBuildingContext),
             new CollectionKeyColumnUpdater(),
             new UnidirectionalOneToManyBinder(collectionWithJoinTableBinder, 
mappings),
@@ -120,6 +120,7 @@ public class CollectionBinder {
                 new DependentKeyValueBinder(simpleValueBinder, 
compositeIdentifierToManyToOneBinder),
                 simpleValueColumnBinder),
             new BidirectionalMapElementBinder(manyToOneBinder, 
collectionForPropertyConfigBinder),
+            new ManyToManyElementBinder(manyToOneBinder, 
collectionForPropertyConfigBinder),
             new CollectionOrderByBinder(),
             new CollectionMultiTenantFilterBinder(new 
DefaultColumnNameFetcher(namingStrategy)));
     this.listSecondPassBinder =
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 ee69807823..501f49d55b 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
@@ -18,13 +18,10 @@
  */
 package org.grails.orm.hibernate.cfg.domainbinding.secondpass;
 
-import static 
org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsDomainBinder.*;
-
 import jakarta.annotation.Nonnull;
 import java.util.*;
 import java.util.Map;
 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.hibernate.HibernateManyToManyProperty;
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateOneToManyProperty;
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyProperty;
@@ -40,7 +37,7 @@ public class CollectionSecondPassBinder {
   private final CollectionMultiTenantFilterBinder 
collectionMultiTenantFilterBinder;
   private final CollectionKeyBinder collectionKeyBinder;
   private final BidirectionalMapElementBinder bidirectionalMapElementBinder;
-  private final ManyToOneBinder manyToOneBinder;
+  private final ManyToManyElementBinder manyToManyElementBinder;
   private final PrimaryKeyValueCreator primaryKeyValueCreator;
   private final CollectionKeyColumnUpdater collectionKeyColumnUpdater;
   private final UnidirectionalOneToManyBinder unidirectionalOneToManyBinder;
@@ -49,7 +46,6 @@ public class CollectionSecondPassBinder {
 
   /** Creates a new {@link CollectionSecondPassBinder} instance. */
   public CollectionSecondPassBinder(
-      ManyToOneBinder manyToOneBinder,
       PrimaryKeyValueCreator primaryKeyValueCreator,
       CollectionKeyColumnUpdater collectionKeyColumnUpdater,
       UnidirectionalOneToManyBinder unidirectionalOneToManyBinder,
@@ -57,9 +53,9 @@ public class CollectionSecondPassBinder {
       CollectionForPropertyConfigBinder collectionForPropertyConfigBinder,
       CollectionKeyBinder collectionKeyBinder,
       BidirectionalMapElementBinder bidirectionalMapElementBinder,
+      ManyToManyElementBinder manyToManyElementBinder,
       CollectionOrderByBinder collectionOrderByBinder,
       CollectionMultiTenantFilterBinder collectionMultiTenantFilterBinder) {
-    this.manyToOneBinder = manyToOneBinder;
     this.primaryKeyValueCreator = primaryKeyValueCreator;
     this.collectionKeyColumnUpdater = collectionKeyColumnUpdater;
     this.unidirectionalOneToManyBinder = unidirectionalOneToManyBinder;
@@ -67,6 +63,7 @@ public class CollectionSecondPassBinder {
     this.collectionForPropertyConfigBinder = collectionForPropertyConfigBinder;
     this.collectionKeyBinder = collectionKeyBinder;
     this.bidirectionalMapElementBinder = bidirectionalMapElementBinder;
+    this.manyToManyElementBinder = manyToManyElementBinder;
     this.collectionOrderByBinder = collectionOrderByBinder;
     this.collectionMultiTenantFilterBinder = collectionMultiTenantFilterBinder;
   }
@@ -119,7 +116,7 @@ public class CollectionSecondPassBinder {
       InFlightMetadataCollector mappings,
       Collection collection) {
     if (property instanceof HibernateManyToManyProperty manyToMany && 
manyToMany.isBidirectional()) {
-      bindManyToManyElement(manyToMany, collection);
+      manyToManyElementBinder.bind(manyToMany, collection);
     } else if (property.isBidirectionalOneToManyMap() && 
property.isBidirectional()) {
       bidirectionalMapElementBinder.bind(property, collection);
     } else if (property instanceof HibernateOneToManyProperty 
oneToManyProperty && oneToManyProperty.isUnidirectionalOneToMany()) {
@@ -129,19 +126,6 @@ public class CollectionSecondPassBinder {
     }
   }
 
-  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 @Nonnull PersistentClass resolveAssociatedClass(
       HibernateToManyProperty property, Map<?, ?> persistentClasses) {
     return Optional.ofNullable(property.getHibernateAssociatedEntity())
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ManyToManyElementBinder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ManyToManyElementBinder.java
new file mode 100644
index 0000000000..9d4aa3654b
--- /dev/null
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ManyToManyElementBinder.java
@@ -0,0 +1,55 @@
+/*
+ *  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 static 
org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsDomainBinder.EMPTY_PATH;
+
+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.hibernate.HibernateManyToManyProperty;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.ManyToOne;
+
+/** Binds the element of a bidirectional many-to-many association. */
+public class ManyToManyElementBinder {
+
+  private final ManyToOneBinder manyToOneBinder;
+  private final CollectionForPropertyConfigBinder 
collectionForPropertyConfigBinder;
+
+  /** Creates a new {@link ManyToManyElementBinder} instance. */
+  public ManyToManyElementBinder(
+      ManyToOneBinder manyToOneBinder,
+      CollectionForPropertyConfigBinder collectionForPropertyConfigBinder) {
+    this.manyToOneBinder = manyToOneBinder;
+    this.collectionForPropertyConfigBinder = collectionForPropertyConfigBinder;
+  }
+
+  /** Binds the ManyToOne element for a bidirectional many-to-many collection. 
*/
+  public void bind(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);
+    }
+  }
+}
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 49f127415d..4aa8190304 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, uotmb, cwjtb, 
cfpcb, new CollectionKeyBinder(botml, dkvb, svcb), new 
BidirectionalMapElementBinder(mtob, cfpcb), new CollectionOrderByBinder(), new 
CollectionMultiTenantFilterBinder(dcnf))
+        binder = new CollectionSecondPassBinder(pkvc, cku, uotmb, cwjtb, 
cfpcb, new CollectionKeyBinder(botml, dkvb, svcb), new 
BidirectionalMapElementBinder(mtob, cfpcb), new ManyToManyElementBinder(mtob, 
cfpcb), new CollectionOrderByBinder(), new 
CollectionMultiTenantFilterBinder(dcnf))
     }
 
     protected HibernatePersistentProperty 
createTestHibernateToManyProperty(Class<?> domainClass = 
CSPBTestEntityWithMany, String propertyName = "items") {
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ManyToManyElementBinderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ManyToManyElementBinderSpec.groovy
new file mode 100644
index 0000000000..af397f4a2a
--- /dev/null
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ManyToManyElementBinderSpec.groovy
@@ -0,0 +1,99 @@
+package org.grails.orm.hibernate.cfg.domainbinding.secondpass
+
+import grails.gorm.annotation.Entity
+import grails.gorm.specs.HibernateGormDatastoreSpec
+import 
org.grails.orm.hibernate.cfg.domainbinding.binder.CollectionForPropertyConfigBinder
+import 
org.grails.orm.hibernate.cfg.domainbinding.binder.CompositeIdentifierToManyToOneBinder
+import org.grails.orm.hibernate.cfg.domainbinding.binder.ManyToOneBinder
+import org.grails.orm.hibernate.cfg.domainbinding.binder.ManyToOneValuesBinder
+import org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueBinder
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToManyProperty
+import org.hibernate.mapping.Bag
+import org.hibernate.mapping.ManyToOne
+import org.hibernate.mapping.Table
+import spock.lang.Subject
+
+class ManyToManyElementBinderSpec extends HibernateGormDatastoreSpec {
+
+    @Subject
+    ManyToManyElementBinder binder
+
+    void setupSpec() {
+        manager.addAllDomainClasses([
+            MTMEOwner,
+            MTMEItem,
+            MTMEBase,
+            MTMESubtype,
+        ])
+    }
+
+    void setup() {
+        def gdb = getGrailsDomainBinder()
+        def mbc = gdb.getMetadataBuildingContext()
+        def ns = gdb.getNamingStrategy()
+        def je = gdb.getJdbcEnvironment()
+        def svb = new SimpleValueBinder(mbc, ns, je)
+        def citmto = new CompositeIdentifierToManyToOneBinder(mbc, ns, je)
+        def mtob = new ManyToOneBinder(mbc, ns, svb, new 
ManyToOneValuesBinder(), citmto)
+        binder = new ManyToManyElementBinder(mtob, new 
CollectionForPropertyConfigBinder())
+    }
+
+    private HibernateManyToManyProperty propertyFor(Class ownerClass, String 
name = "items") {
+        (getPersistentEntity(ownerClass) as 
GrailsHibernatePersistentEntity).getPropertyByName(name) as 
HibernateManyToManyProperty
+    }
+
+    def "bind sets ManyToOne element referencing the inverse owner for a 
standard bidirectional many-to-many"() {
+        given:
+        def property = propertyFor(MTMEOwner)
+        def mbc = getGrailsDomainBinder().getMetadataBuildingContext()
+        def collection = new Bag(mbc, null)
+        collection.setCollectionTable(new Table("test", 
"mtme_owner_mtme_item"))
+
+        when:
+        binder.bind(property, collection)
+
+        then:
+        collection.getElement() instanceof ManyToOne
+        (collection.getElement() as ManyToOne).getReferencedEntityName() == 
MTMEItem.name
+    }
+
+    def "bind sets collection inverse false for a circular many-to-many"() {
+        given:
+        def property = propertyFor(MTMESubtype, "related")
+        def mbc = getGrailsDomainBinder().getMetadataBuildingContext()
+        def collection = new Bag(mbc, null)
+        collection.setCollectionTable(new Table("test", 
"mtme_subtype_mtme_base"))
+
+        when:
+        binder.bind(property, collection)
+
+        then:
+        property.isCircular()
+        !collection.isInverse()
+    }
+}
+
+@Entity
+class MTMEOwner {
+    Long id
+    static hasMany = [items: MTMEItem]
+}
+
+@Entity
+class MTMEItem {
+    Long id
+    String description
+    static hasMany = [owners: MTMEOwner]
+}
+
+@Entity
+class MTMEBase {
+    Long id
+    static hasMany = [subtypes: MTMESubtype]
+}
+
+@Entity
+class MTMESubtype extends MTMEBase {
+    static hasMany = [related: MTMEBase]
+}

Reply via email to