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)
