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
