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 f874707c18e66d2da785038fed4e9c35f8fc604f
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Tue Mar 17 15:52:34 2026 -0500

    hibernate 7: refactor ManyToOneBinder
---
 .../binder/ForeignKeyOneToOneBinder.java           |  2 -
 .../cfg/domainbinding/binder/ManyToOneBinder.java  | 26 ++++---
 .../secondpass/ManyToManyElementBinder.java        | 14 ++--
 .../cfg/domainbinding/ManyToOneBinderSpec.groovy   | 86 +++++++++++-----------
 4 files changed, 62 insertions(+), 66 deletions(-)

diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ForeignKeyOneToOneBinder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ForeignKeyOneToOneBinder.java
index 0f085576ac..32d0c99322 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ForeignKeyOneToOneBinder.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ForeignKeyOneToOneBinder.java
@@ -21,7 +21,6 @@ package org.grails.orm.hibernate.cfg.domainbinding.binder;
 import org.hibernate.MappingException;
 import org.hibernate.mapping.Column;
 import org.hibernate.mapping.ManyToOne;
-import org.hibernate.mapping.Table;
 
 import org.grails.orm.hibernate.cfg.PropertyConfig;
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity;
@@ -51,7 +50,6 @@ public class ForeignKeyOneToOneBinder {
      * Binds the one-to-one property as a {@link ManyToOne} value and applies 
unique-key constraints.
      */
     public ManyToOne bind(HibernateOneToOneProperty property, String path) {
-        Table table = property.getTable();
         GrailsHibernatePersistentEntity refDomainClass = 
property.getHibernateAssociatedEntity();
         ManyToOne manyToOne = manyToOneBinder.bindManyToOne(property, path);
         if (refDomainClass.getHibernateCompositeIdentity().isEmpty()) {
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 90f3e82c24..6134c4101f 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
@@ -22,7 +22,9 @@ import java.util.Optional;
 
 import org.hibernate.boot.spi.MetadataBuildingContext;
 import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
+import org.hibernate.mapping.Collection;
 import org.hibernate.mapping.ManyToOne;
+import org.hibernate.mapping.Table;
 
 import org.grails.orm.hibernate.cfg.ColumnConfig;
 import org.grails.orm.hibernate.cfg.CompositeIdentity;
@@ -72,28 +74,30 @@ public class ManyToOneBinder {
     }
 
     /** Binds a many-to-one association. */
-    public ManyToOne bindManyToOne(
-            HibernateManyToOneProperty property, org.hibernate.mapping.Table 
table, String path) {
+    public ManyToOne bindManyToOne(HibernateManyToOneProperty property, Table 
table, String path) {
         return doBind(property, property.getHibernateAssociatedEntity(), 
table, path);
     }
 
     /** Binds the inverse side of a many-to-many association as a collection 
element. */
-    public ManyToOne bindManyToOne(
-            HibernateManyToManyProperty property, org.hibernate.mapping.Table 
table, String path) {
-        GrailsHibernatePersistentEntity refDomainClass = 
property.getHibernateOwner();
+    public ManyToOne bindManyToOne(HibernateManyToManyProperty property, 
String path) {
+        Collection collection = property.getCollection();
+        HibernateManyToManyProperty otherSide = 
property.getHibernateInverseSide();
+        Table collectionTable = collection.getCollectionTable();
+        GrailsHibernatePersistentEntity refDomainClass = 
otherSide.getHibernateOwner();
         Optional<CompositeIdentity> compositeId = 
refDomainClass.getHibernateCompositeIdentity();
-        if (compositeId.isEmpty() && property.isCircular()) {
-            prepareCircularManyToMany(property);
+        if (compositeId.isEmpty() && otherSide.isCircular()) {
+            prepareCircularManyToMany(otherSide);
         }
-        return doBind(property, refDomainClass, table, path);
+        ManyToOne manyToOne = doBind(otherSide, refDomainClass, 
collectionTable, path);
+        manyToOne.setReferencedEntityName(otherSide.getOwner().getName());
+        return manyToOne;
     }
 
-    public ManyToOne bindManyToOne(
-            HibernateOneToOneProperty property, String path) {
+    public ManyToOne bindManyToOne(HibernateOneToOneProperty property, String 
path) {
         return doBind(property, property.getHibernateAssociatedEntity(), 
property.getTable(), path);
     }
 
-    ManyToOne doBind(
+    private ManyToOne doBind(
             HibernateAssociation property,
             GrailsHibernatePersistentEntity refDomainClass,
             org.hibernate.mapping.Table table,
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
index e6a7344657..f44024af30 100644
--- 
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
@@ -41,15 +41,11 @@ public class ManyToManyElementBinder {
     }
 
     /** Binds the ManyToOne element for a bidirectional many-to-many 
collection. */
-    public void bind(HibernateManyToManyProperty manyToMany) {
-        Collection collection = manyToMany.getCollection();
-        HibernateManyToManyProperty otherSide = 
manyToMany.getHibernateInverseSide();
-        ManyToOne element = manyToOneBinder.bindManyToOne(otherSide, 
collection.getCollectionTable(), EMPTY_PATH);
-        element.setReferencedEntityName(otherSide.getOwner().getName());
+    public void bind(HibernateManyToManyProperty property) {
+        ManyToOne element = manyToOneBinder.bindManyToOne(property, 
EMPTY_PATH);
+        Collection collection = property.getCollection();
         collection.setElement(element);
-        
collectionForPropertyConfigBinder.bindCollectionForPropertyConfig(manyToMany);
-        if (manyToMany.isCircular()) {
-            collection.setInverse(false);
-        }
+        
collectionForPropertyConfigBinder.bindCollectionForPropertyConfig(property);
+        collection.setInverse(!property.isCircular());
     }
 }
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 08d6b0d430..b50dfba7ef 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
@@ -7,9 +7,11 @@ import org.grails.orm.hibernate.cfg.Mapping
 import org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy
 import org.grails.orm.hibernate.cfg.PropertyConfig
 import org.grails.orm.hibernate.cfg.domainbinding.binder.*
-import org.hibernate.mapping.Column
 import org.hibernate.mapping.ManyToOne
 import org.hibernate.mapping.Table
+import org.hibernate.mapping.PersistentClass
+import org.hibernate.mapping.RootClass
+import org.hibernate.mapping.Map as HibernateMap // Use non-sealed Map instead 
of abstract Collection
 import org.hibernate.boot.spi.MetadataBuildingContext
 import spock.lang.Unroll
 
@@ -24,7 +26,6 @@ class ManyToOneBinderSpec extends HibernateGormDatastoreSpec {
 
     def setup() {
         metadataBuildingContext = 
getGrailsDomainBinder().getMetadataBuildingContext()
-        // Using the 5-arg constructor from your provided Java source
         binder = new ManyToOneBinder(
                 metadataBuildingContext,
                 namingStrategy,
@@ -35,9 +36,10 @@ class ManyToOneBinderSpec extends HibernateGormDatastoreSpec 
{
     }
 
     @Unroll
-    def "Test bindManyToOne orchestration for #scenario"() {
+    def "Test bindManyToOne (ManyToOneProperty) orchestration for #scenario"() 
{
         given:
         def association = Mock(HibernateManyToOneProperty)
+        def table = Mock(Table)
         def path = "/test"
         def (mapping, refDomainClass) = mockEntity(hasCompositeId)
 
@@ -45,7 +47,7 @@ class ManyToOneBinderSpec extends HibernateGormDatastoreSpec {
         association.getMappedForm() >> Mock(PropertyConfig)
 
         when:
-        def result = binder.bindManyToOne(association, null, path)
+        def result = binder.bindManyToOne(association, table, path)
 
         then:
         result instanceof ManyToOne
@@ -59,74 +61,70 @@ class ManyToOneBinderSpec extends 
HibernateGormDatastoreSpec {
         "a simple identifier"    | false          | 0                    | 1
     }
 
-    def "Test circular many-to-many binding"() {
+    def "Test bindManyToOne (ManyToManyProperty) with circular logic"() {
         given:
         def property = Mock(HibernateManyToManyProperty)
+        def otherSide = Mock(HibernateManyToManyProperty)
+        def table = Mock(Table)
+        def collectionTable = new Table("coll_table")
+
+        // FIX: Provide real objects for the Map constructor
+        PersistentClass ownerClass = new RootClass(metadataBuildingContext)
+        def realCollection = new HibernateMap(metadataBuildingContext, 
ownerClass)
+        realCollection.setCollectionTable(collectionTable)
+
+        property.getCollection() >> realCollection
+        property.getHibernateInverseSide() >> otherSide
+
         def (mapping, ownerEntity) = mockEntity(false)
         mapping.setColumns([:])
 
         def propertyConfig = Mock(PropertyConfig)
-        property.isCircular() >> true
-        property.getOwner() >> ownerEntity
-        property.getHibernateOwner() >> ownerEntity
-        property.getName() >> "myCircularProp"
-        property.getMappedForm() >> propertyConfig
-        namingStrategy.resolveColumnName("myCircularProp") >> 
"my_circular_prop"
+        propertyConfig.hasJoinKeyMapping() >> false
+
+        otherSide.getHibernateOwner() >> ownerEntity
+        otherSide.getOwner() >> ownerEntity
+        ownerEntity.getName() >> "OwnerEntity"
+
+        otherSide.isCircular() >> true
+        otherSide.getName() >> "circularProp"
+        otherSide.getMappedForm() >> propertyConfig
+
+        namingStrategy.resolveColumnName("circularProp") >> "circular_prop"
 
         when:
-        def result = binder.bindManyToOne(property, null, "/test")
+        def result = binder.bindManyToOne(property, "/test")
 
         then:
         result instanceof ManyToOne
-        1 * manyToOneValuesBinder.bindManyToOneValues(property, _ as ManyToOne)
-        1 * simpleValueBinder.bindSimpleValue(property as 
HibernatePersistentProperty, null, _ as ManyToOne, "/test")
+        result.getReferencedEntityName() == "OwnerEntity"
+        result.getTable() == collectionTable
+        1 * manyToOneValuesBinder.bindManyToOneValues(otherSide, _ as 
ManyToOne)
+        1 * simpleValueBinder.bindSimpleValue(otherSide, null, _ as ManyToOne, 
"/test")
 
-        mapping.getColumns().containsKey("myCircularProp")
-        mapping.getColumns().get("myCircularProp") == propertyConfig
+        mapping.getColumns().get("circularProp") == propertyConfig
+        1 * propertyConfig.setJoinTable({ it.key.name == "circular_prop_id" })
     }
 
-    @Unroll
-    def "Test bindManyToOne with unique key constraints for #scenario"() {
+    def "Test bindManyToOne (OneToOneProperty)"() {
         given:
         def property = Mock(HibernateOneToOneProperty)
         def table = Mock(Table)
-        def (mapping, refDomainClass) = mockEntity(hasCompositeId)
-
-        // Mocking PropertyConfig avoids ReadOnlyPropertyException
-        def propertyConfig = Mock(PropertyConfig)
-        propertyConfig.isUnique() >> isUnique
-        propertyConfig.isUniqueWithinGroup() >> isUniqueWithinGroup
+        def (mapping, refDomainClass) = mockEntity(false)
 
         property.getTable() >> table
         property.getHibernateAssociatedEntity() >> refDomainClass
-        property.getMappedForm() >> propertyConfig
-        property.getName() >> "myUniqueProp"
-        property.isBidirectional() >> isBidirectional
-
-        if (isBidirectional) {
-            property.getInverseSide() >> Mock(HibernateOneToOneProperty) { 
isValidHibernateOneToOne() >> true }
-        }
+        property.getMappedForm() >> Mock(PropertyConfig)
 
         when:
-        // In the Java source provided, there is no bindManyToOneWithUniqueKey.
-        // Assuming you are testing bindManyToOne(HibernateOneToOneProperty, 
path)
         def result = binder.bindManyToOne(property, "/test/path")
 
         then:
         result instanceof ManyToOne
-                // Note: Logic for setting unique on Column usually happens 
inside manyToOneValuesBinder or simpleValueBinder
-                // verify interactions based on your specific implementation 
requirements
-
-       where:
-        scenario            | hasCompositeId | isBidirectional | isUnique | 
isUniqueWithinGroup
-        "Simple ID"         | false          | false           | true     | 
false
-        "Composite ID"      | true           | false           | true     | 
false
-        "Bidirectional OTO" | false          | true            | true     | 
true
+        1 * manyToOneValuesBinder.bindManyToOneValues(property, _ as ManyToOne)
+        1 * simpleValueBinder.bindSimpleValue(property, null, _ as ManyToOne, 
"/test/path")
     }
 
-    /**
-     * Helper to reduce repetitive Mocking of entities and mappings
-     */
     private List mockEntity(boolean composite) {
         def mapping = new Mapping()
         def compositeId = composite ? new CompositeIdentity() : null

Reply via email to