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 380769aaf63cc776225cfee7d8f878f473101715
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Tue Mar 3 11:06:36 2026 -0600

    refactor: extract bindBidirectionalMapElement into 
BidirectionalMapElementBinder
---
 .../cfg/domainbinding/binder/CollectionBinder.java |  2 +
 .../secondpass/BidirectionalMapElementBinder.java  | 54 ++++++++++++++
 .../secondpass/CollectionSecondPassBinder.java     | 17 ++---
 .../BidirectionalMapElementBinderSpec.groovy       | 82 ++++++++++++++++++++++
 .../CollectionSecondPassBinderSpec.groovy          |  2 +-
 5 files changed, 143 insertions(+), 14 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 f8ec48d3fd..0ddbbfb8f8 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.BidirectionalMapElementBinder;
 import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionKeyBinder;
 import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionMultiTenantFilterBinder;
 import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionOrderByBinder;
@@ -118,6 +119,7 @@ public class CollectionBinder {
                 new BidirectionalOneToManyLinker(grailsPropertyResolver),
                 new DependentKeyValueBinder(simpleValueBinder, 
compositeIdentifierToManyToOneBinder),
                 simpleValueColumnBinder),
+            new BidirectionalMapElementBinder(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/BidirectionalMapElementBinder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/BidirectionalMapElementBinder.java
new file mode 100644
index 0000000000..a16946ccb0
--- /dev/null
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/BidirectionalMapElementBinder.java
@@ -0,0 +1,54 @@
+/*
+ *  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.HibernateManyToOneProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyProperty;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.ManyToOne;
+
+/** Binds the element of a bidirectional one-to-many Map association. */
+public class BidirectionalMapElementBinder {
+
+  private final ManyToOneBinder manyToOneBinder;
+  private final CollectionForPropertyConfigBinder 
collectionForPropertyConfigBinder;
+
+  /** Creates a new {@link BidirectionalMapElementBinder} instance. */
+  public BidirectionalMapElementBinder(
+      ManyToOneBinder manyToOneBinder,
+      CollectionForPropertyConfigBinder collectionForPropertyConfigBinder) {
+    this.manyToOneBinder = manyToOneBinder;
+    this.collectionForPropertyConfigBinder = collectionForPropertyConfigBinder;
+  }
+
+  /** Binds the ManyToOne element for a bidirectional Map collection. */
+  public void bind(HibernateToManyProperty property, Collection collection) {
+    HibernateManyToOneProperty otherSide =
+        (HibernateManyToOneProperty) property.getHibernateInverseSide();
+    ManyToOne element =
+        manyToOneBinder.bindManyToOne(otherSide, 
collection.getCollectionTable(), EMPTY_PATH);
+    element.setReferencedEntityName(otherSide.getOwner().getName());
+    collection.setElement(element);
+    
collectionForPropertyConfigBinder.bindCollectionForPropertyConfig(collection, 
property);
+  }
+}
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 c413e9eeb2..ee69807823 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
@@ -28,7 +28,6 @@ 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;
-import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToOneProperty;
 import org.hibernate.MappingException;
 import org.hibernate.boot.spi.InFlightMetadataCollector;
 import org.hibernate.mapping.*;
@@ -40,6 +39,7 @@ public class CollectionSecondPassBinder {
   private final CollectionOrderByBinder collectionOrderByBinder;
   private final CollectionMultiTenantFilterBinder 
collectionMultiTenantFilterBinder;
   private final CollectionKeyBinder collectionKeyBinder;
+  private final BidirectionalMapElementBinder bidirectionalMapElementBinder;
   private final ManyToOneBinder manyToOneBinder;
   private final PrimaryKeyValueCreator primaryKeyValueCreator;
   private final CollectionKeyColumnUpdater collectionKeyColumnUpdater;
@@ -56,6 +56,7 @@ public class CollectionSecondPassBinder {
       CollectionWithJoinTableBinder collectionWithJoinTableBinder,
       CollectionForPropertyConfigBinder collectionForPropertyConfigBinder,
       CollectionKeyBinder collectionKeyBinder,
+      BidirectionalMapElementBinder bidirectionalMapElementBinder,
       CollectionOrderByBinder collectionOrderByBinder,
       CollectionMultiTenantFilterBinder collectionMultiTenantFilterBinder) {
     this.manyToOneBinder = manyToOneBinder;
@@ -65,6 +66,7 @@ public class CollectionSecondPassBinder {
     this.collectionWithJoinTableBinder = collectionWithJoinTableBinder;
     this.collectionForPropertyConfigBinder = collectionForPropertyConfigBinder;
     this.collectionKeyBinder = collectionKeyBinder;
+    this.bidirectionalMapElementBinder = bidirectionalMapElementBinder;
     this.collectionOrderByBinder = collectionOrderByBinder;
     this.collectionMultiTenantFilterBinder = collectionMultiTenantFilterBinder;
   }
@@ -119,7 +121,7 @@ public class CollectionSecondPassBinder {
     if (property instanceof HibernateManyToManyProperty manyToMany && 
manyToMany.isBidirectional()) {
       bindManyToManyElement(manyToMany, collection);
     } else if (property.isBidirectionalOneToManyMap() && 
property.isBidirectional()) {
-      bindBidirectionalMapElement(property, collection);
+      bidirectionalMapElementBinder.bind(property, collection);
     } else if (property instanceof HibernateOneToManyProperty 
oneToManyProperty && oneToManyProperty.isUnidirectionalOneToMany()) {
       unidirectionalOneToManyBinder.bind(oneToManyProperty, collection);
     } else if (property.supportsJoinColumnMapping()) {
@@ -140,17 +142,6 @@ public class CollectionSecondPassBinder {
     }
   }
 
-  private void bindBidirectionalMapElement(
-      HibernateToManyProperty property, Collection collection) {
-    HibernateManyToOneProperty otherSide =
-        (HibernateManyToOneProperty) property.getHibernateInverseSide();
-    ManyToOne element =
-        manyToOneBinder.bindManyToOne(otherSide, 
collection.getCollectionTable(), EMPTY_PATH);
-    element.setReferencedEntityName(otherSide.getOwner().getName());
-    collection.setElement(element);
-    
collectionForPropertyConfigBinder.bindCollectionForPropertyConfig(collection, 
property);
-  }
-
   private @Nonnull PersistentClass resolveAssociatedClass(
       HibernateToManyProperty property, Map<?, ?> persistentClasses) {
     return Optional.ofNullable(property.getHibernateAssociatedEntity())
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/BidirectionalMapElementBinderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/BidirectionalMapElementBinderSpec.groovy
new file mode 100644
index 0000000000..409701a4b3
--- /dev/null
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/BidirectionalMapElementBinderSpec.groovy
@@ -0,0 +1,82 @@
+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.HibernateToManyProperty
+import org.hibernate.mapping.Bag
+import org.hibernate.mapping.ManyToOne
+import org.hibernate.mapping.Table
+import spock.lang.Subject
+
+class BidirectionalMapElementBinderSpec extends HibernateGormDatastoreSpec {
+
+    @Subject
+    BidirectionalMapElementBinder binder
+
+    void setupSpec() {
+        manager.addAllDomainClasses([
+            BBMEOwner,
+            BBMEItem,
+        ])
+    }
+
+    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 BidirectionalMapElementBinder(mtob, new 
CollectionForPropertyConfigBinder())
+    }
+
+    private HibernateToManyProperty propertyFor(Class ownerClass, String name 
= "items") {
+        (getPersistentEntity(ownerClass) as 
GrailsHibernatePersistentEntity).getPropertyByName(name) as 
HibernateToManyProperty
+    }
+
+    def "bind sets ManyToOne element referencing the inverse side's owner"() {
+        given:
+        def property = propertyFor(BBMEOwner)
+        def mbc = getGrailsDomainBinder().getMetadataBuildingContext()
+        def collection = new Bag(mbc, null)
+        collection.setCollectionTable(new Table("test", "bbme_owner_items"))
+
+        when:
+        binder.bind(property, collection)
+
+        then:
+        collection.getElement() instanceof ManyToOne
+        (collection.getElement() as ManyToOne).getReferencedEntityName() == 
BBMEItem.name
+    }
+
+    def "bind honours isBidirectionalOneToManyMap on the property"() {
+        given:
+        def property = propertyFor(BBMEOwner)
+
+        expect:
+        property.isBidirectionalOneToManyMap()
+        property.isBidirectional()
+    }
+}
+
+@Entity
+class BBMEOwner {
+    Long id
+    Map<String, BBMEItem> items
+    static hasMany = [items: BBMEItem]
+}
+
+@Entity
+class BBMEItem {
+    Long id
+    String description
+    BBMEOwner owner
+    static belongsTo = [owner: BBMEOwner]
+}
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 2f01558daf..49f127415d 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 
CollectionOrderByBinder(), new CollectionMultiTenantFilterBinder(dcnf))
+        binder = new CollectionSecondPassBinder(mtob, pkvc, cku, uotmb, cwjtb, 
cfpcb, new CollectionKeyBinder(botml, dkvb, svcb), new 
BidirectionalMapElementBinder(mtob, cfpcb), new CollectionOrderByBinder(), new 
CollectionMultiTenantFilterBinder(dcnf))
     }
 
     protected HibernatePersistentProperty 
createTestHibernateToManyProperty(Class<?> domainClass = 
CSPBTestEntityWithMany, String propertyName = "items") {

Reply via email to