This is an automated email from the ASF dual-hosted git repository. borinquenkid pushed a commit to branch 8.0.x-hibernate7 in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit e4ea00d783c6708909f5d969bd7dcb4ce3d41bf6 Author: Walter Duque de Estrada <[email protected]> AuthorDate: Fri Feb 27 13:47:07 2026 -0600 refactor DiscriminatorPropertyBinder --- .../orm/hibernate/cfg/MappingCacheHolder.java | 12 ++ .../binder/ConfiguredDiscriminatorBinder.java | 110 +++++++++++++++ .../binder/DefaultDiscriminatorBinder.java | 54 ++++++++ .../binder/DiscriminatorPropertyBinder.java | 69 +++------- .../domainbinding/binder/GrailsDomainBinder.java | 10 +- .../cfg/domainbinding/binder/RootBinder.java | 7 +- .../cfg/domainbinding/CollectionBinderSpec.groovy | 6 +- .../domainbinding/GrailsPropertyBinderSpec.groovy | 2 +- .../ConfiguredDiscriminatorBinderSpec.groovy | 149 +++++++++++++++++++++ .../binder/DefaultDiscriminatorBinderSpec.groovy | 25 ++++ .../binder/DiscriminatorPropertyBinderSpec.groovy | 114 ++++++++-------- .../cfg/domainbinding/binder/RootBinderSpec.groovy | 4 +- .../secondpass/ListSecondPassBinderSpec.groovy | 6 +- .../secondpass/MapSecondPassBinderSpec.groovy | 2 +- 14 files changed, 449 insertions(+), 121 deletions(-) diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/MappingCacheHolder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/MappingCacheHolder.java index 2ca6202801..85710f44fb 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/MappingCacheHolder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/MappingCacheHolder.java @@ -57,6 +57,18 @@ public class MappingCacheHolder { } } + /** + * Testing method + * + * @param theClass The domain class + * @param mapping The mapping + */ + public void cacheMapping(Class<?> theClass, Mapping mapping) { + if (theClass != null && mapping != null) { + MAPPING_CACHE.put(theClass, mapping); + } + } + public void clear() { MAPPING_CACHE.clear(); } diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ConfiguredDiscriminatorBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ConfiguredDiscriminatorBinder.java new file mode 100644 index 0000000000..6e0e5efa28 --- /dev/null +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ConfiguredDiscriminatorBinder.java @@ -0,0 +1,110 @@ +/* + * 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 static org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsDomainBinder.JPA_DEFAULT_DISCRIMINATOR_TYPE; + +import org.grails.orm.hibernate.cfg.ColumnConfig; +import org.grails.orm.hibernate.cfg.DiscriminatorConfig; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Formula; +import org.hibernate.mapping.RootClass; +import org.hibernate.mapping.SimpleValue; + +public class ConfiguredDiscriminatorBinder { + + private static final String STRING_TYPE = "string"; + + private final SimpleValueColumnBinder simpleValueColumnBinder; + private final ColumnConfigToColumnBinder columnConfigToColumnBinder; + + public ConfiguredDiscriminatorBinder( + SimpleValueColumnBinder simpleValueColumnBinder, + ColumnConfigToColumnBinder columnConfigToColumnBinder) { + this.simpleValueColumnBinder = simpleValueColumnBinder; + this.columnConfigToColumnBinder = columnConfigToColumnBinder; + } + + /** + * Binds a discriminator with explicit configuration + * + * @param entity The root class entity + * @param discriminator The discriminator value to configure + * @param config The discriminator configuration + */ + public void bindConfiguredDiscriminator(RootClass entity, SimpleValue discriminator, DiscriminatorConfig config) { + // Set discriminator value + entity.setDiscriminatorValue(config.getValue()); + + // Configure insertable if specified + if (config.getInsertable() != null) { + entity.setDiscriminatorInsertable(config.getInsertable()); + } + + // Resolve type name + String typeName = resolveTypeName(config.getType()); + + // Bind based on configuration type + if (config.getFormula() != null) { + bindDiscriminatorWithFormula(discriminator, typeName, config.getFormula()); + } else { + bindDiscriminatorWithColumn(discriminator, typeName, config.getColumn()); + } + } + + private String resolveTypeName(Object type) { + if (type == null) { + return STRING_TYPE; + } + + return (type instanceof Class) + ? ((Class<?>) type).getName() + : type.toString(); + } + + private void bindDiscriminatorWithFormula(SimpleValue discriminator, String typeName, String formula) { + discriminator.setTypeName(typeName); + Formula f = new Formula(); + f.setFormula(formula); + discriminator.addFormula(f); + } + + private void bindDiscriminatorWithColumn(SimpleValue discriminator, String typeName, ColumnConfig columnConfig) { + simpleValueColumnBinder.bindSimpleValue( + discriminator, + typeName, + JPA_DEFAULT_DISCRIMINATOR_TYPE, + false + ); + + if (columnConfig != null) { + configureDiscriminatorColumn(discriminator, columnConfig); + } + } + + private void configureDiscriminatorColumn(SimpleValue discriminator, ColumnConfig columnConfig) { + Column column = discriminator.getColumns().iterator().next(); + + if (columnConfig.getName() != null) { + column.setName(columnConfig.getName()); + } + + columnConfigToColumnBinder.bindColumnConfigToColumn(column, columnConfig, null); + } +} diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/DefaultDiscriminatorBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/DefaultDiscriminatorBinder.java new file mode 100644 index 0000000000..0906979bbb --- /dev/null +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/DefaultDiscriminatorBinder.java @@ -0,0 +1,54 @@ +/* + * 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 static org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsDomainBinder.JPA_DEFAULT_DISCRIMINATOR_TYPE; + +import org.hibernate.mapping.RootClass; +import org.hibernate.mapping.SimpleValue; + +public class DefaultDiscriminatorBinder { + + private static final String STRING_TYPE = "string"; + + private final SimpleValueColumnBinder simpleValueColumnBinder; + + public DefaultDiscriminatorBinder(SimpleValueColumnBinder simpleValueColumnBinder) { + this.simpleValueColumnBinder = simpleValueColumnBinder; + } + + /** + * Binds a discriminator with default configuration (no explicit config) + * + * @param entity The root class entity + * @param discriminator The discriminator value to configure + */ + public void bindDefaultDiscriminator(RootClass entity, SimpleValue discriminator) { + // Use class name as discriminator value + entity.setDiscriminatorValue(entity.getClassName()); + + // Bind with default column configuration + simpleValueColumnBinder.bindSimpleValue( + discriminator, + STRING_TYPE, + JPA_DEFAULT_DISCRIMINATOR_TYPE, + false + ); + } +} diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/DiscriminatorPropertyBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/DiscriminatorPropertyBinder.java index fc60911a54..b6305a0ddb 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/DiscriminatorPropertyBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/DiscriminatorPropertyBinder.java @@ -23,6 +23,7 @@ import static org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsDomainBind import org.grails.orm.hibernate.cfg.ColumnConfig; import org.grails.orm.hibernate.cfg.DiscriminatorConfig; import org.grails.orm.hibernate.cfg.Mapping; +import org.grails.orm.hibernate.cfg.MappingCacheHolder; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.mapping.BasicValue; import org.hibernate.mapping.Column; @@ -34,19 +35,20 @@ import org.hibernate.mapping.Table; @SuppressWarnings("PMD.DataflowAnomalyAnalysis") public class DiscriminatorPropertyBinder { - private static final String STRING_TYPE = "string"; - private final MetadataBuildingContext metadataBuildingContext; - private final SimpleValueColumnBinder simpleValueColumnBinder; - private final ColumnConfigToColumnBinder columnConfigToColumnBinder; + private final MappingCacheHolder mappingCacheHolder; + private final ConfiguredDiscriminatorBinder configuredDiscriminatorBinder; + private final DefaultDiscriminatorBinder defaultDiscriminatorBinder; public DiscriminatorPropertyBinder( MetadataBuildingContext metadataBuildingContext, - SimpleValueColumnBinder simpleValueColumnBinder, - ColumnConfigToColumnBinder columnConfigToColumnBinder) { + MappingCacheHolder mappingCacheHolder, + ConfiguredDiscriminatorBinder configuredDiscriminatorBinder, + DefaultDiscriminatorBinder defaultDiscriminatorBinder) { this.metadataBuildingContext = metadataBuildingContext; - this.simpleValueColumnBinder = simpleValueColumnBinder; - this.columnConfigToColumnBinder = columnConfigToColumnBinder; + this.mappingCacheHolder = mappingCacheHolder; + this.configuredDiscriminatorBinder = configuredDiscriminatorBinder; + this.defaultDiscriminatorBinder = defaultDiscriminatorBinder; } /** @@ -54,49 +56,22 @@ public class DiscriminatorPropertyBinder { * discriminate between sub class instances * * @param entity The root class entity - * @param someMapping The mappings instance */ - public void bindDiscriminatorProperty(RootClass entity, Mapping someMapping) { - Table table = entity.getTable(); - SimpleValue d = new BasicValue(metadataBuildingContext, table); - entity.setDiscriminator(d); - DiscriminatorConfig discriminatorConfig = someMapping.getDiscriminator(); - - boolean hasDiscriminatorConfig = discriminatorConfig != null; - entity.setDiscriminatorValue( - hasDiscriminatorConfig ? discriminatorConfig.getValue() : entity.getClassName()); + public void bindDiscriminatorProperty(RootClass entity) { + SimpleValue discriminator = createDiscriminator(entity); + entity.setDiscriminator(discriminator); - String typeName = STRING_TYPE; - if (hasDiscriminatorConfig) { - if (discriminatorConfig.getInsertable() != null) { - entity.setDiscriminatorInsertable(discriminatorConfig.getInsertable()); - } - Object type = discriminatorConfig.getType(); - if (type != null) { - if (type instanceof Class) { - typeName = ((Class) type).getName(); - } else { - typeName = type.toString(); - } - } - } + Mapping mapping = mappingCacheHolder.getMapping(entity.getMappedClass()); + DiscriminatorConfig config = mapping.getDiscriminator(); - if (hasDiscriminatorConfig && discriminatorConfig.getFormula() != null) { - d.setTypeName(typeName); - Formula formula = new Formula(); - formula.setFormula(discriminatorConfig.getFormula()); - d.addFormula(formula); + if (config != null) { + configuredDiscriminatorBinder.bindConfiguredDiscriminator(entity, discriminator, config); } else { - simpleValueColumnBinder.bindSimpleValue(d, typeName, JPA_DEFAULT_DISCRIMINATOR_TYPE, false); - - ColumnConfig cc = !hasDiscriminatorConfig ? null : discriminatorConfig.getColumn(); - if (cc != null) { - Column c = (Column) d.getColumns().iterator().next(); - if (cc.getName() != null) { - c.setName(cc.getName()); - } - columnConfigToColumnBinder.bindColumnConfigToColumn(c, cc, null); - } + defaultDiscriminatorBinder.bindDefaultDiscriminator(entity, discriminator); } } + + private SimpleValue createDiscriminator(RootClass entity) { + return new BasicValue(metadataBuildingContext, entity.getTable()); + } } 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 6b4ebec72f..4cc1b9eaf8 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 @@ -223,8 +223,14 @@ public class GrailsDomainBinder implements AdditionalMappingContributor, TypeCon DiscriminatorPropertyBinder discriminatorPropertyBinder = new DiscriminatorPropertyBinder( metadataBuildingContext, - new SimpleValueColumnBinder(), - new ColumnConfigToColumnBinder()); + mappingCacheHolder, + new ConfiguredDiscriminatorBinder( + new SimpleValueColumnBinder(), + new ColumnConfigToColumnBinder() + ), + new DefaultDiscriminatorBinder( + new SimpleValueColumnBinder() + )); RootBinder rootBinder = new RootBinder( dataSourceName, diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/RootBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/RootBinder.java index ef1e65241c..07a401bc82 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/RootBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/RootBinder.java @@ -68,7 +68,7 @@ public class RootBinder { @Nonnull GrailsHibernatePersistentEntity entity, @Nonnull InFlightMetadataCollector mappings) { if (mappings.getEntityBinding(entity.getName()) != null) { - LOG.info("[RootBinder] Class [" + entity.getName() + "] is already mapped, skipping.. "); + LOG.warn("[RootBinder] Class [" + entity.getName() + "] is already mapped, skipping.. "); return; } @@ -76,12 +76,13 @@ public class RootBinder { RootClass root = rootPersistentClassCommonValuesBinder.bindRootPersistentClassCommonValues( entity, children, mappings); - Mapping m = entity.getMappedForm(); if (!children.isEmpty() && entity.isTablePerHierarchy()) { - discriminatorPropertyBinder.bindDiscriminatorProperty(root, m); + discriminatorPropertyBinder.bindDiscriminatorProperty(root); } + Mapping m = entity.getMappedForm(); + // bind the sub classes children.forEach(sub -> subClassBinder.bindSubClass(sub, root, mappings, m)); 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 2ff68e294a..88db2b6048 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 @@ -2,7 +2,7 @@ package org.grails.orm.hibernate.cfg.domainbinding import grails.gorm.specs.HibernateGormDatastoreSpec import org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty +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 @@ -120,7 +120,7 @@ class CollectionBinderSpec extends HibernateGormDatastoreSpec { SubclassMappingBinder subclassMappingBinder = new SubclassMappingBinder(metadataBuildingContext, joinedSubClassBinder, unionSubclassBinder, singleTableSubclassBinder, classPropertiesBinder) SubClassBinder subClassBinder = new SubClassBinder(binder.getMappingCacheHolder(), subclassMappingBinder, multiTenantFilterBinder, defaultColumnNameFetcher, "dataSource") RootPersistentClassCommonValuesBinder rootPersistentClassCommonValuesBinder = new RootPersistentClassCommonValuesBinder(metadataBuildingContext, namingStrategy, identityBinder, versionBinder, classBinder, classPropertiesBinder) - DiscriminatorPropertyBinder discriminatorPropertyBinder = new DiscriminatorPropertyBinder(metadataBuildingContext, new org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueColumnBinder(), new ColumnConfigToColumnBinder()) + 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, defaultColumnNameFetcher, rootPersistentClassCommonValuesBinder, discriminatorPropertyBinder) return [ @@ -167,7 +167,7 @@ class CollectionBinderSpec extends HibernateGormDatastoreSpec { rootClass.setEntityName(personEntity.name) rootClass.setTable(collector.addTable(null, null, "PERSON", null, false, binder.getMetadataBuildingContext())) - def petsProp = personEntity.getPropertyByName("pets") as HibernatePersistentProperty + def petsProp = personEntity.getPropertyByName("pets") as HibernateToManyProperty when: def collection = collectionBinder.bindCollection(petsProp, rootClass, collector, "") 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 218abedec7..b75fa6a9e7 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 @@ -180,7 +180,7 @@ class GrailsPropertyBinderSpec extends HibernateGormDatastoreSpec { SubclassMappingBinder subclassMappingBinder = new SubclassMappingBinder(metadataBuildingContext, joinedSubClassBinder, unionSubclassBinder, singleTableSubclassBinder, classPropertiesBinder) SubClassBinder subClassBinder = new SubClassBinder(binder.getMappingCacheHolder(), subclassMappingBinder, multiTenantFilterBinder, defaultColumnNameFetcher, "dataSource") RootPersistentClassCommonValuesBinder rootPersistentClassCommonValuesBinder = new RootPersistentClassCommonValuesBinder(metadataBuildingContext, namingStrategy, identityBinder, versionBinder, classBinder, classPropertiesBinder) - DiscriminatorPropertyBinder discriminatorPropertyBinder = new DiscriminatorPropertyBinder(metadataBuildingContext, new org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueColumnBinder(), new ColumnConfigToColumnBinder()) + 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, defaultColumnNameFetcher, rootPersistentClassCommonValuesBinder, discriminatorPropertyBinder) return [ diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ConfiguredDiscriminatorBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ConfiguredDiscriminatorBinderSpec.groovy new file mode 100644 index 0000000000..4643b6990e --- /dev/null +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ConfiguredDiscriminatorBinderSpec.groovy @@ -0,0 +1,149 @@ +package org.grails.orm.hibernate.cfg.domainbinding.binder + +import grails.gorm.annotation.Entity +import grails.gorm.specs.HibernateGormDatastoreSpec +import org.grails.orm.hibernate.cfg.ColumnConfig +import org.grails.orm.hibernate.cfg.DiscriminatorConfig +import org.hibernate.boot.spi.MetadataBuildingContext +import org.hibernate.mapping.BasicValue +import org.hibernate.mapping.Formula +import org.hibernate.mapping.RootClass +import org.hibernate.mapping.Table +import spock.lang.Subject + +class ConfiguredDiscriminatorBinderSpec extends HibernateGormDatastoreSpec { + + @Subject + ConfiguredDiscriminatorBinder binder + + MetadataBuildingContext metadataBuildingContext + + def setup() { + def domainBinder = getGrailsDomainBinder() + metadataBuildingContext = domainBinder.getMetadataBuildingContext() + binder = new ConfiguredDiscriminatorBinder( + new SimpleValueColumnBinder(), + new ColumnConfigToColumnBinder() + ) + } + + private RootClass createRootClass() { + def rootClass = new RootClass(metadataBuildingContext) + rootClass.setEntityName(ConfiguredDiscriminatorBinderSpecEntity.name) + rootClass.setJpaEntityName(ConfiguredDiscriminatorBinderSpecEntity.name) + rootClass.setClassName(ConfiguredDiscriminatorBinderSpecEntity.name) + rootClass.setTable(new Table("orm", "CONFIGURED_DISCRIMINATOR_TEST")) + return rootClass + } + + private BasicValue createDiscriminator(RootClass rootClass) { + return new BasicValue(metadataBuildingContext, rootClass.getTable()) + } + + def "test bindConfiguredDiscriminator with value only"() { + given: + def rootClass = createRootClass() + def discriminator = createDiscriminator(rootClass) + def config = new DiscriminatorConfig(value: "CUSTOM_VALUE") + + when: + binder.bindConfiguredDiscriminator(rootClass, discriminator, config) + + then: + rootClass.getDiscriminatorValue() == "CUSTOM_VALUE" + discriminator.getColumns().iterator().next().getName() == GrailsDomainBinder.JPA_DEFAULT_DISCRIMINATOR_TYPE + } + + def "test bindConfiguredDiscriminator with custom string type"() { + given: + def rootClass = createRootClass() + def discriminator = createDiscriminator(rootClass) + def config = new DiscriminatorConfig(value: "TEST", type: "integer") + + when: + binder.bindConfiguredDiscriminator(rootClass, discriminator, config) + + then: + rootClass.getDiscriminatorValue() == "TEST" + discriminator.getTypeName() == "integer" + } + + def "test bindConfiguredDiscriminator with class type"() { + given: + def rootClass = createRootClass() + def discriminator = createDiscriminator(rootClass) + def config = new DiscriminatorConfig(value: "TEST", type: String.class) + + when: + binder.bindConfiguredDiscriminator(rootClass, discriminator, config) + + then: + rootClass.getDiscriminatorValue() == "TEST" + discriminator.getTypeName() == "java.lang.String" + } + + def "test bindConfiguredDiscriminator with insertable false"() { + given: + def rootClass = createRootClass() + def discriminator = createDiscriminator(rootClass) + def config = new DiscriminatorConfig(value: "TEST", insertable: false) + + when: + binder.bindConfiguredDiscriminator(rootClass, discriminator, config) + + then: + rootClass.getDiscriminatorValue() == "TEST" + !rootClass.isDiscriminatorInsertable() + } + + def "test bindConfiguredDiscriminator with formula"() { + given: + def rootClass = createRootClass() + def discriminator = createDiscriminator(rootClass) + def config = new DiscriminatorConfig(value: "TEST", formula: "case when type=1 then 'A' else 'B' end") + + when: + binder.bindConfiguredDiscriminator(rootClass, discriminator, config) + + then: + rootClass.getDiscriminatorValue() == "TEST" + discriminator.getSelectables().iterator().next() instanceof Formula + ((Formula) discriminator.getSelectables().iterator().next()).getFormula() == "case when type=1 then 'A' else 'B' end" + } + + def "test bindConfiguredDiscriminator with custom column name"() { + given: + def rootClass = createRootClass() + def discriminator = createDiscriminator(rootClass) + def columnConfig = new ColumnConfig(name: "MY_DISCRIMINATOR") + def config = new DiscriminatorConfig(value: "TEST", column: columnConfig) + + when: + binder.bindConfiguredDiscriminator(rootClass, discriminator, config) + + then: + rootClass.getDiscriminatorValue() == "TEST" + discriminator.getColumns().iterator().next().getName() == "MY_DISCRIMINATOR" + } + + def "test resolveTypeName with null returns string"() { + expect: + binder.resolveTypeName(null) == "string" + } + + def "test resolveTypeName with Class returns class name"() { + expect: + binder.resolveTypeName(Integer.class) == "java.lang.Integer" + } + + def "test resolveTypeName with String returns same value"() { + expect: + binder.resolveTypeName("custom") == "custom" + } +} + +@Entity +class ConfiguredDiscriminatorBinderSpecEntity { + Long id + String name +} diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/DefaultDiscriminatorBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/DefaultDiscriminatorBinderSpec.groovy new file mode 100644 index 0000000000..365da4f9a3 --- /dev/null +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/DefaultDiscriminatorBinderSpec.groovy @@ -0,0 +1,25 @@ +package org.grails.orm.hibernate.cfg.domainbinding.binder + +import spock.lang.Specification +import spock.lang.Subject + +/** + * Tests for DefaultDiscriminatorBinder focusing on logic rather than Hibernate integration + * since many Hibernate 7 classes are sealed and cannot be mocked. + */ +class DefaultDiscriminatorBinderSpec extends Specification { + + @Subject + DefaultDiscriminatorBinder binder + + SimpleValueColumnBinder simpleValueColumnBinder = Mock() + + def setup() { + binder = new DefaultDiscriminatorBinder(simpleValueColumnBinder) + } + + def "test constructor sets dependencies correctly"() { + expect: + binder.simpleValueColumnBinder == simpleValueColumnBinder + } +} diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/DiscriminatorPropertyBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/DiscriminatorPropertyBinderSpec.groovy index e63f76b4d3..0741d669df 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/DiscriminatorPropertyBinderSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/DiscriminatorPropertyBinderSpec.groovy @@ -4,117 +4,113 @@ import grails.gorm.annotation.Entity import grails.gorm.specs.HibernateGormDatastoreSpec import org.grails.orm.hibernate.cfg.DiscriminatorConfig import org.grails.orm.hibernate.cfg.Mapping -import org.grails.orm.hibernate.cfg.PropertyConfig import org.hibernate.boot.spi.MetadataBuildingContext +import org.hibernate.mapping.BasicValue import org.hibernate.mapping.Formula import org.hibernate.mapping.RootClass -import org.hibernate.mapping.SimpleValue import org.hibernate.mapping.Table -import spock.lang.Shared +import spock.lang.Subject class DiscriminatorPropertyBinderSpec extends HibernateGormDatastoreSpec { + @Subject DiscriminatorPropertyBinder binder - MetadataBuildingContext metadataBuildingContext - void setup() { - manager.addAllDomainClasses([DiscriminatorTestEntity]) - def gdb = getGrailsDomainBinder() - metadataBuildingContext = gdb.getMetadataBuildingContext() + MetadataBuildingContext metadataBuildingContext + ConfiguredDiscriminatorBinder configuredBinder + DefaultDiscriminatorBinder defaultBinder + + def setup() { + def domainBinder = getGrailsDomainBinder() + metadataBuildingContext = domainBinder.getMetadataBuildingContext() + def simpleValueColumnBinder = new SimpleValueColumnBinder() + def columnConfigToColumnBinder = new ColumnConfigToColumnBinder() + configuredBinder = new ConfiguredDiscriminatorBinder(simpleValueColumnBinder, columnConfigToColumnBinder) + defaultBinder = new DefaultDiscriminatorBinder(simpleValueColumnBinder) binder = new DiscriminatorPropertyBinder( metadataBuildingContext, - new SimpleValueColumnBinder(), - new ColumnConfigToColumnBinder() + domainBinder.getMappingCacheHolder(), + configuredBinder, + defaultBinder ) } - def "test bindDiscriminatorProperty with default values"() { - given: + private RootClass createRootClass() { def rootClass = new RootClass(metadataBuildingContext) - rootClass.setEntityName(DiscriminatorTestEntity.name) - rootClass.setClassName(DiscriminatorTestEntity.name) - rootClass.setTable(new Table("DISCRIMINATOR_TEST_ENTITY")) + rootClass.setEntityName(DiscriminatorPropertyBinderSpecEntity.name) + rootClass.setJpaEntityName(DiscriminatorPropertyBinderSpecEntity.name) + rootClass.setClassName(DiscriminatorPropertyBinderSpecEntity.name) + rootClass.setTable(new Table("orm", "DISCRIMINATOR_TEST_ENTITY")) + return rootClass + } + + def "test bindDiscriminatorProperty with no discriminator config uses default binder"() { + given: + def rootClass = createRootClass() def mapping = new Mapping() + getGrailsDomainBinder().getMappingCacheHolder().cacheMapping(DiscriminatorPropertyBinderSpecEntity, mapping) when: - binder.bindDiscriminatorProperty(rootClass, mapping) + binder.bindDiscriminatorProperty(rootClass) then: rootClass.getDiscriminator() != null - rootClass.getDiscriminatorValue() == DiscriminatorTestEntity.name - rootClass.getDiscriminator().getTypeName() == "string" - rootClass.getDiscriminator().getColumnSpan() == 1 + rootClass.getDiscriminator() instanceof BasicValue + rootClass.getDiscriminatorValue() == DiscriminatorPropertyBinderSpecEntity.name rootClass.getDiscriminator().getColumns().iterator().next().getName() == GrailsDomainBinder.JPA_DEFAULT_DISCRIMINATOR_TYPE } - def "test bindDiscriminatorProperty with custom value"() { + def "test bindDiscriminatorProperty with discriminator config uses configured binder"() { given: - def rootClass = new RootClass(metadataBuildingContext) - rootClass.setEntityName(DiscriminatorTestEntity.name) - rootClass.setClassName(DiscriminatorTestEntity.name) - rootClass.setTable(new Table("DISCRIMINATOR_TEST_ENTITY")) + def rootClass = createRootClass() def mapping = new Mapping() - mapping.setDiscriminator(new DiscriminatorConfig(value: "CUSTOM_VALUE")) + def discriminatorConfig = new DiscriminatorConfig(value: "TEST") + mapping.setDiscriminator(discriminatorConfig) + getGrailsDomainBinder().getMappingCacheHolder().cacheMapping(DiscriminatorPropertyBinderSpecEntity, mapping) when: - binder.bindDiscriminatorProperty(rootClass, mapping) + binder.bindDiscriminatorProperty(rootClass) then: - rootClass.getDiscriminatorValue() == "CUSTOM_VALUE" + rootClass.getDiscriminator() != null + rootClass.getDiscriminator() instanceof BasicValue + rootClass.getDiscriminatorValue() == "TEST" + rootClass.getDiscriminator().getColumns().iterator().next().getName() == GrailsDomainBinder.JPA_DEFAULT_DISCRIMINATOR_TYPE } - def "test bindDiscriminatorProperty with custom type"() { + def "test bindDiscriminatorProperty with custom discriminator column name"() { given: - def rootClass = new RootClass(metadataBuildingContext) - rootClass.setEntityName(DiscriminatorTestEntity.name) - rootClass.setClassName(DiscriminatorTestEntity.name) - rootClass.setTable(new Table("DISCRIMINATOR_TEST_ENTITY")) + def rootClass = createRootClass() def mapping = new Mapping() - mapping.setDiscriminator(new DiscriminatorConfig(type: "integer")) + def discriminatorConfig = new DiscriminatorConfig(value: "TEST", column: [name: "MY_TYPE"]) + mapping.setDiscriminator(discriminatorConfig) + getGrailsDomainBinder().getMappingCacheHolder().cacheMapping(DiscriminatorPropertyBinderSpecEntity, mapping) when: - binder.bindDiscriminatorProperty(rootClass, mapping) + binder.bindDiscriminatorProperty(rootClass) then: - rootClass.getDiscriminator().getTypeName() == "integer" + rootClass.getDiscriminator().getColumns().iterator().next().getName() == "MY_TYPE" } def "test bindDiscriminatorProperty with formula"() { given: - def rootClass = new RootClass(metadataBuildingContext) - rootClass.setEntityName(DiscriminatorTestEntity.name) - rootClass.setClassName(DiscriminatorTestEntity.name) - rootClass.setTable(new Table("DISCRIMINATOR_TEST_ENTITY")) + def rootClass = createRootClass() def mapping = new Mapping() - mapping.setDiscriminator(new DiscriminatorConfig(formula: "case when type=1 then 'A' else 'B' end")) + def discriminatorConfig = new DiscriminatorConfig(value: "TEST", formula: "case when type=1 then 'A' else 'B' end") + mapping.setDiscriminator(discriminatorConfig) + getGrailsDomainBinder().getMappingCacheHolder().cacheMapping(DiscriminatorPropertyBinderSpecEntity, mapping) when: - binder.bindDiscriminatorProperty(rootClass, mapping) + binder.bindDiscriminatorProperty(rootClass) then: rootClass.getDiscriminator().getSelectables().iterator().next() instanceof Formula - ((Formula)rootClass.getDiscriminator().getSelectables().iterator().next()).getFormula() == "case when type=1 then 'A' else 'B' end" - } - - def "test bindDiscriminatorProperty with custom column name"() { - given: - def rootClass = new RootClass(metadataBuildingContext) - rootClass.setEntityName(DiscriminatorTestEntity.name) - rootClass.setClassName(DiscriminatorTestEntity.name) - rootClass.setTable(new Table("DISCRIMINATOR_TEST_ENTITY")) - def mapping = new Mapping() - mapping.setDiscriminator(new DiscriminatorConfig(column: [name: "MY_DISCRIMINATOR"])) - - when: - binder.bindDiscriminatorProperty(rootClass, mapping) - - then: - rootClass.getDiscriminator().getColumns().iterator().next().getName() == "MY_DISCRIMINATOR" } } @Entity -class DiscriminatorTestEntity { +class DiscriminatorPropertyBinderSpecEntity { Long id String name } diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/RootBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/RootBinderSpec.groovy index e7a80f6008..587631e269 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/RootBinderSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/RootBinderSpec.groovy @@ -60,7 +60,7 @@ class RootBinderSpec extends HibernateGormDatastoreSpec { then: 1 * rootPersistentClassCommonValuesBinder.bindRootPersistentClassCommonValues(entity, [], mappings) >> rootClass - 0 * discriminatorPropertyBinder.bindDiscriminatorProperty(_, _) + 0 * discriminatorPropertyBinder.bindDiscriminatorProperty(_) 0 * subClassBinder.bindSubClass(_, _, _, _) 1 * multiTenantFilterBinder.addMultiTenantFilterIfNecessary(entity, rootClass, mappings, defaultColumnNameFetcher) mappings.getEntityBinding("Parent") == rootClass @@ -87,7 +87,7 @@ class RootBinderSpec extends HibernateGormDatastoreSpec { then: 1 * rootPersistentClassCommonValuesBinder.bindRootPersistentClassCommonValues(entity, [childEntity], mappings) >> rootClass - 1 * discriminatorPropertyBinder.bindDiscriminatorProperty(rootClass, mapping) + 1 * discriminatorPropertyBinder.bindDiscriminatorProperty(rootClass) 1 * subClassBinder.bindSubClass(childEntity, rootClass, mappings, mapping) 1 * multiTenantFilterBinder.addMultiTenantFilterIfNecessary(entity, rootClass, mappings, defaultColumnNameFetcher) mappings.getEntityBinding("Parent") == rootClass 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 107e10b20c..f8e24f4e63 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 @@ -2,7 +2,7 @@ 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.HibernatePersistentProperty +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 @@ -119,7 +119,7 @@ class ListSecondPassBinderSpec extends HibernateGormDatastoreSpec { SubclassMappingBinder subclassMappingBinder = new SubclassMappingBinder(metadataBuildingContext, joinedSubClassBinder, unionSubclassBinder, singleTableSubclassBinder, classPropertiesBinder) SubClassBinder subClassBinder = new SubClassBinder(binder.getMappingCacheHolder(), subclassMappingBinder, multiTenantFilterBinder, defaultColumnNameFetcher, "dataSource") RootPersistentClassCommonValuesBinder rootPersistentClassCommonValuesBinder = new RootPersistentClassCommonValuesBinder(metadataBuildingContext, namingStrategy, identityBinder, versionBinder, classBinder, classPropertiesBinder) - DiscriminatorPropertyBinder discriminatorPropertyBinder = new DiscriminatorPropertyBinder(metadataBuildingContext, new org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueColumnBinder(), new ColumnConfigToColumnBinder()) + 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, defaultColumnNameFetcher, rootPersistentClassCommonValuesBinder, discriminatorPropertyBinder) return [ @@ -166,7 +166,7 @@ class ListSecondPassBinderSpec extends HibernateGormDatastoreSpec { rootClass.setEntityName(personEntity.name) rootClass.setTable(collector.addTable(null, null, "PERSON", null, false, binder.getMetadataBuildingContext())) - def petsProp = personEntity.getPropertyByName("pets") as HibernatePersistentProperty + def petsProp = personEntity.getPropertyByName("pets") as HibernateToManyProperty when: def collection = collectionBinder.bindCollection(petsProp, rootClass, collector, "") 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 520b87c209..d54e67f3eb 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 @@ -119,7 +119,7 @@ class MapSecondPassBinderSpec extends HibernateGormDatastoreSpec { SubclassMappingBinder subclassMappingBinder = new SubclassMappingBinder(metadataBuildingContext, joinedSubClassBinder, unionSubclassBinder, singleTableSubclassBinder, classPropertiesBinder) SubClassBinder subClassBinder = new SubClassBinder(binder.getMappingCacheHolder(), subclassMappingBinder, multiTenantFilterBinder, defaultColumnNameFetcher, "dataSource") RootPersistentClassCommonValuesBinder rootPersistentClassCommonValuesBinder = new RootPersistentClassCommonValuesBinder(metadataBuildingContext, namingStrategy, identityBinder, versionBinder, classBinder, classPropertiesBinder) - DiscriminatorPropertyBinder discriminatorPropertyBinder = new DiscriminatorPropertyBinder(metadataBuildingContext, new org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueColumnBinder(), new ColumnConfigToColumnBinder()) + 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, defaultColumnNameFetcher, rootPersistentClassCommonValuesBinder, discriminatorPropertyBinder) return [
