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/04/06 19:16:55 UTC
(grails-core) 05/12: H7: expand test coverage for ManyToMany properties and List second pass binding
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 ad10d58a6cd1cbc966e4202410b528d336e54a6d
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Sat Apr 4 22:48:12 2026 -0500
H7: expand test coverage for ManyToMany properties and List second pass binding
---
.../domainbinding/secondpass/ListSecondPass.java | 2 +-
.../secondpass/ListSecondPassBinder.java | 90 ++--
.../gorm/specs/HibernateGormDatastoreSpec.groovy | 4 +-
.../HibernateManyToManyPropertySpec.groovy | 59 +++
.../secondpass/ListSecondPassBinderSpec.groovy | 454 +++++++++++----------
5 files changed, 353 insertions(+), 256 deletions(-)
diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPass.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPass.java
index ee6381b30e..3d5e6c832e 100644
--- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPass.java
+++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPass.java
@@ -41,7 +41,7 @@ public class ListSecondPass implements org.hibernate.boot.spi.SecondPass, Grails
@Override
public void doSecondPass(Map persistentClasses) throws MappingException {
- listSecondPassBinder.bindListSecondPass(property, persistentClasses);
+ listSecondPassBinder.bindListSecondPass(property);
createCollectionKeys(property.getCollection());
}
}
diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPassBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPassBinder.java
index f4006cb0b0..8ec6a0c493 100644
--- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPassBinder.java
+++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPassBinder.java
@@ -18,8 +18,6 @@
*/
package org.grails.orm.hibernate.cfg.domainbinding.secondpass;
-import java.util.Map;
-
import jakarta.annotation.Nonnull;
import org.hibernate.MappingException;
@@ -39,7 +37,6 @@ import org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy;
import org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueColumnBinder;
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.HibernateToManyProperty;
import org.grails.orm.hibernate.cfg.domainbinding.util.BackticksRemover;
@@ -68,7 +65,7 @@ public class ListSecondPassBinder {
this.mappings = mappings;
}
- public void bindListSecondPass(@Nonnull HibernateToManyProperty property, Map<?, ?> persistentClasses) {
+ public void bindListSecondPass(@Nonnull HibernateToManyProperty property) {
List list = (List) property.getCollection();
collectionSecondPassBinder.bindCollectionSecondPass(property);
@@ -93,57 +90,54 @@ public class ListSecondPassBinder {
if (property.isBidirectional()) {
- String entityName;
HibernateAssociation inverseSide = property.getHibernateInverseSide();
- if (inverseSide instanceof HibernateManyToOneProperty manyToOne) {
- entityName = manyToOne.getReferencedEntityName();
- } else {
- entityName = inverseSide.getReferencedEntityName();
- }
+ String entityName = inverseSide.getHibernateOwner().getName();
PersistentClass referenced = mappings.getEntityBinding(entityName);
- boolean compositeIdProperty = property.getHibernateInverseSide().isCompositeIdProperty();
- if (!compositeIdProperty) {
- Backref prop = new Backref();
- final PersistentEntity owner = property.getOwner();
- prop.setEntityName(owner.getName());
- String s2 = property.getName();
- prop.setName(UNDERSCORE +
- new BackticksRemover().apply(owner.getJavaClass().getSimpleName()) +
- UNDERSCORE +
- new BackticksRemover().apply(s2) +
- "Backref");
- prop.setSelectable(false);
- prop.setUpdatable(false);
- if (isManyToMany) {
- prop.setInsertable(false);
- }
- prop.setCollectionRole(list.getRole());
- prop.setValue(list.getKey());
-
- DependantValue value = (DependantValue) prop.getValue();
- if (!property.isCircular()) {
- value.setNullable(false);
+ if (referenced != null) {
+ boolean compositeIdProperty = property.getHibernateInverseSide().isCompositeIdProperty();
+ if (!compositeIdProperty) {
+ Backref prop = new Backref();
+ final PersistentEntity owner = property.getOwner();
+ prop.setEntityName(owner.getName());
+ String s2 = property.getName();
+ prop.setName(UNDERSCORE +
+ new BackticksRemover().apply(owner.getJavaClass().getSimpleName()) +
+ UNDERSCORE +
+ new BackticksRemover().apply(s2) +
+ "Backref");
+ prop.setSelectable(false);
+ prop.setUpdatable(false);
+ if (isManyToMany) {
+ prop.setInsertable(false);
+ }
+ prop.setCollectionRole(list.getRole());
+ prop.setValue(list.getKey());
+
+ DependantValue value = (DependantValue) prop.getValue();
+ if (!property.isCircular()) {
+ value.setNullable(false);
+ }
+ value.setUpdateable(true);
+ prop.setOptional(false);
+
+ referenced.addProperty(prop);
}
- value.setUpdateable(true);
- prop.setOptional(false);
-
- referenced.addProperty(prop);
- }
- if ((!list.getKey().isNullable() && !list.isInverse()) || compositeIdProperty) {
- IndexBackref ib = new IndexBackref();
- ib.setName(UNDERSCORE + property.getName() + "IndexBackref");
- ib.setUpdatable(false);
- ib.setSelectable(false);
- if (isManyToMany) {
- ib.setInsertable(false);
+ if ((!list.getKey().isNullable() && !list.isInverse()) || compositeIdProperty) {
+ IndexBackref ib = new IndexBackref();
+ ib.setName(UNDERSCORE + property.getName() + "IndexBackref");
+ ib.setUpdatable(false);
+ ib.setSelectable(false);
+ if (isManyToMany) {
+ ib.setInsertable(false);
+ }
+ ib.setCollectionRole(list.getRole());
+ ib.setEntityName(list.getOwner().getEntityName());
+ ib.setValue(list.getIndex());
+ referenced.addProperty(ib);
}
- ib.setCollectionRole(list.getRole());
- ib.setEntityName(list.getOwner().getEntityName());
- ib.setValue(list.getIndex());
- referenced.addProperty(ib);
}
}
}
diff --git a/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy
index 29209d2c82..1ca4db98c0 100644
--- a/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy
+++ b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy
@@ -165,8 +165,8 @@ class HibernateGormDatastoreSpec extends GrailsDataTckSpec<GrailsDataHibernate7T
datastore.connect() as HibernateSession
}
- protected PersistentEntity getPersistentEntity(Class clazz) {
- getMappingContext().getPersistentEntity(clazz.typeName)
+ protected GrailsHibernatePersistentEntity getPersistentEntity(Class clazz) {
+ getMappingContext().getPersistentEntity(clazz.typeName) as GrailsHibernatePersistentEntity
}
protected HibernateQuery getQuery(Class clazz) {
diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateManyToManyPropertySpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateManyToManyPropertySpec.groovy
new file mode 100644
index 0000000000..e62ab1ea88
--- /dev/null
+++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateManyToManyPropertySpec.groovy
@@ -0,0 +1,59 @@
+/*
+ * 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.hibernate
+
+import grails.gorm.specs.HibernateGormDatastoreSpec
+import grails.persistence.Entity
+
+class HibernateManyToManyPropertySpec extends HibernateGormDatastoreSpec {
+
+ void setupSpec() {
+ manager.addAllDomainClasses([HMMPA, HMMPB])
+ }
+
+ def "test HibernateManyToManyProperty basic methods"() {
+ given:
+ def entityA = (HibernatePersistentEntity) getMappingContext().getPersistentEntity(HMMPA.name)
+ def property = (HibernateManyToManyProperty) entityA.getPropertyByName("others")
+
+ expect:
+ property.getHibernateAssociatedEntity().name == HMMPB.name
+ property.getReferencedEntityName() == HMMPB.name
+ property.isManyToMany()
+ !property.isOneToMany()
+ property.isLazy()
+ !property.isAssociationColumnNullable()
+ }
+}
+
+@Entity
+class HMMPA {
+ Long id
+ static hasMany = [others: HMMPB]
+ static mapping = {
+ others joinTable: [name: "h_m_m_p_a_others"]
+ }
+}
+
+@Entity
+class HMMPB {
+ Long id
+ static hasMany = [owners: HMMPA]
+ static belongsTo = [owners: HMMPA]
+}
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 5d5381976d..291b53dc7e 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
@@ -20,259 +20,303 @@
package org.grails.orm.hibernate.cfg.domainbinding.secondpass
import grails.gorm.specs.HibernateGormDatastoreSpec
-import org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity
-import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyProperty
-
-import org.hibernate.mapping.OneToMany
-import org.hibernate.mapping.RootClass
-import org.hibernate.boot.spi.MetadataBuildingContext
+import grails.persistence.Entity
+import org.grails.datastore.mapping.model.PersistentEntity
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
-import org.grails.orm.hibernate.cfg.domainbinding.util.SimpleValueColumnFetcher
-import org.grails.orm.hibernate.cfg.domainbinding.binder.CollectionBinder
-import org.grails.orm.hibernate.cfg.domainbinding.binder.OneToOneBinder
-import org.grails.orm.hibernate.cfg.domainbinding.binder.ClassBinder
-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.util.BasicValueCreator
-import org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsPropertyBinder
-import org.grails.orm.hibernate.cfg.domainbinding.binder.IdentityBinder
-import org.hibernate.boot.spi.InFlightMetadataCollector
-import org.grails.orm.hibernate.cfg.domainbinding.binder.VersionBinder
-import org.grails.orm.hibernate.cfg.domainbinding.binder.CompositeIdBinder
-import org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleIdBinder
-import org.grails.orm.hibernate.cfg.domainbinding.binder.PropertyBinder
-import org.hibernate.mapping.BasicValue
-import org.grails.orm.hibernate.cfg.domainbinding.util.BackticksRemover
-import org.grails.orm.hibernate.cfg.domainbinding.util.DefaultColumnNameFetcher
-import org.grails.orm.hibernate.cfg.domainbinding.util.ColumnNameForPropertyAndPathFetcher
+import org.grails.orm.hibernate.cfg.domainbinding.binder.*
import org.grails.orm.hibernate.cfg.domainbinding.collectionType.CollectionHolder
-import org.grails.orm.hibernate.cfg.domainbinding.util.PropertyFromValueCreator
-import org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsDomainBinder
-import org.grails.orm.hibernate.cfg.domainbinding.binder.SubClassBinder
-import org.grails.orm.hibernate.cfg.domainbinding.binder.SubclassMappingBinder
-import org.grails.orm.hibernate.cfg.domainbinding.binder.RootBinder
-import org.grails.orm.hibernate.cfg.domainbinding.binder.RootPersistentClassCommonValuesBinder
-import org.grails.orm.hibernate.cfg.domainbinding.binder.DiscriminatorPropertyBinder
-import org.grails.orm.hibernate.cfg.domainbinding.binder.ColumnConfigToColumnBinder
-
-import org.grails.orm.hibernate.cfg.domainbinding.binder.ClassPropertiesBinder
-import org.grails.orm.hibernate.cfg.domainbinding.util.MultiTenantFilterBinder
-import org.grails.orm.hibernate.cfg.domainbinding.binder.JoinedSubClassBinder
-import org.grails.orm.hibernate.cfg.domainbinding.binder.UnionSubclassBinder
-import org.grails.orm.hibernate.cfg.domainbinding.binder.SingleTableSubclassBinder
+import org.grails.orm.hibernate.cfg.domainbinding.hibernate.*
+import org.grails.orm.hibernate.cfg.domainbinding.util.*
+import org.hibernate.MappingException
+import org.hibernate.boot.spi.InFlightMetadataCollector
+import org.hibernate.boot.spi.MetadataBuildingContext
+import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment
+import org.hibernate.mapping.*
class ListSecondPassBinderSpec extends HibernateGormDatastoreSpec {
- protected Map getBinders(GrailsDomainBinder binder, InFlightMetadataCollector collector = getCollector()) {
- MetadataBuildingContext metadataBuildingContext = binder.getMetadataBuildingContext()
- PersistentEntityNamingStrategy namingStrategy = binder.getNamingStrategy()
- JdbcEnvironment jdbcEnvironment = binder.getJdbcEnvironment()
- BackticksRemover backticksRemover = new BackticksRemover()
- DefaultColumnNameFetcher defaultColumnNameFetcher = new DefaultColumnNameFetcher(namingStrategy, backticksRemover)
- ColumnNameForPropertyAndPathFetcher columnNameForPropertyAndPathFetcher = new ColumnNameForPropertyAndPathFetcher(namingStrategy, defaultColumnNameFetcher, backticksRemover)
- CollectionHolder collectionHolder = new CollectionHolder(metadataBuildingContext)
- SimpleValueBinder simpleValueBinder = new SimpleValueBinder(metadataBuildingContext, namingStrategy, jdbcEnvironment)
- EnumTypeBinder enumTypeBinderToUse = new EnumTypeBinder(metadataBuildingContext, columnNameForPropertyAndPathFetcher,namingStrategy)
- SimpleValueColumnFetcher simpleValueColumnFetcher = new SimpleValueColumnFetcher()
- CompositeIdentifierToManyToOneBinder compositeIdentifierToManyToOneBinder = new CompositeIdentifierToManyToOneBinder(
-
- new org.grails.orm.hibernate.cfg.domainbinding.util.ForeignKeyColumnCountCalculator(),
- namingStrategy,
- defaultColumnNameFetcher,
- backticksRemover,
- simpleValueBinder
- )
- OneToOneBinder oneToOneBinder = new OneToOneBinder(metadataBuildingContext, simpleValueBinder)
- ManyToOneBinder manyToOneBinder = new ManyToOneBinder(metadataBuildingContext, namingStrategy, simpleValueBinder, new ManyToOneValuesBinder(), compositeIdentifierToManyToOneBinder)
- ForeignKeyOneToOneBinder foreignKeyOneToOneBinder = new ForeignKeyOneToOneBinder(manyToOneBinder, simpleValueColumnFetcher)
-
- CollectionBinder collectionBinder = new CollectionBinder(
- metadataBuildingContext,
- namingStrategy
- ,
- simpleValueBinder,
- enumTypeBinderToUse,
- manyToOneBinder,
- compositeIdentifierToManyToOneBinder,
- simpleValueColumnFetcher
- ,
- collectionHolder,
- collector
- )
- PropertyFromValueCreator propertyFromValueCreator = new PropertyFromValueCreator()
- ComponentUpdater componentUpdater = new ComponentUpdater(propertyFromValueCreator)
- ComponentBinder componentBinder = new ComponentBinder(
- metadataBuildingContext,
- binder.getMappingCacheHolder(),
- componentUpdater
- )
-
- GrailsPropertyBinder propertyBinder = new GrailsPropertyBinder(
-
-
- enumTypeBinderToUse,
- componentBinder,
- collectionBinder,
- simpleValueBinder
- ,
- oneToOneBinder,
- manyToOneBinder,
- foreignKeyOneToOneBinder
-
- )
- CompositeIdBinder compositeIdBinder = new CompositeIdBinder(metadataBuildingContext, componentUpdater, propertyBinder)
- PropertyBinder propertyBinderHelper = new PropertyBinder()
- SimpleIdBinder simpleIdBinder = new SimpleIdBinder(metadataBuildingContext, new BasicValueCreator(metadataBuildingContext, jdbcEnvironment, namingStrategy), simpleValueBinder, propertyBinderHelper)
- IdentityBinder identityBinder = new IdentityBinder(simpleIdBinder, compositeIdBinder)
- VersionBinder versionBinder = new VersionBinder(metadataBuildingContext, simpleValueBinder, propertyBinderHelper, BasicValue::new)
-
- ClassBinder classBinder = new ClassBinder(collector)
- ClassPropertiesBinder classPropertiesBinder = new ClassPropertiesBinder(propertyBinder, propertyFromValueCreator)
- MultiTenantFilterBinder multiTenantFilterBinder = new MultiTenantFilterBinder(new org.grails.orm.hibernate.cfg.domainbinding.util.GrailsPropertyResolver(), new org.grails.orm.hibernate.cfg.domainbinding.util.MultiTenantFilterDefinitionBinder(), collector, defaultColumnNameFetcher)
- JoinedSubClassBinder joinedSubClassBinder = new JoinedSubClassBinder(metadataBuildingContext, namingStrategy, new org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueColumnBinder(), columnNameForPropertyAndPathFetcher, classBinder, collector)
- UnionSubclassBinder unionSubclassBinder = new UnionSubclassBinder(metadataBuildingContext, namingStrategy, classBinder, collector)
- SingleTableSubclassBinder singleTableSubclassBinder = new SingleTableSubclassBinder(classBinder, metadataBuildingContext)
-
- SubclassMappingBinder subclassMappingBinder = new SubclassMappingBinder(joinedSubClassBinder, unionSubclassBinder, singleTableSubclassBinder, classPropertiesBinder)
- SubClassBinder subClassBinder = new SubClassBinder(subclassMappingBinder, multiTenantFilterBinder, "dataSource")
- RootPersistentClassCommonValuesBinder rootPersistentClassCommonValuesBinder = new RootPersistentClassCommonValuesBinder(metadataBuildingContext, namingStrategy, identityBinder, versionBinder, classBinder, classPropertiesBinder, collector)
- DiscriminatorPropertyBinder discriminatorPropertyBinder = new DiscriminatorPropertyBinder(metadataBuildingContext, binder.getMappingCacheHolder(), new org.grails.orm.hibernate.cfg.domainbinding.binder.ConfiguredDiscriminatorBinder(new org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueColumnBinder(), new ColumnConfigToColumnBinder()), new org.grails.orm.hibernate.cfg.domainbinding.binder.DefaultDiscriminatorBinder(new org.grails.orm.hibernate.cfg.domainbinding.binder.Si [...]
- RootBinder rootBinder = new RootBinder("default", multiTenantFilterBinder, subClassBinder, rootPersistentClassCommonValuesBinder, discriminatorPropertyBinder, collector, binder.getMappingCacheHolder())
+ protected java.util.Map getBinders(GrailsDomainBinder binder, InFlightMetadataCollector collector = getCollector()) {
+ MetadataBuildingContext mbc = binder.getMetadataBuildingContext()
+ PersistentEntityNamingStrategy ns = binder.getNamingStrategy()
+ JdbcEnvironment je = binder.getJdbcEnvironment()
+ BackticksRemover br = new BackticksRemover()
+ DefaultColumnNameFetcher dcnf = new DefaultColumnNameFetcher(ns, br)
+ ColumnNameForPropertyAndPathFetcher cnfpapf = new ColumnNameForPropertyAndPathFetcher(ns, dcnf, br)
+ CollectionHolder ch = new CollectionHolder(mbc)
+ SimpleValueBinder svb = new SimpleValueBinder(mbc, ns, je)
+ EnumTypeBinder etb = new EnumTypeBinder(mbc, cnfpapf, ns)
+ SimpleValueColumnFetcher svcf = new SimpleValueColumnFetcher()
+ CompositeIdentifierToManyToOneBinder citmto = new CompositeIdentifierToManyToOneBinder(
+ new ForeignKeyColumnCountCalculator(), ns, dcnf, br, svb)
+ OneToOneBinder otob = new OneToOneBinder(mbc, svb)
+ ManyToOneBinder mtob = new ManyToOneBinder(mbc, ns, svb, new ManyToOneValuesBinder(), citmto)
+ ForeignKeyOneToOneBinder fkotob = new ForeignKeyOneToOneBinder(mtob, svcf)
+
+ CollectionBinder cb = new CollectionBinder(mbc, ns, svb, etb, mtob, citmto, svcf, ch, collector)
+ PropertyFromValueCreator pfvc = new PropertyFromValueCreator()
+ ComponentUpdater cu = new ComponentUpdater(pfvc)
+ ComponentBinder comb = new ComponentBinder(mbc, binder.getMappingCacheHolder(), cu)
+
+ GrailsPropertyBinder pb = new GrailsPropertyBinder(etb, comb, cb, svb, otob, mtob, fkotob)
+ CompositeIdBinder cib = new CompositeIdBinder(mbc, cu, pb)
+ PropertyBinder pbh = new PropertyBinder()
+ SimpleIdBinder sib = new SimpleIdBinder(mbc, new BasicValueCreator(mbc, je, ns), svb, pbh)
+ IdentityBinder ib = new IdentityBinder(sib, cib)
+ VersionBinder vb = new VersionBinder(mbc, svb, pbh, BasicValue::new)
+
+ ClassBinder clb = new ClassBinder(collector)
+ ClassPropertiesBinder clpb = new ClassPropertiesBinder(pb, pfvc)
+ MultiTenantFilterBinder mtfb = new MultiTenantFilterBinder(new GrailsPropertyResolver(), new MultiTenantFilterDefinitionBinder(), collector, dcnf)
+ JoinedSubClassBinder jscb = new JoinedSubClassBinder(mbc, ns, new org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueColumnBinder(), cnfpapf, clb, collector)
+ UnionSubclassBinder uscb = new UnionSubclassBinder(mbc, ns, clb, collector)
+ SingleTableSubclassBinder stscb = new SingleTableSubclassBinder(clb, mbc)
+
+ SubclassMappingBinder scmb = new SubclassMappingBinder(jscb, uscb, stscb, clpb)
+ SubClassBinder scb = new SubClassBinder(scmb, mtfb, "dataSource")
+ RootPersistentClassCommonValuesBinder rpccvb = new RootPersistentClassCommonValuesBinder(mbc, ns, ib, vb, clb, clpb, collector)
+ DiscriminatorPropertyBinder dpb = new DiscriminatorPropertyBinder(mbc, binder.getMappingCacheHolder(), new ConfiguredDiscriminatorBinder(new org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueColumnBinder(), new ColumnConfigToColumnBinder()), new DefaultDiscriminatorBinder(new org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueColumnBinder()))
+ RootBinder rb = new RootBinder("default", mtfb, scb, rpccvb, dpb, collector, binder.getMappingCacheHolder())
return [
- propertyBinder: propertyBinder,
- collectionBinder: collectionBinder,
- identityBinder: identityBinder,
- versionBinder: versionBinder,
- defaultColumnNameFetcher: defaultColumnNameFetcher,
- columnNameForPropertyAndPathFetcher: columnNameForPropertyAndPathFetcher,
- classBinder: classBinder,
- classPropertiesBinder: classPropertiesBinder,
- multiTenantFilterBinder: multiTenantFilterBinder,
- joinedSubClassBinder: joinedSubClassBinder,
- unionSubclassBinder: unionSubclassBinder,
- singleTableSubclassBinder: singleTableSubclassBinder,
- subClassBinder: subClassBinder,
- rootBinder: rootBinder
+ propertyBinder: pb,
+ collectionBinder: cb,
+ identityBinder: ib,
+ versionBinder: vb,
+ classBinder: clb,
+ classPropertiesBinder: clpb,
+ rootBinder: rb
]
}
- protected void bindRoot(GrailsDomainBinder binder, GrailsHibernatePersistentEntity entity, InFlightMetadataCollector mappings, String sessionFactoryBeanName) {
- def binders = getBinders(binder, mappings)
- binders.rootBinder.bindRoot(entity)
+ void setupSpec() {
+ // Empty to avoid global failures
+ }
+
+ protected HibernatePersistentProperty propertyFor(Class domainClass, String propertyName) {
+ PersistentEntity entity = createPersistentEntity(domainClass)
+ return (HibernatePersistentProperty) entity.getPropertyByName(propertyName)
}
- void setupSpec() {
- manager.addAllDomainClasses([
- org.apache.grails.data.testing.tck.domains.Pet,
- org.apache.grails.data.testing.tck.domains.Person,
- org.apache.grails.data.testing.tck.domains.PetType,
- LSBAuthor,
- LSBBook
- ])
+ protected RootClass createMockPersistentClass(Class domainClass, InFlightMetadataCollector collector, java.util.List<String> properties = []) {
+ def binder = getGrailsDomainBinder()
+ def rootClass = new RootClass(binder.getMetadataBuildingContext())
+ rootClass.setEntityName(domainClass.name)
+ rootClass.setJpaEntityName(domainClass.simpleName)
+ rootClass.setTable(collector.addTable(null, null, domainClass.simpleName.toUpperCase(), null, false, binder.getMetadataBuildingContext()))
+
+ properties.each { propName ->
+ def p = new Property()
+ p.setName(propName)
+ p.setValue(new BasicValue(binder.getMetadataBuildingContext(), rootClass.getTable()))
+ rootClass.addProperty(p)
+ }
+
+ collector.addEntityBinding(rootClass)
+
+ def entity = (GrailsHibernatePersistentEntity) getMappingContext().getPersistentEntity(domainClass.name) ?: createPersistentEntity(domainClass)
+ entity.setPersistentClass(rootClass)
+
+ return rootClass
}
- void "Test bind collection"() {
+ def "bindListSecondPass applies index customization"() {
given:
def binder = getGrailsDomainBinder()
- def collectionBinder = getBinders(binder).collectionBinder
def collector = getCollector()
+ def listBinder = getBinders(binder, collector).collectionBinder.listSecondPassBinder
+ def property = propertyFor(LSBCustomIndex, "items") as HibernateToManyProperty
- def personEntity = getPersistentEntity(org.apache.grails.data.testing.tck.domains.Person) as GrailsHibernatePersistentEntity
- def petEntity = getPersistentEntity(org.apache.grails.data.testing.tck.domains.Pet) as GrailsHibernatePersistentEntity
+ def rootClass = createMockPersistentClass(LSBCustomIndex, collector)
+
+ def list = new org.hibernate.mapping.List(binder.getMetadataBuildingContext(), rootClass)
+ list.setRole("${LSBCustomIndex.name}.items".toString())
+ list.setCollectionTable(rootClass.getTable())
+ list.setElement(new BasicValue(binder.getMetadataBuildingContext(), list.getCollectionTable()))
+ property.setCollection(list)
- def rootClass = new RootClass(binder.getMetadataBuildingContext())
- rootClass.setEntityName(personEntity.name)
- rootClass.setTable(collector.addTable(null, null, "PERSON", null, false, binder.getMetadataBuildingContext()))
+ when:
+ listBinder.bindListSecondPass(property)
+
+ then:
+ list.index != null
+ list.index.getColumn(0).name == "my_index_col"
+ (list.index as BasicValue).typeName == "long"
+ }
+
+ def "bindListSecondPass throws exception for many-to-many non-owning side"() {
+ given:
+ def binder = getGrailsDomainBinder()
+ def collector = getCollector()
+ def listBinder = getBinders(binder, collector).collectionBinder.listSecondPassBinder
+
+ def property = propertyFor(LSBManyToManyB, "owners") as HibernateManyToManyProperty
+ def ownerRoot = createMockPersistentClass(LSBManyToManyB, collector, ["owners"])
- def petsProp = personEntity.getPropertyByName("pets") as HibernateToManyProperty
+ def list = new org.hibernate.mapping.List(binder.getMetadataBuildingContext(), ownerRoot)
+ list.setRole("${LSBManyToManyB.name}.owners".toString())
+ property.setCollection(list)
when:
- def collection = collectionBinder.bindCollection(petsProp, "")
+ listBinder.bindListSecondPass(property)
then:
- collection.role == "${personEntity.name}.pets".toString()
- collection.element instanceof OneToMany
- (collection.element as OneToMany).referencedEntityName == petEntity.name
+ def e = thrown(MappingException)
+ e.message.contains("has no associated class") || e.message.contains("List collection types only supported on the owning side")
}
- void "test bidirectional list binding"() {
+ def "bindListSecondPass handles many-to-many specific flags"() {
given:
def binder = getGrailsDomainBinder()
def collector = getCollector()
- def metadataBuildingContext = binder.getMetadataBuildingContext()
- def binders = getBinders(binder, collector)
- def collectionBinder = binders.collectionBinder
- def listBinder = collectionBinder.listSecondPassBinder
-
- def authorEntity = getPersistentEntity(LSBAuthor) as GrailsHibernatePersistentEntity
- def bookEntity = getPersistentEntity(LSBBook) as GrailsHibernatePersistentEntity
- def booksProp = authorEntity.getPropertyByName("books") as HibernateToManyProperty
-
- def rootClass = new RootClass(metadataBuildingContext)
- rootClass.setEntityName(authorEntity.name)
- rootClass.setJpaEntityName(authorEntity.name)
- rootClass.setTable(collector.addTable(null, null, "LSB_AUTHOR", null, false, metadataBuildingContext))
- collector.addEntityBinding(rootClass)
+ def listBinder = getBinders(binder, collector).collectionBinder.listSecondPassBinder
+
+ def property = propertyFor(LSBManyToManyA, "others") as HibernateManyToManyProperty
+ def ownerRoot = createMockPersistentClass(LSBManyToManyA, collector, ["others"])
+ def otherRoot = createMockPersistentClass(LSBManyToManyB, collector, ["owners"])
+
+ def list = new org.hibernate.mapping.List(binder.getMetadataBuildingContext(), ownerRoot)
+ list.setRole("${LSBManyToManyA.name}.others".toString())
+ list.setCollectionTable(collector.addTable(null, null, "JOIN_TABLE", null, false, binder.getMetadataBuildingContext()))
+ list.setKey(new DependantValue(binder.getMetadataBuildingContext(), list.getCollectionTable(), null))
+ list.setElement(new ManyToOne(binder.getMetadataBuildingContext(), list.getCollectionTable()))
+ ((ManyToOne)list.getElement()).setReferencedEntityName(LSBManyToManyB.name)
+ property.setCollection(list)
- def bookRootClass = new RootClass(metadataBuildingContext)
- bookRootClass.setEntityName(bookEntity.name)
- bookRootClass.setJpaEntityName(bookEntity.name)
- bookRootClass.setTable(collector.addTable(null, null, "LSB_BOOK", null, false, metadataBuildingContext))
- collector.addEntityBinding(bookRootClass)
-
- def authorValue = new org.hibernate.mapping.ManyToOne(metadataBuildingContext, bookRootClass.getTable())
- authorValue.setReferencedEntityName(authorEntity.name)
- def authorCol = new org.hibernate.mapping.Column("author_id")
- authorCol.setValue(authorValue)
- authorValue.addColumn(authorCol)
- def authorProp = new org.hibernate.mapping.Property()
- authorProp.setName("author")
- authorProp.setValue(authorValue)
- bookRootClass.addProperty(authorProp)
-
- def persistentClasses = [
- (authorEntity.name): rootClass,
- (bookEntity.name): bookRootClass
- ]
+ when:
+ listBinder.bindListSecondPass(property)
+
+ then:
+ def backref = otherRoot.getProperties().find { it.name == "_" + "LSBManyToManyA" + "_" + "others" + "Backref" }
+ backref instanceof Backref
+ !backref.isInsertable()
+
+ def indexBackref = otherRoot.getProperties().find { it.name == "_" + "others" + "IndexBackref" }
+ indexBackref instanceof IndexBackref
+ !indexBackref.isInsertable()
+ }
- def list = new org.hibernate.mapping.List(metadataBuildingContext, rootClass)
- list.setRole("${authorEntity.name}.books".toString())
+ def "bindListSecondPass handles circular associations"() {
+ given:
+ def binder = getGrailsDomainBinder()
+ def collector = getCollector()
+ def listBinder = getBinders(binder, collector).collectionBinder.listSecondPassBinder
+ def property = propertyFor(LSBCircular, "children") as HibernateToManyProperty
+
+ def rootClass = createMockPersistentClass(LSBCircular, collector, ["parent", "children"])
+
+ def list = new org.hibernate.mapping.List(binder.getMetadataBuildingContext(), rootClass)
+ list.setRole("${LSBCircular.name}.children".toString())
list.setCollectionTable(rootClass.getTable())
+ def key = new DependantValue(binder.getMetadataBuildingContext(), list.getCollectionTable(), null)
+ key.setNullable(false)
+ list.setKey(key)
+ list.setElement(new ManyToOne(binder.getMetadataBuildingContext(), list.getCollectionTable()))
+ ((ManyToOne)list.getElement()).setReferencedEntityName(LSBCircular.name)
+ property.setCollection(list)
+
+ when:
+ listBinder.bindListSecondPass(property)
- def element = new org.hibernate.mapping.ManyToOne(metadataBuildingContext, list.getCollectionTable())
- element.setReferencedEntityName(LSBBook.name)
- list.setElement(element)
+ then:
+ property.isCircular()
+ // For circular, we don't force nullable false
+ list.getKey().isNullable()
+ }
- booksProp.setCollection(list)
+ def "bindListSecondPass handles composite identity"() {
+ given:
+ def binder = getGrailsDomainBinder()
+ def collector = getCollector()
+ def listBinder = getBinders(binder, collector).collectionBinder.listSecondPassBinder
+
+ def property = propertyFor(LSBCompositeIdOwner, "items") as HibernateToManyProperty
+ def ownerRoot = createMockPersistentClass(LSBCompositeIdOwner, collector, ["items"])
+ def itemRoot = createMockPersistentClass(LSBCompositeIdItem, collector, ["owner", "name"])
+
+ def list = new org.hibernate.mapping.List(binder.getMetadataBuildingContext(), ownerRoot)
+ list.setRole("${LSBCompositeIdOwner.name}.items".toString())
+ list.setCollectionTable(itemRoot.getTable())
+ list.setKey(new DependantValue(binder.getMetadataBuildingContext(), list.getCollectionTable(), null))
+ list.setElement(new ManyToOne(binder.getMetadataBuildingContext(), list.getCollectionTable()))
+ ((ManyToOne)list.getElement()).setReferencedEntityName(LSBCompositeIdItem.name)
+ property.setCollection(list)
+
+ expect:
+ property.getHibernateInverseSide().isCompositeIdProperty()
when:
- listBinder.bindListSecondPass(booksProp, persistentClasses)
+ listBinder.bindListSecondPass(property)
then:
- noExceptionThrown()
- list.index != null
- list.baseIndex == 0
+ // No Backref should be created for composite ID inverse
+ !itemRoot.getProperties().find { it.name.endsWith("Backref") && it instanceof Backref }
+
+ // IndexBackref should still be created
+ itemRoot.getProperties().find { it.name == "_" + "items" + "IndexBackref" } instanceof IndexBackref
+ }
+}
+
+@Entity
+class LSBCustomIndex {
+ Long id
+ java.util.List<String> items
+ static hasMany = [items: String]
+ static mapping = {
+ items index: [column: "my_index_col", type: "long"]
}
}
-@grails.gorm.annotation.Entity
+@Entity
+class LSBCircular {
+ Long id
+ LSBCircular parent
+ java.util.List<LSBCircular> children
+ static hasMany = [children: LSBCircular]
+ static belongsTo = [parent: LSBCircular]
+}
+
+@Entity
class LSBAuthor {
Long id
- List<LSBBook> books
+ java.util.List<LSBBook> books
static hasMany = [books: LSBBook]
}
-@grails.gorm.annotation.Entity
+@Entity
class LSBBook {
Long id
LSBAuthor author
static belongsTo = [author: LSBAuthor]
}
+
+@Entity
+class LSBManyToManyA {
+ Long id
+ java.util.List<LSBManyToManyB> others
+ static hasMany = [others: LSBManyToManyB]
+}
+
+@Entity
+class LSBManyToManyB {
+ Long id
+ java.util.List<LSBManyToManyA> owners
+ static hasMany = [owners: LSBManyToManyA]
+ static belongsTo = LSBManyToManyA
+}
+
+@Entity
+class LSBCompositeIdOwner {
+ Long id
+ java.util.List<LSBCompositeIdItem> items
+ static hasMany = [items: LSBCompositeIdItem]
+}
+
+@Entity
+class LSBCompositeIdItem implements Serializable {
+ LSBCompositeIdOwner owner
+ String name
+ static belongsTo = [owner: LSBCompositeIdOwner]
+ static mapping = {
+ id composite: ['owner', 'name']
+ }
+}