You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by bo...@apache.org on 2026/03/03 23:27:25 UTC
(grails-core) 18/24: refactor: extract bindManyToManyElement into ManyToManyElementBinder
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 6cf5923907b2dc2da036679aab3b791a42c7aaa4
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Tue Mar 3 11:57:26 2026 -0600
refactor: extract bindManyToManyElement into ManyToManyElementBinder
---
.../cfg/domainbinding/binder/CollectionBinder.java | 3 +-
.../secondpass/CollectionSecondPassBinder.java | 24 +-----
.../secondpass/ManyToManyElementBinder.java | 55 ++++++++++++
.../CollectionSecondPassBinderSpec.groovy | 2 +-
.../secondpass/ManyToManyElementBinderSpec.groovy | 99 ++++++++++++++++++++++
5 files changed, 161 insertions(+), 22 deletions(-)
diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CollectionBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CollectionBinder.java
index 0ddbbfb8f8..2a9fa23bcc 100644
--- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CollectionBinder.java
+++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CollectionBinder.java
@@ -32,6 +32,7 @@ import org.grails.orm.hibernate.cfg.domainbinding.secondpass.BidirectionalOneToM
import org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionKeyColumnUpdater;
import org.grails.orm.hibernate.cfg.domainbinding.secondpass.BidirectionalMapElementBinder;
import org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionKeyBinder;
+import org.grails.orm.hibernate.cfg.domainbinding.secondpass.ManyToManyElementBinder;
import org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionMultiTenantFilterBinder;
import org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionOrderByBinder;
import org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionSecondPassBinder;
@@ -109,7 +110,6 @@ public class CollectionBinder {
new ColumnConfigToColumnBinder());
this.collectionSecondPassBinder =
new CollectionSecondPassBinder(
- manyToOneBinder,
new PrimaryKeyValueCreator(metadataBuildingContext),
new CollectionKeyColumnUpdater(),
new UnidirectionalOneToManyBinder(collectionWithJoinTableBinder, mappings),
@@ -120,6 +120,7 @@ public class CollectionBinder {
new DependentKeyValueBinder(simpleValueBinder, compositeIdentifierToManyToOneBinder),
simpleValueColumnBinder),
new BidirectionalMapElementBinder(manyToOneBinder, collectionForPropertyConfigBinder),
+ new ManyToManyElementBinder(manyToOneBinder, collectionForPropertyConfigBinder),
new CollectionOrderByBinder(),
new CollectionMultiTenantFilterBinder(new DefaultColumnNameFetcher(namingStrategy)));
this.listSecondPassBinder =
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 ee69807823..501f49d55b 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
@@ -18,13 +18,10 @@
*/
package org.grails.orm.hibernate.cfg.domainbinding.secondpass;
-import static org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsDomainBinder.*;
-
import jakarta.annotation.Nonnull;
import java.util.*;
import java.util.Map;
import org.grails.orm.hibernate.cfg.domainbinding.binder.CollectionForPropertyConfigBinder;
-import org.grails.orm.hibernate.cfg.domainbinding.binder.ManyToOneBinder;
import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToManyProperty;
import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateOneToManyProperty;
import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyProperty;
@@ -40,7 +37,7 @@ public class CollectionSecondPassBinder {
private final CollectionMultiTenantFilterBinder collectionMultiTenantFilterBinder;
private final CollectionKeyBinder collectionKeyBinder;
private final BidirectionalMapElementBinder bidirectionalMapElementBinder;
- private final ManyToOneBinder manyToOneBinder;
+ private final ManyToManyElementBinder manyToManyElementBinder;
private final PrimaryKeyValueCreator primaryKeyValueCreator;
private final CollectionKeyColumnUpdater collectionKeyColumnUpdater;
private final UnidirectionalOneToManyBinder unidirectionalOneToManyBinder;
@@ -49,7 +46,6 @@ public class CollectionSecondPassBinder {
/** Creates a new {@link CollectionSecondPassBinder} instance. */
public CollectionSecondPassBinder(
- ManyToOneBinder manyToOneBinder,
PrimaryKeyValueCreator primaryKeyValueCreator,
CollectionKeyColumnUpdater collectionKeyColumnUpdater,
UnidirectionalOneToManyBinder unidirectionalOneToManyBinder,
@@ -57,9 +53,9 @@ public class CollectionSecondPassBinder {
CollectionForPropertyConfigBinder collectionForPropertyConfigBinder,
CollectionKeyBinder collectionKeyBinder,
BidirectionalMapElementBinder bidirectionalMapElementBinder,
+ ManyToManyElementBinder manyToManyElementBinder,
CollectionOrderByBinder collectionOrderByBinder,
CollectionMultiTenantFilterBinder collectionMultiTenantFilterBinder) {
- this.manyToOneBinder = manyToOneBinder;
this.primaryKeyValueCreator = primaryKeyValueCreator;
this.collectionKeyColumnUpdater = collectionKeyColumnUpdater;
this.unidirectionalOneToManyBinder = unidirectionalOneToManyBinder;
@@ -67,6 +63,7 @@ public class CollectionSecondPassBinder {
this.collectionForPropertyConfigBinder = collectionForPropertyConfigBinder;
this.collectionKeyBinder = collectionKeyBinder;
this.bidirectionalMapElementBinder = bidirectionalMapElementBinder;
+ this.manyToManyElementBinder = manyToManyElementBinder;
this.collectionOrderByBinder = collectionOrderByBinder;
this.collectionMultiTenantFilterBinder = collectionMultiTenantFilterBinder;
}
@@ -119,7 +116,7 @@ public class CollectionSecondPassBinder {
InFlightMetadataCollector mappings,
Collection collection) {
if (property instanceof HibernateManyToManyProperty manyToMany && manyToMany.isBidirectional()) {
- bindManyToManyElement(manyToMany, collection);
+ manyToManyElementBinder.bind(manyToMany, collection);
} else if (property.isBidirectionalOneToManyMap() && property.isBidirectional()) {
bidirectionalMapElementBinder.bind(property, collection);
} else if (property instanceof HibernateOneToManyProperty oneToManyProperty && oneToManyProperty.isUnidirectionalOneToMany()) {
@@ -129,19 +126,6 @@ public class CollectionSecondPassBinder {
}
}
- 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 @Nonnull PersistentClass resolveAssociatedClass(
HibernateToManyProperty property, Map<?, ?> persistentClasses) {
return Optional.ofNullable(property.getHibernateAssociatedEntity())
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
new file mode 100644
index 0000000000..9d4aa3654b
--- /dev/null
+++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ManyToManyElementBinder.java
@@ -0,0 +1,55 @@
+/*
+ * 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.secondpass;
+
+import static org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsDomainBinder.EMPTY_PATH;
+
+import org.grails.orm.hibernate.cfg.domainbinding.binder.CollectionForPropertyConfigBinder;
+import org.grails.orm.hibernate.cfg.domainbinding.binder.ManyToOneBinder;
+import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToManyProperty;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.ManyToOne;
+
+/** Binds the element of a bidirectional many-to-many association. */
+public class ManyToManyElementBinder {
+
+ private final ManyToOneBinder manyToOneBinder;
+ private final CollectionForPropertyConfigBinder collectionForPropertyConfigBinder;
+
+ /** Creates a new {@link ManyToManyElementBinder} instance. */
+ public ManyToManyElementBinder(
+ ManyToOneBinder manyToOneBinder,
+ CollectionForPropertyConfigBinder collectionForPropertyConfigBinder) {
+ this.manyToOneBinder = manyToOneBinder;
+ this.collectionForPropertyConfigBinder = collectionForPropertyConfigBinder;
+ }
+
+ /** Binds the ManyToOne element for a bidirectional many-to-many collection. */
+ public void bind(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);
+ }
+ }
+}
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 49f127415d..4aa8190304 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
@@ -41,7 +41,7 @@ class CollectionSecondPassBinderSpec extends HibernateGormDatastoreSpec {
def dcnf = new DefaultColumnNameFetcher(ns, new BackticksRemover())
def svcb = new SimpleValueColumnBinder()
- binder = new CollectionSecondPassBinder(mtob, pkvc, cku, uotmb, cwjtb, cfpcb, new CollectionKeyBinder(botml, dkvb, svcb), new BidirectionalMapElementBinder(mtob, cfpcb), new CollectionOrderByBinder(), new CollectionMultiTenantFilterBinder(dcnf))
+ binder = new CollectionSecondPassBinder(pkvc, cku, uotmb, cwjtb, cfpcb, new CollectionKeyBinder(botml, dkvb, svcb), new BidirectionalMapElementBinder(mtob, cfpcb), new ManyToManyElementBinder(mtob, cfpcb), new CollectionOrderByBinder(), new CollectionMultiTenantFilterBinder(dcnf))
}
protected HibernatePersistentProperty createTestHibernateToManyProperty(Class<?> domainClass = CSPBTestEntityWithMany, String propertyName = "items") {
diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ManyToManyElementBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ManyToManyElementBinderSpec.groovy
new file mode 100644
index 0000000000..af397f4a2a
--- /dev/null
+++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ManyToManyElementBinderSpec.groovy
@@ -0,0 +1,99 @@
+package org.grails.orm.hibernate.cfg.domainbinding.secondpass
+
+import grails.gorm.annotation.Entity
+import grails.gorm.specs.HibernateGormDatastoreSpec
+import org.grails.orm.hibernate.cfg.domainbinding.binder.CollectionForPropertyConfigBinder
+import org.grails.orm.hibernate.cfg.domainbinding.binder.CompositeIdentifierToManyToOneBinder
+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.hibernate.GrailsHibernatePersistentEntity
+import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToManyProperty
+import org.hibernate.mapping.Bag
+import org.hibernate.mapping.ManyToOne
+import org.hibernate.mapping.Table
+import spock.lang.Subject
+
+class ManyToManyElementBinderSpec extends HibernateGormDatastoreSpec {
+
+ @Subject
+ ManyToManyElementBinder binder
+
+ void setupSpec() {
+ manager.addAllDomainClasses([
+ MTMEOwner,
+ MTMEItem,
+ MTMEBase,
+ MTMESubtype,
+ ])
+ }
+
+ void setup() {
+ def gdb = getGrailsDomainBinder()
+ def mbc = gdb.getMetadataBuildingContext()
+ def ns = gdb.getNamingStrategy()
+ def je = gdb.getJdbcEnvironment()
+ def svb = new SimpleValueBinder(mbc, ns, je)
+ def citmto = new CompositeIdentifierToManyToOneBinder(mbc, ns, je)
+ def mtob = new ManyToOneBinder(mbc, ns, svb, new ManyToOneValuesBinder(), citmto)
+ binder = new ManyToManyElementBinder(mtob, new CollectionForPropertyConfigBinder())
+ }
+
+ private HibernateManyToManyProperty propertyFor(Class ownerClass, String name = "items") {
+ (getPersistentEntity(ownerClass) as GrailsHibernatePersistentEntity).getPropertyByName(name) as HibernateManyToManyProperty
+ }
+
+ def "bind sets ManyToOne element referencing the inverse owner for a standard bidirectional many-to-many"() {
+ given:
+ def property = propertyFor(MTMEOwner)
+ def mbc = getGrailsDomainBinder().getMetadataBuildingContext()
+ def collection = new Bag(mbc, null)
+ collection.setCollectionTable(new Table("test", "mtme_owner_mtme_item"))
+
+ when:
+ binder.bind(property, collection)
+
+ then:
+ collection.getElement() instanceof ManyToOne
+ (collection.getElement() as ManyToOne).getReferencedEntityName() == MTMEItem.name
+ }
+
+ def "bind sets collection inverse false for a circular many-to-many"() {
+ given:
+ def property = propertyFor(MTMESubtype, "related")
+ def mbc = getGrailsDomainBinder().getMetadataBuildingContext()
+ def collection = new Bag(mbc, null)
+ collection.setCollectionTable(new Table("test", "mtme_subtype_mtme_base"))
+
+ when:
+ binder.bind(property, collection)
+
+ then:
+ property.isCircular()
+ !collection.isInverse()
+ }
+}
+
+@Entity
+class MTMEOwner {
+ Long id
+ static hasMany = [items: MTMEItem]
+}
+
+@Entity
+class MTMEItem {
+ Long id
+ String description
+ static hasMany = [owners: MTMEOwner]
+}
+
+@Entity
+class MTMEBase {
+ Long id
+ static hasMany = [subtypes: MTMESubtype]
+}
+
+@Entity
+class MTMESubtype extends MTMEBase {
+ static hasMany = [related: MTMEBase]
+}