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 1b205085814495ea6715dfcb55b53ad37352cd69
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Mon Mar 2 13:09:33 2026 -0600

    refactor: split ManyToOneBinder into ManyToOneBinder and 
ForeignKeyOneToOneBinder
---
 .../binder/ForeignKeyOneToOneBinder.java           |  77 ++++++++++++
 .../domainbinding/binder/GrailsDomainBinder.java   |   8 +-
 .../domainbinding/binder/GrailsPropertyBinder.java |   7 +-
 .../cfg/domainbinding/binder/ManyToOneBinder.java  |  46 +------
 .../cfg/domainbinding/CollectionBinderSpec.groovy  |   7 +-
 .../ForeignKeyOneToOneBinderSpec.groovy            | 134 +++++++++++++++++++++
 .../domainbinding/GrailsPropertyBinderSpec.groovy  |  11 +-
 .../cfg/domainbinding/ManyToOneBinderSpec.groovy   |  96 +--------------
 .../CollectionSecondPassBinderSpec.groovy          |   2 +-
 .../secondpass/ListSecondPassBinderSpec.groovy     |   7 +-
 .../secondpass/MapSecondPassBinderSpec.groovy      |   7 +-
 11 files changed, 251 insertions(+), 151 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
new file mode 100644
index 0000000000..ef45844a18
--- /dev/null
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ForeignKeyOneToOneBinder.java
@@ -0,0 +1,77 @@
+/*
+ *  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.binder;
+
+import org.grails.orm.hibernate.cfg.PropertyConfig;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateOneToOneProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.SimpleValueColumnFetcher;
+import org.hibernate.MappingException;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.ManyToOne;
+
+/**
+ * Binds a {@link HibernateOneToOneProperty} whose foreign key resides on this 
side as a Hibernate
+ * {@link ManyToOne} value, and applies unique-key constraints as needed.
+ *
+ * <p>This handles the case where {@code isValidHibernateOneToOne()} is {@code 
false} — i.e. the
+ * association cannot be mapped as a Hibernate {@code OneToOne}, so it falls 
back to a ManyToOne
+ * column with an alternate unique key.
+ */
+public class ForeignKeyOneToOneBinder {
+
+  private final ManyToOneBinder manyToOneBinder;
+  private final SimpleValueColumnFetcher simpleValueColumnFetcher;
+
+  public ForeignKeyOneToOneBinder(
+      ManyToOneBinder manyToOneBinder, SimpleValueColumnFetcher 
simpleValueColumnFetcher) {
+    this.manyToOneBinder = manyToOneBinder;
+    this.simpleValueColumnFetcher = simpleValueColumnFetcher;
+  }
+
+  /**
+   * Binds the one-to-one property as a {@link ManyToOne} value and applies 
unique-key constraints.
+   */
+  public ManyToOne bind(
+      HibernateOneToOneProperty property, org.hibernate.mapping.Table table, 
String path) {
+    GrailsHibernatePersistentEntity refDomainClass = 
property.getHibernateAssociatedEntity();
+    boolean isComposite = 
ManyToOneBinder.isCompositeIdentifier(refDomainClass);
+    ManyToOne manyToOne =
+        manyToOneBinder.doBind(property, refDomainClass, isComposite, table, 
path);
+    if (!isComposite) {
+      bindUniqueKey(property, manyToOne);
+    }
+    return manyToOne;
+  }
+
+  private void bindUniqueKey(HibernateOneToOneProperty property, ManyToOne 
manyToOne) {
+    PropertyConfig config = property.getMappedForm();
+    manyToOne.setAlternateUniqueKey(true);
+    Column c = simpleValueColumnFetcher.getColumnForSimpleValue(manyToOne);
+    if (c == null) {
+      throw new MappingException("There is no column for property [" + 
property.getName() + "]");
+    }
+    if (!config.isUniqueWithinGroup()) {
+      c.setUnique(config.isUnique());
+    } else if (property.isBidirectional()
+        && property.getHibernateInverseSide().isValidHibernateOneToOne()) {
+      c.setUnique(true);
+    }
+  }
+}
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/GrailsDomainBinder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/GrailsDomainBinder.java
index c7ae65c34d..048022b202 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/GrailsDomainBinder.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/GrailsDomainBinder.java
@@ -142,8 +142,9 @@ public class GrailsDomainBinder implements 
AdditionalMappingContributor, TypeCon
             namingStrategy,
             simpleValueBinder,
             new ManyToOneValuesBinder(),
-            compositeIdentifierToManyToOneBinder,
-            simpleValueColumnFetcher);
+            compositeIdentifierToManyToOneBinder);
+    ForeignKeyOneToOneBinder foreignKeyOneToOneBinder =
+        new ForeignKeyOneToOneBinder(manyToOneBinder, 
simpleValueColumnFetcher);
 
     CollectionBinder collectionBinder =
         new CollectionBinder(
@@ -167,7 +168,8 @@ public class GrailsDomainBinder implements 
AdditionalMappingContributor, TypeCon
             collectionBinder,
             simpleValueBinder,
             oneToOneBinder,
-            manyToOneBinder);
+            manyToOneBinder,
+            foreignKeyOneToOneBinder);
     componentBinder.setGrailsPropertyBinder(grailsPropertyBinder);
     CompositeIdBinder compositeIdBinder =
         new CompositeIdBinder(metadataBuildingContext, componentUpdater, 
grailsPropertyBinder);
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/GrailsPropertyBinder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/GrailsPropertyBinder.java
index c970dd5a32..75ed9937ce 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/GrailsPropertyBinder.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/GrailsPropertyBinder.java
@@ -42,6 +42,7 @@ public class GrailsPropertyBinder {
   private final SimpleValueBinder simpleValueBinder;
   private final OneToOneBinder oneToOneBinder;
   private final ManyToOneBinder manyToOneBinder;
+  private final ForeignKeyOneToOneBinder foreignKeyOneToOneBinder;
 
   public GrailsPropertyBinder(
       EnumTypeBinder enumTypeBinder,
@@ -49,13 +50,15 @@ public class GrailsPropertyBinder {
       CollectionBinder collectionBinder,
       SimpleValueBinder simpleValueBinder,
       OneToOneBinder oneToOneBinder,
-      ManyToOneBinder manyToOneBinder) {
+      ManyToOneBinder manyToOneBinder,
+      ForeignKeyOneToOneBinder foreignKeyOneToOneBinder) {
     this.enumTypeBinder = enumTypeBinder;
     this.componentBinder = componentBinder;
     this.collectionBinder = collectionBinder;
     this.simpleValueBinder = simpleValueBinder;
     this.oneToOneBinder = oneToOneBinder;
     this.manyToOneBinder = manyToOneBinder;
+    this.foreignKeyOneToOneBinder = foreignKeyOneToOneBinder;
   }
 
   public Value bindProperty(
@@ -81,7 +84,7 @@ public class GrailsPropertyBinder {
         && oneToOne.isValidHibernateOneToOne()) {
       value = oneToOneBinder.bindOneToOne(oneToOne, persistentClass, table, 
path);
     } else if (currentGrailsProp instanceof HibernateOneToOneProperty 
oneToOne) {
-      value = manyToOneBinder.bindManyToOne(oneToOne, table, path);
+      value = foreignKeyOneToOneBinder.bind(oneToOne, table, path);
     } else if (currentGrailsProp instanceof HibernateManyToOneProperty 
manyToOne) {
       value = manyToOneBinder.bindManyToOne(manyToOne, table, path);
     } else if (currentGrailsProp instanceof HibernateToManyProperty toMany
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 35e0af023a..7e256ff33f 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
@@ -30,12 +30,8 @@ import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersi
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateAssociation;
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToManyProperty;
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToOneProperty;
-import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateOneToOneProperty;
-import 
org.grails.orm.hibernate.cfg.domainbinding.util.SimpleValueColumnFetcher;
-import org.hibernate.MappingException;
 import org.hibernate.boot.spi.MetadataBuildingContext;
 import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
-import org.hibernate.mapping.Column;
 import org.hibernate.mapping.ManyToOne;
 
 @SuppressWarnings("PMD.DataflowAnomalyAnalysis")
@@ -46,21 +42,18 @@ public class ManyToOneBinder {
   private final SimpleValueBinder simpleValueBinder;
   private final ManyToOneValuesBinder manyToOneValuesBinder;
   private final CompositeIdentifierToManyToOneBinder 
compositeIdentifierToManyToOneBinder;
-  private final SimpleValueColumnFetcher simpleValueColumnFetcher;
 
   public ManyToOneBinder(
       MetadataBuildingContext metadataBuildingContext,
       PersistentEntityNamingStrategy namingStrategy,
       SimpleValueBinder simpleValueBinder,
       ManyToOneValuesBinder manyToOneValuesBinder,
-      CompositeIdentifierToManyToOneBinder 
compositeIdentifierToManyToOneBinder,
-      SimpleValueColumnFetcher simpleValueColumnFetcher) {
+      CompositeIdentifierToManyToOneBinder 
compositeIdentifierToManyToOneBinder) {
     this.metadataBuildingContext = metadataBuildingContext;
     this.namingStrategy = namingStrategy;
     this.simpleValueBinder = simpleValueBinder;
     this.manyToOneValuesBinder = manyToOneValuesBinder;
     this.compositeIdentifierToManyToOneBinder = 
compositeIdentifierToManyToOneBinder;
-    this.simpleValueColumnFetcher = simpleValueColumnFetcher;
   }
 
   public ManyToOneBinder(
@@ -73,8 +66,7 @@ public class ManyToOneBinder {
         new SimpleValueBinder(metadataBuildingContext, namingStrategy, 
jdbcEnvironment),
         new ManyToOneValuesBinder(),
         new CompositeIdentifierToManyToOneBinder(
-            metadataBuildingContext, namingStrategy, jdbcEnvironment),
-        new SimpleValueColumnFetcher());
+            metadataBuildingContext, namingStrategy, jdbcEnvironment));
   }
 
   /** Binds a many-to-one association. */
@@ -84,21 +76,6 @@ public class ManyToOneBinder {
     return doBind(property, refDomainClass, 
isCompositeIdentifier(refDomainClass), table, path);
   }
 
-  /**
-   * Binds a one-to-one association where the foreign key resides on the other 
side (not a true
-   * Hibernate one-to-one), i.e. {@code isHibernateOneToOne()} is false.
-   */
-  public ManyToOne bindManyToOne(
-      HibernateOneToOneProperty property, org.hibernate.mapping.Table table, 
String path) {
-    GrailsHibernatePersistentEntity refDomainClass = 
property.getHibernateAssociatedEntity();
-    boolean isComposite = isCompositeIdentifier(refDomainClass);
-    ManyToOne manyToOne = doBind(property, refDomainClass, isComposite, table, 
path);
-    if (!isComposite) {
-      bindOneToOneUniqueKey(property, manyToOne);
-    }
-    return manyToOne;
-  }
-
   /** 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) {
@@ -110,12 +87,12 @@ public class ManyToOneBinder {
     return doBind(property, refDomainClass, isComposite, table, path);
   }
 
-  private static boolean isCompositeIdentifier(GrailsHibernatePersistentEntity 
entity) {
+  static boolean isCompositeIdentifier(GrailsHibernatePersistentEntity entity) 
{
     Mapping mapping = entity.getMappedForm();
     return mapping != null && mapping.hasCompositeIdentifier();
   }
 
-  private ManyToOne doBind(
+  ManyToOne doBind(
       HibernateAssociation property,
       GrailsHibernatePersistentEntity refDomainClass,
       boolean isComposite,
@@ -134,21 +111,6 @@ public class ManyToOneBinder {
     return manyToOne;
   }
 
-  private void bindOneToOneUniqueKey(HibernateOneToOneProperty property, 
ManyToOne manyToOne) {
-    PropertyConfig config = property.getMappedForm();
-    manyToOne.setAlternateUniqueKey(true);
-    Column c = simpleValueColumnFetcher.getColumnForSimpleValue(manyToOne);
-    if (c == null) {
-      throw new MappingException("There is no column for property [" + 
property.getName() + "]");
-    }
-    if (!config.isUniqueWithinGroup()) {
-      c.setUnique(config.isUnique());
-    } else if (property.isBidirectional()
-        && property.getHibernateInverseSide().isValidHibernateOneToOne()) {
-      c.setUnique(true);
-    }
-  }
-
   private void prepareCircularManyToMany(HibernateManyToManyProperty property, 
Mapping mapping) {
     PropertyConfig pc = property.getMappedForm();
     if (mapping != null && pc.getColumns().isEmpty()) {
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CollectionBinderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CollectionBinderSpec.groovy
index 629c3e9f6f..945bf05dd5 100644
--- 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CollectionBinderSpec.groovy
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CollectionBinderSpec.groovy
@@ -10,6 +10,7 @@ import 
org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy
 import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment
 import org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueBinder
 import org.grails.orm.hibernate.cfg.domainbinding.binder.EnumTypeBinder
+import 
org.grails.orm.hibernate.cfg.domainbinding.binder.ForeignKeyOneToOneBinder
 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.CompositeIdentifierToManyToOneBinder
@@ -71,7 +72,8 @@ class CollectionBinderSpec extends HibernateGormDatastoreSpec 
{
                 simpleValueBinder
         )
         OneToOneBinder oneToOneBinder = new 
OneToOneBinder(metadataBuildingContext, simpleValueBinder)
-        ManyToOneBinder manyToOneBinder = new 
ManyToOneBinder(metadataBuildingContext, namingStrategy, simpleValueBinder, new 
ManyToOneValuesBinder(), compositeIdentifierToManyToOneBinder, 
simpleValueColumnFetcher)
+        ManyToOneBinder manyToOneBinder = new 
ManyToOneBinder(metadataBuildingContext, namingStrategy, simpleValueBinder, new 
ManyToOneValuesBinder(), compositeIdentifierToManyToOneBinder)
+        ForeignKeyOneToOneBinder foreignKeyOneToOneBinder = new 
ForeignKeyOneToOneBinder(manyToOneBinder, simpleValueColumnFetcher)
 
         CollectionBinder collectionBinder = new CollectionBinder(
                 metadataBuildingContext,
@@ -104,7 +106,8 @@ class CollectionBinderSpec extends 
HibernateGormDatastoreSpec {
                 simpleValueBinder
                 ,
                 oneToOneBinder,
-                manyToOneBinder
+                manyToOneBinder,
+                foreignKeyOneToOneBinder
 
         )
         CompositeIdBinder compositeIdBinder = new 
CompositeIdBinder(metadataBuildingContext, componentUpdater, propertyBinder)
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ForeignKeyOneToOneBinderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ForeignKeyOneToOneBinderSpec.groovy
new file mode 100644
index 0000000000..bfcadc3367
--- /dev/null
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ForeignKeyOneToOneBinderSpec.groovy
@@ -0,0 +1,134 @@
+/*
+ *  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
+
+import grails.gorm.specs.HibernateGormDatastoreSpec
+import org.grails.datastore.mapping.model.MappingContext
+import org.grails.datastore.mapping.model.PersistentEntity
+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.hibernate.GrailsHibernatePersistentEntity
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateOneToOneProperty
+import org.hibernate.MappingException
+import org.hibernate.mapping.Column
+import org.hibernate.mapping.ManyToOne
+import spock.lang.Unroll
+
+import 
org.grails.orm.hibernate.cfg.domainbinding.binder.CompositeIdentifierToManyToOneBinder
+import 
org.grails.orm.hibernate.cfg.domainbinding.binder.ForeignKeyOneToOneBinder
+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.util.SimpleValueColumnFetcher
+
+class ForeignKeyOneToOneBinderSpec extends HibernateGormDatastoreSpec {
+
+    @Unroll
+    def "bind sets alternate unique key and column uniqueness for #scenario"() 
{
+        given:
+        def namingStrategy = Mock(PersistentEntityNamingStrategy)
+        def simpleValueBinder = Mock(SimpleValueBinder)
+        def manyToOneValuesBinder = Mock(ManyToOneValuesBinder)
+        def compositeBinder = Mock(CompositeIdentifierToManyToOneBinder)
+        def columnFetcher = Mock(SimpleValueColumnFetcher)
+
+        def manyToOneBinder = new ManyToOneBinder(
+                getGrailsDomainBinder().getMetadataBuildingContext(),
+                namingStrategy, simpleValueBinder, manyToOneValuesBinder, 
compositeBinder)
+        def binder = new ForeignKeyOneToOneBinder(manyToOneBinder, 
columnFetcher)
+
+        def property = Mock(TestFKOneToOne)
+        def mapping = new Mapping()
+        def refDomainClass = Mock(GrailsHibernatePersistentEntity) {
+            getMappedForm() >> mapping
+        }
+        def propertyConfig = Mock(PropertyConfig)
+        def column = new Column('test')
+        def inverseSide = Mock(TestFKOneToOne)
+
+        property.getHibernateAssociatedEntity() >> refDomainClass
+        mapping.setIdentity(null)
+        property.getMappedForm() >> propertyConfig
+        columnFetcher.getColumnForSimpleValue(_ as ManyToOne) >> column
+
+        propertyConfig.isUnique() >> isUnique
+        propertyConfig.isUniqueWithinGroup() >> isUniqueWithinGroup
+        property.isBidirectional() >> isBidirectional
+        property.getHibernateInverseSide() >> inverseSide
+        inverseSide.isValidHibernateOneToOne() >> isInverseHasOne
+
+        when:
+        def result = binder.bind(property, null, "/test")
+
+        then:
+        result.isAlternateUniqueKey()
+        if (expectedUniqueValue != null) {
+            assert column.isUnique() == expectedUniqueValue
+        } else {
+            assert !column.isUnique()
+        }
+
+        where:
+        scenario                               | isUnique | 
isUniqueWithinGroup | isBidirectional | isInverseHasOne | expectedUniqueValue
+        "simple unique=true"                   | true     | false              
 | false           | false           | true
+        "simple unique=false"                  | false    | false              
 | false           | false           | false
+        "uniqueWithinGroup and bidirectional"  | false    | true               
 | true            | true            | true
+        "uniqueWithinGroup and unidirectional" | false    | true               
 | false           | false           | null
+        "uniqueWithinGroup and not hasOne"     | false    | true               
 | true            | false           | null
+    }
+
+    def "bind throws MappingException when column is not found"() {
+        given:
+        def namingStrategy = Mock(PersistentEntityNamingStrategy)
+        def simpleValueBinder = Mock(SimpleValueBinder)
+        def manyToOneValuesBinder = Mock(ManyToOneValuesBinder)
+        def compositeBinder = Mock(CompositeIdentifierToManyToOneBinder)
+        def columnFetcher = Mock(SimpleValueColumnFetcher)
+
+        def manyToOneBinder = new ManyToOneBinder(
+                getGrailsDomainBinder().getMetadataBuildingContext(),
+                namingStrategy, simpleValueBinder, manyToOneValuesBinder, 
compositeBinder)
+        def binder = new ForeignKeyOneToOneBinder(manyToOneBinder, 
columnFetcher)
+
+        def property = Mock(TestFKOneToOne)
+        def mapping = new Mapping()
+        def refDomainClass = Mock(GrailsHibernatePersistentEntity) {
+            getMappedForm() >> mapping
+        }
+        def propertyConfig = new PropertyConfig()
+
+        property.getHibernateAssociatedEntity() >> refDomainClass
+        mapping.setIdentity(null)
+        property.getMappedForm() >> propertyConfig
+        columnFetcher.getColumnForSimpleValue(_ as ManyToOne) >> null
+
+        when:
+        binder.bind(property, null, "/test")
+
+        then:
+        thrown(MappingException)
+    }
+}
+
+abstract class TestFKOneToOne extends HibernateOneToOneProperty {
+    TestFKOneToOne(PersistentEntity owner, MappingContext context, 
java.beans.PropertyDescriptor descriptor) {
+        super(owner, context, descriptor)
+    }
+}
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/GrailsPropertyBinderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/GrailsPropertyBinderSpec.groovy
index 4c01fa8d17..563d7d768b 100644
--- 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/GrailsPropertyBinderSpec.groovy
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/GrailsPropertyBinderSpec.groovy
@@ -26,6 +26,7 @@ import 
org.grails.orm.hibernate.cfg.domainbinding.binder.ComponentBinder
 import org.grails.orm.hibernate.cfg.domainbinding.binder.ComponentUpdater
 import org.grails.orm.hibernate.cfg.domainbinding.binder.EnumTypeBinder
 import org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsPropertyBinder
+import 
org.grails.orm.hibernate.cfg.domainbinding.binder.ForeignKeyOneToOneBinder
 import org.grails.orm.hibernate.cfg.domainbinding.binder.ManyToOneBinder
 import org.grails.orm.hibernate.cfg.domainbinding.binder.OneToOneBinder
 import org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueBinder
@@ -136,7 +137,8 @@ class GrailsPropertyBinderSpec extends 
HibernateGormDatastoreSpec {
                 simpleValueBinder
         )
         OneToOneBinder oneToOneBinder = new 
OneToOneBinder(metadataBuildingContext, simpleValueBinder)
-        ManyToOneBinder manyToOneBinder = new 
ManyToOneBinder(metadataBuildingContext, namingStrategy, simpleValueBinder, new 
ManyToOneValuesBinder(), compositeIdentifierToManyToOneBinder, 
simpleValueColumnFetcher)
+        ManyToOneBinder manyToOneBinder = new 
ManyToOneBinder(metadataBuildingContext, namingStrategy, simpleValueBinder, new 
ManyToOneValuesBinder(), compositeIdentifierToManyToOneBinder)
+        ForeignKeyOneToOneBinder foreignKeyOneToOneBinder = new 
ForeignKeyOneToOneBinder(manyToOneBinder, simpleValueColumnFetcher)
 
         CollectionBinder collectionBinder = new CollectionBinder(
                 metadataBuildingContext,
@@ -168,7 +170,8 @@ class GrailsPropertyBinderSpec extends 
HibernateGormDatastoreSpec {
                 simpleValueBinder
                 ,
                 oneToOneBinder,
-                manyToOneBinder
+                manyToOneBinder,
+                foreignKeyOneToOneBinder
 
         )
         componentBinder.setGrailsPropertyBinder(propertyBinder)
@@ -507,6 +510,7 @@ class GrailsPropertyBinderSpec extends 
HibernateGormDatastoreSpec {
         def columnNameForPropertyAndPathFetcher = 
Mock(ColumnNameForPropertyAndPathFetcher)
         def oneToOneBinder = Mock(OneToOneBinder)
         def manyToOneBinder = Mock(ManyToOneBinder)
+        def foreignKeyOneToOneBinder = Mock(ForeignKeyOneToOneBinder)
 
         // Instantiate GrailsPropertyBinder using the protected constructor 
with necessary mocks
         def propertyBinder = new GrailsPropertyBinder(
@@ -518,7 +522,8 @@ class GrailsPropertyBinderSpec extends 
HibernateGormDatastoreSpec {
                 simpleValueBinder
                 ,
                 oneToOneBinder,
-                manyToOneBinder
+                manyToOneBinder,
+                foreignKeyOneToOneBinder
 
         )
 
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 e358200774..15a7bbae11 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
@@ -1,7 +1,6 @@
 package org.grails.orm.hibernate.cfg.domainbinding
 
 import grails.gorm.specs.HibernateGormDatastoreSpec
-import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToOneProperty
 import org.grails.orm.hibernate.cfg.CompositeIdentity
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty
@@ -10,9 +9,6 @@ import 
org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy
 import org.grails.orm.hibernate.cfg.PropertyConfig
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToManyProperty
 import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToOneProperty
-import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateOneToOneProperty
-import org.hibernate.MappingException
-import org.hibernate.mapping.Column
 import org.hibernate.mapping.ManyToOne
 import spock.lang.Unroll
 
@@ -31,9 +27,8 @@ class ManyToOneBinderSpec extends HibernateGormDatastoreSpec {
         def simpleValueBinder = Mock(SimpleValueBinder)
         def manyToOneValuesBinder = Mock(ManyToOneValuesBinder)
         def compositeBinder = Mock(CompositeIdentifierToManyToOneBinder)
-        def columnFetcher = Mock(SimpleValueColumnFetcher)
 
-        def binder = new 
ManyToOneBinder(getGrailsDomainBinder().getMetadataBuildingContext(), 
namingStrategy, simpleValueBinder, manyToOneValuesBinder, compositeBinder, 
columnFetcher)
+        def binder = new 
ManyToOneBinder(getGrailsDomainBinder().getMetadataBuildingContext(), 
namingStrategy, simpleValueBinder, manyToOneValuesBinder, compositeBinder)
 
         def association = Mock(HibernateManyToOneProperty)
         def path = "/test"
@@ -68,9 +63,8 @@ class ManyToOneBinderSpec extends HibernateGormDatastoreSpec {
         def simpleValueBinder = Mock(SimpleValueBinder)
         def manyToOneValuesBinder = Mock(ManyToOneValuesBinder)
         def compositeBinder = Mock(CompositeIdentifierToManyToOneBinder)
-        def columnFetcher = Mock(SimpleValueColumnFetcher)
 
-        def binder = new 
ManyToOneBinder(getGrailsDomainBinder().getMetadataBuildingContext(), 
namingStrategy, simpleValueBinder, manyToOneValuesBinder, compositeBinder, 
columnFetcher)
+        def binder = new 
ManyToOneBinder(getGrailsDomainBinder().getMetadataBuildingContext(), 
namingStrategy, simpleValueBinder, manyToOneValuesBinder, compositeBinder)
 
         def property = Mock(HibernateManyToManyProperty)
         def mapping = new Mapping()
@@ -98,90 +92,4 @@ class ManyToOneBinderSpec extends HibernateGormDatastoreSpec 
{
         resultConfig != null
         resultConfig.getJoinTable().getKey().getName() == "my_circular_prop_id"
     }
-
-    @Unroll
-    def "Test one-to-one binding with uniqueWithinGroup constraint for 
#scenario"() {
-        given:
-        def namingStrategy = Mock(PersistentEntityNamingStrategy)
-        def simpleValueBinder = Mock(SimpleValueBinder)
-        def manyToOneValuesBinder = Mock(ManyToOneValuesBinder)
-        def compositeBinder = Mock(CompositeIdentifierToManyToOneBinder)
-        def columnFetcher = Mock(SimpleValueColumnFetcher)
-
-        def binder = new 
ManyToOneBinder(getGrailsDomainBinder().getMetadataBuildingContext(), 
namingStrategy, simpleValueBinder, manyToOneValuesBinder, compositeBinder, 
columnFetcher)
-
-        def property = Mock(HibernateOneToOneProperty)
-        def mapping = new Mapping()
-        def refDomainClass = Mock(GrailsHibernatePersistentEntity) {
-            getMappedForm() >> mapping
-        }
-        def propertyConfig = Mock(PropertyConfig)
-        def column = new Column('test')
-        def inverseSide = Mock(TestOneToOneInverse)
-
-        property.getHibernateAssociatedEntity() >> refDomainClass
-        mapping.setIdentity(null)
-        property.getMappedForm() >> propertyConfig
-        columnFetcher.getColumnForSimpleValue(_ as ManyToOne) >> column
-
-        propertyConfig.isUnique() >> isUnique
-        propertyConfig.isUniqueWithinGroup() >> isUniqueWithinGroup
-        property.isBidirectional() >> isBidirectional
-        property.getHibernateInverseSide() >> inverseSide
-        inverseSide.isValidHibernateOneToOne() >> isInverseHasOne
-
-        when:
-        def result = binder.bindManyToOne(property, null, "/test")
-
-        then:
-        result.isAlternateUniqueKey()
-        if (expectedUniqueValue != null) {
-            assert column.isUnique() == expectedUniqueValue
-        } else {
-            assert !column.isUnique()
-        }
-
-        where:
-        scenario                               | isUnique | 
isUniqueWithinGroup | isBidirectional | isInverseHasOne | expectedUniqueValue
-        "simple unique=true"                   | true     | false              
 | false           | false           | true
-        "simple unique=false"                  | false    | false              
 | false           | false           | false
-        "uniqueWithinGroup and bidirectional"  | false    | true               
 | true            | true            | true
-        "uniqueWithinGroup and unidirectional" | false    | true               
 | false           | false           | null
-        "uniqueWithinGroup and not hasOne"     | false    | true               
 | true            | false           | null
-    }
-
-    def "Test one-to-one binding throws exception when column is not found"() {
-        given:
-        def namingStrategy = Mock(PersistentEntityNamingStrategy)
-        def simpleValueBinder = Mock(SimpleValueBinder)
-        def manyToOneValuesBinder = Mock(ManyToOneValuesBinder)
-        def compositeBinder = Mock(CompositeIdentifierToManyToOneBinder)
-        def columnFetcher = Mock(SimpleValueColumnFetcher)
-
-        def binder = new 
ManyToOneBinder(getGrailsDomainBinder().getMetadataBuildingContext(), 
namingStrategy, simpleValueBinder, manyToOneValuesBinder, compositeBinder, 
columnFetcher)
-
-        def property = Mock(HibernateOneToOneProperty)
-        def mapping = new Mapping()
-        def refDomainClass = Mock(GrailsHibernatePersistentEntity) {
-            getMappedForm() >> mapping
-        }
-        def propertyConfig = new PropertyConfig()
-
-        property.getHibernateAssociatedEntity() >> refDomainClass
-        mapping.setIdentity(null)
-        property.getMappedForm() >> propertyConfig
-        columnFetcher.getColumnForSimpleValue(_ as ManyToOne) >> null
-
-        when:
-        binder.bindManyToOne(property, null, "/test")
-
-        then:
-        thrown(MappingException)
-    }
-}
-
-abstract class TestOneToOneInverse extends HibernateOneToOneProperty {
-    TestOneToOneInverse(org.grails.datastore.mapping.model.PersistentEntity 
owner, org.grails.datastore.mapping.model.MappingContext context, 
java.beans.PropertyDescriptor descriptor) {
-        super(owner, context, descriptor)
-    }
 }
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 38eb6c4761..76c011d992 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
@@ -32,7 +32,7 @@ class CollectionSecondPassBinderSpec extends 
HibernateGormDatastoreSpec {
         def svb = new SimpleValueBinder(mbc, ns, je)
         def svcf = new SimpleValueColumnFetcher()
         def citmto = new CompositeIdentifierToManyToOneBinder(mbc, ns, je)
-        def mtob = new ManyToOneBinder(mbc, ns, svb, new 
ManyToOneValuesBinder(), citmto, svcf)
+        def mtob = new ManyToOneBinder(mbc, ns, svb, new 
ManyToOneValuesBinder(), citmto)
         def pkvc = new PrimaryKeyValueCreator(mbc)
         def cku = new CollectionKeyColumnUpdater()
         def botml = new BidirectionalOneToManyLinker(new 
org.grails.orm.hibernate.cfg.domainbinding.util.GrailsPropertyResolver())
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPassBinderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPassBinderSpec.groovy
index 5266866570..19a63afb81 100644
--- 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPassBinderSpec.groovy
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPassBinderSpec.groovy
@@ -10,6 +10,7 @@ import 
org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy
 import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment
 import org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueBinder
 import org.grails.orm.hibernate.cfg.domainbinding.binder.EnumTypeBinder
+import 
org.grails.orm.hibernate.cfg.domainbinding.binder.ForeignKeyOneToOneBinder
 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.CompositeIdentifierToManyToOneBinder
@@ -70,7 +71,8 @@ class ListSecondPassBinderSpec extends 
HibernateGormDatastoreSpec {
                 simpleValueBinder
         )
         OneToOneBinder oneToOneBinder = new 
OneToOneBinder(metadataBuildingContext, simpleValueBinder)
-        ManyToOneBinder manyToOneBinder = new 
ManyToOneBinder(metadataBuildingContext, namingStrategy, simpleValueBinder, new 
ManyToOneValuesBinder(), compositeIdentifierToManyToOneBinder, 
simpleValueColumnFetcher)
+        ManyToOneBinder manyToOneBinder = new 
ManyToOneBinder(metadataBuildingContext, namingStrategy, simpleValueBinder, new 
ManyToOneValuesBinder(), compositeIdentifierToManyToOneBinder)
+        ForeignKeyOneToOneBinder foreignKeyOneToOneBinder = new 
ForeignKeyOneToOneBinder(manyToOneBinder, simpleValueColumnFetcher)
 
         CollectionBinder collectionBinder = new CollectionBinder(
                 metadataBuildingContext,
@@ -103,7 +105,8 @@ class ListSecondPassBinderSpec extends 
HibernateGormDatastoreSpec {
                 simpleValueBinder
                 ,
                 oneToOneBinder,
-                manyToOneBinder
+                manyToOneBinder,
+                foreignKeyOneToOneBinder
 
         )
         CompositeIdBinder compositeIdBinder = new 
CompositeIdBinder(metadataBuildingContext, componentUpdater, propertyBinder)
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/MapSecondPassBinderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/MapSecondPassBinderSpec.groovy
index 9b1c4d1b53..cdff3a07c2 100644
--- 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/MapSecondPassBinderSpec.groovy
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/MapSecondPassBinderSpec.groovy
@@ -10,6 +10,7 @@ import 
org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy
 import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment
 import org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueBinder
 import org.grails.orm.hibernate.cfg.domainbinding.binder.EnumTypeBinder
+import 
org.grails.orm.hibernate.cfg.domainbinding.binder.ForeignKeyOneToOneBinder
 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.CompositeIdentifierToManyToOneBinder
@@ -70,7 +71,8 @@ class MapSecondPassBinderSpec extends 
HibernateGormDatastoreSpec {
                 simpleValueBinder
         )
         OneToOneBinder oneToOneBinder = new 
OneToOneBinder(metadataBuildingContext, simpleValueBinder)
-        ManyToOneBinder manyToOneBinder = new 
ManyToOneBinder(metadataBuildingContext, namingStrategy, simpleValueBinder, new 
ManyToOneValuesBinder(), compositeIdentifierToManyToOneBinder, 
simpleValueColumnFetcher)
+        ManyToOneBinder manyToOneBinder = new 
ManyToOneBinder(metadataBuildingContext, namingStrategy, simpleValueBinder, new 
ManyToOneValuesBinder(), compositeIdentifierToManyToOneBinder)
+        ForeignKeyOneToOneBinder foreignKeyOneToOneBinder = new 
ForeignKeyOneToOneBinder(manyToOneBinder, simpleValueColumnFetcher)
 
         CollectionBinder collectionBinder = new CollectionBinder(
                 metadataBuildingContext,
@@ -103,7 +105,8 @@ class MapSecondPassBinderSpec extends 
HibernateGormDatastoreSpec {
                 simpleValueBinder
                 ,
                 oneToOneBinder,
-                manyToOneBinder
+                manyToOneBinder,
+                foreignKeyOneToOneBinder
 
         )
         CompositeIdBinder compositeIdBinder = new 
CompositeIdBinder(metadataBuildingContext, componentUpdater, propertyBinder)


Reply via email to