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 d35d831b72a0c4d8f3c359e2e99b98b7655905c1 Author: Walter Duque de Estrada <[email protected]> AuthorDate: Mon Mar 2 08:26:15 2026 -0600 Refactor CollectionSecondPassBinder and ManyToOneBinder for typed dispatch Extract bindCollectionSecondPass into focused private methods: - bindOneToManyAssociation: handles OneToMany collection setup - applyMultiTenantFilter: applies tenant filter condition - bindCollectionKey: handles bidirectional/unidirectional key binding - bindCollectionElement: dispatches element binding by property type - bindManyToManyElement: typed ManyToMany element binding - bindBidirectionalMapElement: typed bidirectional map element binding Replace ManyToOneBinder.bindManyToOne(HibernateAssociation) with two typed overloads that eliminate instanceof guards: - bindManyToOne(HibernateToOneProperty): handles direct to-one associations; extracts bindOneToOneUniqueKey for the OneToOne case - bindManyToOne(HibernateManyToManyProperty): handles inverse side of ManyToMany; extracts prepareCircularManyToMany Add HibernateManyToManyProperty.getHibernateInverseSide() covariant override returning HibernateManyToManyProperty, enabling CollectionSecondPassBinder to call the typed overload without a cast. Update ManyToOneBinderSpec to call typed overloads directly (removing 'as HibernateAssociation' casts). Co-authored-by: Copilot <[email protected]> --- .../cfg/domainbinding/binder/ManyToOneBinder.java | 114 ++++++++++++--------- .../hibernate/HibernateManyToManyProperty.java | 5 + .../secondpass/CollectionSecondPassBinder.java | 113 +++++++++++++------- .../cfg/domainbinding/ManyToOneBinderSpec.groovy | 13 ++- 4 files changed, 152 insertions(+), 93 deletions(-) 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 f24160a3a3..f0cde199da 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 @@ -77,66 +77,82 @@ public class ManyToOneBinder { new SimpleValueColumnFetcher()); } - /** Binds a many-to-one relationship to the */ - @SuppressWarnings("unchecked") + /** Binds a to-one (many-to-one or one-to-one) association. */ + public ManyToOne bindManyToOne( + HibernateToOneProperty 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 (property instanceof HibernateOneToOneProperty oneToOne && !isComposite) { + bindOneToOneUniqueKey(oneToOne, manyToOne); + } + return manyToOne; + } + + /** Binds the inverse side of a many-to-many association as a collection element. */ public ManyToOne bindManyToOne( - HibernateAssociation property, org.hibernate.mapping.Table table, String path) { + HibernateManyToManyProperty property, org.hibernate.mapping.Table table, String path) { + GrailsHibernatePersistentEntity refDomainClass = property.getHibernateOwner(); + boolean isComposite = isCompositeIdentifier(refDomainClass); + if (!isComposite && property.isCircular()) { + prepareCircularManyToMany(property, refDomainClass.getMappedForm()); + } + return doBind(property, refDomainClass, isComposite, table, path); + } + + private static boolean isCompositeIdentifier(GrailsHibernatePersistentEntity entity) { + Mapping mapping = entity.getMappedForm(); + return mapping != null && mapping.hasCompositeIdentifier(); + } + + @SuppressWarnings("unchecked") + private ManyToOne doBind( + HibernateAssociation property, + GrailsHibernatePersistentEntity refDomainClass, + boolean isComposite, + org.hibernate.mapping.Table table, + String path) { ManyToOne manyToOne = new ManyToOne(metadataBuildingContext, table); manyToOneValuesBinder.bindManyToOneValues(property, manyToOne); - GrailsHibernatePersistentEntity refDomainClass = - (property instanceof HibernateManyToManyProperty - ? property.getHibernateOwner() - : property.getHibernateAssociatedEntity()); - Mapping mapping = refDomainClass.getMappedForm(); - - boolean isComposite = mapping != null && mapping.hasCompositeIdentifier(); if (isComposite) { + Mapping mapping = refDomainClass.getMappedForm(); CompositeIdentity ci = (CompositeIdentity) mapping.getIdentity(); compositeIdentifierToManyToOneBinder.bindCompositeIdentifierToManyToOne( property, manyToOne, ci, refDomainClass, path); } else { - if (property.isCircular() && (property instanceof HibernateManyToManyProperty)) { - PropertyConfig pc = property.getMappedForm(); - - if (mapping != null && pc.getColumns().isEmpty()) { - mapping.getColumns().put(property.getName(), pc); - } - if (!pc.hasJoinKeyMapping()) { - JoinTable jt = new JoinTable(); - final ColumnConfig columnConfig = new ColumnConfig(); - columnConfig.setName( - namingStrategy.resolveColumnName(property.getName()) + FOREIGN_KEY_SUFFIX); - jt.setKey(columnConfig); - pc.setJoinTable(jt); - } - // set type - simpleValueBinder.bindSimpleValue(property, null, manyToOne, path); - } else { - // bind column - // set type - simpleValueBinder.bindSimpleValue(property, null, manyToOne, path); - } + simpleValueBinder.bindSimpleValue(property, null, manyToOne, path); } + return manyToOne; + } + private void bindOneToOneUniqueKey(HibernateOneToOneProperty property, ManyToOne manyToOne) { PropertyConfig config = property.getMappedForm(); - boolean isOneToOne = property instanceof HibernateOneToOneProperty; - boolean notComposite = !isComposite; - if (isOneToOne && notComposite) { - 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() instanceof HibernateToOneProperty inverseSide - && inverseSide.isHibernateOneToOne()) { - c.setUnique(true); - } - } + 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() instanceof HibernateToOneProperty inverseSide + && inverseSide.isHibernateOneToOne()) { + c.setUnique(true); + } + } + + private void prepareCircularManyToMany(HibernateManyToManyProperty property, Mapping mapping) { + PropertyConfig pc = property.getMappedForm(); + if (mapping != null && pc.getColumns().isEmpty()) { + mapping.getColumns().put(property.getName(), pc); + } + if (!pc.hasJoinKeyMapping()) { + JoinTable jt = new JoinTable(); + ColumnConfig columnConfig = new ColumnConfig(); + columnConfig.setName( + namingStrategy.resolveColumnName(property.getName()) + FOREIGN_KEY_SUFFIX); + jt.setKey(columnConfig); + pc.setJoinTable(jt); } - return manyToOne; } } diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateManyToManyProperty.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateManyToManyProperty.java index 947ce10fd7..d5d57d6e54 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateManyToManyProperty.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateManyToManyProperty.java @@ -36,4 +36,9 @@ public class HibernateManyToManyProperty extends ManyToManyWithMapping<PropertyC public GrailsHibernatePersistentEntity getHibernateAssociatedEntity() { return (GrailsHibernatePersistentEntity) super.getAssociatedEntity(); } + + @Override + public HibernateManyToManyProperty getHibernateInverseSide() { + return (HibernateManyToManyProperty) getInverseSide(); + } } diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java index 2d0f524947..cb3ce556f7 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java @@ -33,6 +33,7 @@ import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToManyP import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateOneToManyProperty; import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty; import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyProperty; +import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToOneProperty; import org.grails.orm.hibernate.cfg.domainbinding.util.DefaultColumnNameFetcher; import org.grails.orm.hibernate.cfg.domainbinding.util.OrderByClauseBuilder; import org.hibernate.MappingException; @@ -93,16 +94,41 @@ public class CollectionSecondPassBinder { @Nonnull Collection collection) { PersistentClass associatedClass = bindOrderBy(property, collection, persistentClasses); + bindOneToManyAssociation(property, associatedClass, collection); - if (collection.isOneToMany()) { - OneToMany oneToMany = (OneToMany) collection.getElement(); - oneToMany.setAssociatedClass(associatedClass); - if (property.shouldBindWithForeignKey()) { - collection.setCollectionTable(associatedClass.getTable()); - } - collectionForPropertyConfigBinder.bindCollectionForPropertyConfig(collection, property); + applyMultiTenantFilter(property, collection); + + if (property.isSorted()) { + collection.setSorted(true); + } + + DependantValue key = primaryKeyValueCreator.createPrimaryKeyValue(collection); + collection.setKey(key); + bindCollectionKey(property, key, associatedClass, collection); + + Optional.ofNullable(property.getMappedForm().getCache()) + .ifPresent(cacheConfig -> collection.setCacheConcurrencyStrategy(cacheConfig.getUsage())); + + bindCollectionElement(property, mappings, collection); + collectionKeyColumnUpdater.forceNullableAndCheckUpdatable(key, property); + } + + private void bindOneToManyAssociation( + HibernateToManyProperty property, + PersistentClass associatedClass, + Collection collection) { + if (!collection.isOneToMany()) { + return; + } + OneToMany oneToMany = (OneToMany) collection.getElement(); + oneToMany.setAssociatedClass(associatedClass); + if (property.shouldBindWithForeignKey()) { + collection.setCollectionTable(associatedClass.getTable()); } + collectionForPropertyConfigBinder.bindCollectionForPropertyConfig(collection, property); + } + private void applyMultiTenantFilter(HibernateToManyProperty property, Collection collection) { Optional.ofNullable(property.getHibernateAssociatedEntity()) .filter( referenced -> @@ -126,21 +152,19 @@ public class CollectionSecondPassBinder { Collections.emptyMap()); } }); + } - if (property.isSorted()) { - collection.setSorted(true); - } - - DependantValue key = primaryKeyValueCreator.createPrimaryKeyValue(collection); - collection.setKey(key); - + private void bindCollectionKey( + HibernateToManyProperty property, + DependantValue key, + PersistentClass associatedClass, + Collection collection) { if (property.isBidirectional()) { - if (property.getHibernateInverseSide() - instanceof org.grails.datastore.mapping.model.types.ToOne + var inverseSide = property.getHibernateInverseSide(); + if (inverseSide instanceof org.grails.datastore.mapping.model.types.ToOne && property.shouldBindWithForeignKey()) { - bidirectionalOneToManyLinker.link( - collection, associatedClass, key, property.getHibernateInverseSide()); - } else if (property.getHibernateInverseSide() instanceof HibernateManyToManyProperty + bidirectionalOneToManyLinker.link(collection, associatedClass, key, inverseSide); + } else if (inverseSide instanceof HibernateManyToManyProperty || java.util.Map.class.isAssignableFrom(property.getType())) { dependentKeyValueBinder.bind(property, key); } @@ -152,29 +176,44 @@ public class CollectionSecondPassBinder { dependentKeyValueBinder.bind(property, key); } } + } - Optional.ofNullable(property.getMappedForm().getCache()) - .ifPresent(cacheConfig -> collection.setCacheConcurrencyStrategy(cacheConfig.getUsage())); - - if (property instanceof HibernateManyToManyProperty || property.isBidirectionalOneToManyMap()) { - if (property.isBidirectional()) { - var otherSide = property.getHibernateInverseSide(); - ManyToOne element = - manyToOneBinder.bindManyToOne(otherSide, collection.getCollectionTable(), EMPTY_PATH); - element.setReferencedEntityName(otherSide.getOwner().getName()); - collection.setElement(element); - collectionForPropertyConfigBinder.bindCollectionForPropertyConfig(collection, property); - if (property.isCircular()) { - collection.setInverse(false); - } - } + private void bindCollectionElement( + HibernateToManyProperty property, + InFlightMetadataCollector mappings, + Collection collection) { + if (property instanceof HibernateManyToManyProperty manyToMany && manyToMany.isBidirectional()) { + bindManyToManyElement(manyToMany, collection); + } else if (property.isBidirectionalOneToManyMap() && property.isBidirectional()) { + bindBidirectionalMapElement(property, collection); } else if (property.isUnidirectionalOneToMany()) { - unidirectionalOneToManyBinder.bind( - (HibernateOneToManyProperty) property, collection); + unidirectionalOneToManyBinder.bind((HibernateOneToManyProperty) property, collection); } else if (property.supportsJoinColumnMapping()) { collectionWithJoinTableBinder.bindCollectionWithJoinTable(property, mappings, collection); } - collectionKeyColumnUpdater.forceNullableAndCheckUpdatable(key, property); + } + + private void bindManyToManyElement( + HibernateManyToManyProperty manyToMany, Collection collection) { + HibernateManyToManyProperty otherSide = manyToMany.getHibernateInverseSide(); + ManyToOne element = + manyToOneBinder.bindManyToOne(otherSide, collection.getCollectionTable(), EMPTY_PATH); + element.setReferencedEntityName(otherSide.getOwner().getName()); + collection.setElement(element); + collectionForPropertyConfigBinder.bindCollectionForPropertyConfig(collection, manyToMany); + if (manyToMany.isCircular()) { + collection.setInverse(false); + } + } + + private void bindBidirectionalMapElement( + HibernateToManyProperty property, Collection collection) { + HibernateToOneProperty otherSide = (HibernateToOneProperty) property.getHibernateInverseSide(); + ManyToOne element = + manyToOneBinder.bindManyToOne(otherSide, collection.getCollectionTable(), EMPTY_PATH); + element.setReferencedEntityName(otherSide.getOwner().getName()); + collection.setElement(element); + collectionForPropertyConfigBinder.bindCollectionForPropertyConfig(collection, property); } PersistentClass bindOrderBy( 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 7b54f81ad3..789054f2e7 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.HibernateAssociation 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 @@ -49,11 +48,11 @@ class ManyToOneBinderSpec extends HibernateGormDatastoreSpec { mapping.setIdentity(hasCompositeId ? new CompositeIdentity() : null) when: - def result = binder.bindManyToOne(association as HibernateAssociation, null, path) + def result = binder.bindManyToOne(association, null, path) then: result instanceof ManyToOne - 1 * manyToOneValuesBinder.bindManyToOneValues(association as HibernateAssociation, _ as ManyToOne) + 1 * manyToOneValuesBinder.bindManyToOneValues(association, _ as ManyToOne) compositeBinderCalls * compositeBinder.bindCompositeIdentifierToManyToOne(association as HibernatePersistentProperty, _ as ManyToOne, _, refDomainClass, path) simpleValueBinderCalls * simpleValueBinder.bindSimpleValue(association as HibernatePersistentProperty, null, _ as ManyToOne, path) @@ -89,11 +88,11 @@ class ManyToOneBinderSpec extends HibernateGormDatastoreSpec { namingStrategy.resolveColumnName("myCircularProp") >> "my_circular_prop" when: - def result = binder.bindManyToOne(property as HibernateAssociation, null, "/test") + def result = binder.bindManyToOne(property, null, "/test") then: result instanceof ManyToOne - 1 * manyToOneValuesBinder.bindManyToOneValues(property as HibernateAssociation, _ as ManyToOne) + 1 * manyToOneValuesBinder.bindManyToOneValues(property, _ as ManyToOne) 1 * simpleValueBinder.bindSimpleValue(property as HibernatePersistentProperty, null, _ as ManyToOne, "/test") def resultConfig = mapping.getColumns().get("myCircularProp") resultConfig != null @@ -132,7 +131,7 @@ class ManyToOneBinderSpec extends HibernateGormDatastoreSpec { inverseSide.isHibernateOneToOne() >> isInverseHasOne when: - def result = binder.bindManyToOne(property as HibernateAssociation, null, "/test") + def result = binder.bindManyToOne(property, null, "/test") then: result.isAlternateUniqueKey() @@ -174,7 +173,7 @@ class ManyToOneBinderSpec extends HibernateGormDatastoreSpec { columnFetcher.getColumnForSimpleValue(_ as ManyToOne) >> null when: - binder.bindManyToOne(property as HibernateAssociation, null, "/test") + binder.bindManyToOne(property, null, "/test") then: thrown(MappingException)
