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 4db915f661e1f01d9eafb72e20bc162874c36f16 Author: Walter Duque de Estrada <[email protected]> AuthorDate: Wed Feb 11 21:31:15 2026 -0600 Now delegate to getChildEntities --- .../cfg/GrailsHibernatePersistentEntity.java | 133 ++++++++++++--------- .../secondpass/CollectionSecondPassBinder.java | 36 ++---- .../cfg/GrailsHibernatePersistentEntitySpec.groovy | 114 +++++++++++------- 3 files changed, 157 insertions(+), 126 deletions(-) diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentEntity.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentEntity.java index 1e59ac83cd..65900d3935 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentEntity.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentEntity.java @@ -1,64 +1,66 @@ package org.grails.orm.hibernate.cfg; - -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -import jakarta.annotation.Nonnull; - -import org.hibernate.boot.spi.InFlightMetadataCollector; - -import org.grails.datastore.mapping.model.PersistentEntity; -import org.grails.datastore.mapping.model.PersistentProperty; -import org.grails.datastore.mapping.model.config.GormProperties; -import org.grails.orm.hibernate.cfg.domainbinding.ConfigureDerivedPropertiesConsumer; -import org.grails.orm.hibernate.cfg.domainbinding.DefaultColumnNameFetcher; -import org.grails.orm.hibernate.cfg.domainbinding.NamespaceNameExtractor; + + import java.util.Collection; + import java.util.List; + import java.util.Objects; + import java.util.Optional; + import java.util.Set; + import java.util.stream.Collectors; + import java.util.stream.Stream; + + import jakarta.annotation.Nonnull; + + import org.hibernate.boot.spi.InFlightMetadataCollector; + + import org.grails.datastore.mapping.model.PersistentEntity; + import org.grails.datastore.mapping.model.PersistentProperty; + import org.grails.datastore.mapping.model.config.GormProperties; + import org.grails.orm.hibernate.cfg.domainbinding.ConfigureDerivedPropertiesConsumer; + import org.grails.orm.hibernate.cfg.domainbinding.DefaultColumnNameFetcher; + import org.grails.orm.hibernate.cfg.domainbinding.NamespaceNameExtractor; + + import static org.grails.orm.hibernate.cfg.GrailsDomainBinder.JPA_DEFAULT_DISCRIMINATOR_TYPE; /** - * Common interface for Hibernate persistent entities - */ -public interface GrailsHibernatePersistentEntity extends PersistentEntity { - Mapping getMappedForm(); - - - @Nonnull default GrailsHibernatePersistentEntity getHibernateRootEntity() { - return (GrailsHibernatePersistentEntity) getRootEntity(); - } - - default Mapping getRootMapping() { - return getHibernateRootEntity().getMappedForm(); - } - - default boolean isTablePerHierarchySubclass() { - Mapping rootMapping = getRootMapping(); - return !this.isRoot() && (rootMapping == null || rootMapping.getTablePerHierarchy()); - } - - default java.util.Set<String> buildDiscriminatorSet() { - java.util.Set<String> theSet = new java.util.HashSet<>(); - - String discriminator = getDiscriminatorValue(); - Mapping rootMapping = getRootMapping(); - String quote = "'"; - if (rootMapping != null && rootMapping.getDatasources() != null) { - DiscriminatorConfig discriminatorConfig = rootMapping.getDiscriminator(); - if(discriminatorConfig != null && discriminatorConfig.getType() != null && !discriminatorConfig.getType().equals("string")) - quote = ""; - } - theSet.add(quote + discriminator + quote); - - final java.util.Collection<PersistentEntity> childEntities = getMappingContext().getDirectChildEntities(this); - for (PersistentEntity subClass : childEntities) { - if (subClass instanceof GrailsHibernatePersistentEntity) { - theSet.addAll(((GrailsHibernatePersistentEntity) subClass).buildDiscriminatorSet()); - } - } - return theSet; - } - - @Override - GrailsHibernatePersistentProperty getIdentity(); + * Common interface for Hibernate persistent entities + */ + public interface GrailsHibernatePersistentEntity extends PersistentEntity { + Mapping getMappedForm(); + + + @Nonnull default GrailsHibernatePersistentEntity getHibernateRootEntity() { + return (GrailsHibernatePersistentEntity) getRootEntity(); + } + + default Mapping getRootMapping() { + return getHibernateRootEntity().getMappedForm(); + } + + default boolean isTablePerHierarchySubclass() { + Mapping rootMapping = getRootMapping(); + return !this.isRoot() && (rootMapping == null || rootMapping.getTablePerHierarchy()); + } + + default Set<String> buildDiscriminatorSet() { + String quote = Optional.ofNullable(getRootMapping()) + .filter(m -> m.getDatasources() != null) + .map(Mapping::getDiscriminator) + .filter(config -> config.getType() != null && !config.getType().equals("string")) + .map(config -> "") + .orElse("'"); + + String quotedDiscriminator = quote + getDiscriminatorValue() + quote; + + return Stream.concat( + Stream.of(quotedDiscriminator), + getChildEntities().stream() + .map(GrailsHibernatePersistentEntity::buildDiscriminatorSet) + .flatMap(Collection::stream) + ).collect(Collectors.toSet()); + } + + @Override + GrailsHibernatePersistentProperty getIdentity(); @Override GrailsHibernatePersistentProperty[] getCompositeIdentity(); @@ -103,6 +105,10 @@ public interface GrailsHibernatePersistentEntity extends PersistentEntity { @Override GrailsHibernatePersistentProperty getVersion(); + default List<GrailsHibernatePersistentEntity> getChildEntities() { + return getChildEntities(getDataSourceName()); + } + default List<GrailsHibernatePersistentEntity> getChildEntities(String dataSourceName) { return getMappingContext() .getDirectChildEntities(this) @@ -146,5 +152,16 @@ public interface GrailsHibernatePersistentEntity extends PersistentEntity { } + default String getDiscriminatorColumnName() { + return Optional.ofNullable(getRootMapping()) + .map(Mapping::getDiscriminator) + .map(GrailsHibernatePersistentEntity::resolveDiscriminatorValue) + .orElse(JPA_DEFAULT_DISCRIMINATOR_TYPE); + + } + + private static String resolveDiscriminatorValue(DiscriminatorConfig discriminatorConfig) { + return discriminatorConfig.getColumn() != null ? discriminatorConfig.getColumn().getName() : discriminatorConfig.getFormula(); + } } 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 84c0ff8d52..79477f0b66 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java @@ -33,6 +33,7 @@ import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; import java.util.*; +import java.util.Set; import static org.grails.orm.hibernate.cfg.GrailsDomainBinder.*; @@ -91,24 +92,9 @@ public class CollectionSecondPassBinder { if (collection.isOneToMany()) { if (referenced != null && referenced.isTablePerHierarchySubclass()) { - Mapping rootMapping = referenced.getRootMapping(); - //TODO FIXME - String discriminatorColumnName = JPA_DEFAULT_DISCRIMINATOR_TYPE; - - if (rootMapping != null) { - DiscriminatorConfig discriminatorConfig = rootMapping.getDiscriminator(); - if(discriminatorConfig != null) { - final ColumnConfig discriminatorColumn = discriminatorConfig.getColumn(); - if (discriminatorColumn != null) { - discriminatorColumnName = discriminatorColumn.getName(); - } - if (discriminatorConfig.getFormula() != null) { - discriminatorColumnName = discriminatorConfig.getFormula(); - } - } - } + String discriminatorColumnName = referenced.getDiscriminatorColumnName(); //NOTE: this will build the set for the in clause if it has sublcasses - java.util.Set<String> discSet = referenced.buildDiscriminatorSet(); + Set<String> discSet = referenced.buildDiscriminatorSet(); String inclause = String.join(",", discSet); collection.setWhere(discriminatorColumnName + " in (" + inclause + ")"); @@ -149,7 +135,7 @@ public class CollectionSecondPassBinder { } // setup the primary key references - DependantValue key = createPrimaryKeyValue(mappings, (GrailsHibernatePersistentProperty) property, collection, persistentClasses); + DependantValue key = createPrimaryKeyValue(mappings, property, collection, persistentClasses); // link a bidirectional relationship if (property.isBidirectional()) { @@ -162,7 +148,7 @@ public class CollectionSecondPassBinder { } else if ((otherSide instanceof ManyToMany) || java.util.Map.class.isAssignableFrom(property.getType())) { - bindDependentKeyValue((GrailsHibernatePersistentProperty) property, key, mappings, sessionFactoryBeanName); + bindDependentKeyValue(property, key, mappings, sessionFactoryBeanName); } @@ -176,7 +162,7 @@ public class CollectionSecondPassBinder { } else { - bindDependentKeyValue((GrailsHibernatePersistentProperty) property, key, mappings, sessionFactoryBeanName); + bindDependentKeyValue(property, key, mappings, sessionFactoryBeanName); } @@ -184,11 +170,9 @@ public class CollectionSecondPassBinder { collection.setKey(key); // get cache config - if (propConfig != null) { - CacheConfig cacheConfig = propConfig.getCache(); - if (cacheConfig != null) { - collection.setCacheConcurrencyStrategy(cacheConfig.getUsage()); - } + CacheConfig cacheConfig = propConfig.getCache(); + if (cacheConfig != null) { + collection.setCacheConcurrencyStrategy(cacheConfig.getUsage()); } // if we have a many-to-many @@ -282,7 +266,7 @@ public class CollectionSecondPassBinder { } if (isEnum) { - new EnumTypeBinder().bindEnumType((GrailsHibernatePersistentProperty) property, referencedType, element, columnName); + new EnumTypeBinder().bindEnumType(property, referencedType, element, columnName); } else { diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentEntitySpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentEntitySpec.groovy index 1f7421afe2..05a8e59329 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentEntitySpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentEntitySpec.groovy @@ -2,6 +2,15 @@ package org.grails.orm.hibernate.cfg import grails.gorm.annotation.Entity import grails.gorm.specs.HibernateGormDatastoreSpec +import org.grails.datastore.mapping.model.PersistentEntity +import org.grails.datastore.mapping.model.PersistentProperty +import org.grails.datastore.mapping.model.types.TenantId +import org.grails.orm.hibernate.cfg.domainbinding.ConfigureDerivedPropertiesConsumer +import org.grails.orm.hibernate.cfg.domainbinding.DefaultColumnNameFetcher +import org.grails.orm.hibernate.connections.HibernateConnectionSourceFactory +import org.hibernate.boot.spi.InFlightMetadataCollector +import spock.lang.Specification +import org.grails.datastore.mapping.model.MappingContext class GrailsHibernatePersistentEntitySpec extends HibernateGormDatastoreSpec { @@ -24,68 +33,51 @@ class GrailsHibernatePersistentEntitySpec extends HibernateGormDatastoreSpec { given: GrailsHibernatePersistentEntity entity = getPersistentEntity(Simple) as GrailsHibernatePersistentEntity - when: - Set<String> result = entity.buildDiscriminatorSet() - - then: - result == ["'org.grails.orm.hibernate.cfg.Simple'"] as Set + expect: + entity.buildDiscriminatorSet() == ["'org.grails.orm.hibernate.cfg.Simple'"] as Set } void "test buildDiscriminatorSet with custom discriminator value"() { given: GrailsHibernatePersistentEntity entity = getPersistentEntity(CustomDiscriminator) as GrailsHibernatePersistentEntity - when: - Set<String> result = entity.buildDiscriminatorSet() - - then: - result == ["'custom_val'"] as Set + expect: + entity.buildDiscriminatorSet() == ["'custom_val'"] as Set } void "test buildDiscriminatorSet with numeric discriminator type"() { given: GrailsHibernatePersistentEntity entity = getPersistentEntity(NumericDiscriminator) as GrailsHibernatePersistentEntity - when: - Set<String> result = entity.buildDiscriminatorSet() - - then: - result == ["1"] as Set + expect: + entity.buildDiscriminatorSet() == ["1"] as Set } void "test buildDiscriminatorSet with hierarchy"() { given: - GrailsHibernatePersistentEntity entity = getPersistentEntity(Vehicle) as GrailsHibernatePersistentEntity - - when: - Set<String> result = entity.buildDiscriminatorSet() + GrailsHibernatePersistentEntity vehicle = getPersistentEntity(Vehicle) as GrailsHibernatePersistentEntity - then: - result == ["'VEHICLE'", "'CAR'", "'TRUCK'"] as Set + expect: + vehicle.buildDiscriminatorSet() == ["'VEHICLE'", "'CAR'", "'TRUCK'"] as Set } void "test getHibernateRootEntity and getRootMapping"() { given: - GrailsHibernatePersistentEntity vehicle = getPersistentEntity(Vehicle) as GrailsHibernatePersistentEntity GrailsHibernatePersistentEntity car = getPersistentEntity(Car) as GrailsHibernatePersistentEntity expect: - car.hibernateRootEntity == vehicle - car.rootMapping == vehicle.mappedForm - vehicle.hibernateRootEntity == vehicle - vehicle.rootMapping == vehicle.mappedForm + car.getHibernateRootEntity().javaClass == Vehicle + car.getRootMapping().discriminator.value == "VEHICLE" } void "test isTablePerHierarchySubclass"() { given: GrailsHibernatePersistentEntity vehicle = getPersistentEntity(Vehicle) as GrailsHibernatePersistentEntity GrailsHibernatePersistentEntity car = getPersistentEntity(Car) as GrailsHibernatePersistentEntity - GrailsHibernatePersistentEntity simple = getPersistentEntity(Simple) as GrailsHibernatePersistentEntity expect: !vehicle.isTablePerHierarchySubclass() car.isTablePerHierarchySubclass() - !simple.isTablePerHierarchySubclass() } void "test getDiscriminatorValue"() { @@ -95,9 +87,9 @@ class GrailsHibernatePersistentEntitySpec extends HibernateGormDatastoreSpec { GrailsHibernatePersistentEntity simple = getPersistentEntity(Simple) as GrailsHibernatePersistentEntity expect: - vehicle.discriminatorValue == "VEHICLE" - car.discriminatorValue == "CAR" - simple.discriminatorValue == "org.grails.orm.hibernate.cfg.Simple" + vehicle.getDiscriminatorValue() == "VEHICLE" + car.getDiscriminatorValue() == "CAR" + simple.getDiscriminatorValue() == "org.grails.orm.hibernate.cfg.Simple" } void "test getPersistentPropertiesToBind"() { @@ -121,29 +113,33 @@ class GrailsHibernatePersistentEntitySpec extends HibernateGormDatastoreSpec { then: children.size() == 2 - children.any { it.name == Car.name } - children.any { it.name == Truck.name } + children.any { it.javaClass == Car } + children.any { it.javaClass == Truck } } void "test isComponentPropertyNullable"() { given: - GrailsHibernatePersistentEntity addressOwner = getPersistentEntity(AddressOwner) as GrailsHibernatePersistentEntity - def addressProp = addressOwner.getPropertyByName("address") + GrailsHibernatePersistentEntity owner = getPersistentEntity(AddressOwner) as GrailsHibernatePersistentEntity + def addressProp = owner.getPropertyByName("address") expect: - addressOwner.isComponentPropertyNullable(addressProp) == false + owner.isComponentPropertyNullable(addressProp) == false } void "test getMultiTenantFilterCondition"() { given: - GrailsHibernatePersistentEntity entity = Spy(getPersistentEntity(Person)) as GrailsHibernatePersistentEntity - def tenantIdProp = Mock(org.grails.datastore.mapping.model.types.TenantId) + GrailsHibernatePersistentEntity entity = Spy(HibernatePersistentEntity, constructorArgs: [Person, getMappingContext()]) + def tenantIdProp = Stub(TenantId) + tenantIdProp.getName() >> "tenantId" entity.getTenantId() >> tenantIdProp - def fetcher = Mock(org.grails.orm.hibernate.cfg.domainbinding.DefaultColumnNameFetcher) - fetcher.getDefaultColumnName(tenantIdProp) >> "tenant_id" + def fetcher = Stub(DefaultColumnNameFetcher, constructorArgs: [Stub(org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy)]) + fetcher.getDefaultColumnName(_) >> "tenant_id_col" - expect: - entity.getMultiTenantFilterCondition(fetcher) == ":tenantId = tenant_id" + when: + def condition = entity.getMultiTenantFilterCondition(fetcher) + + then: + condition == ":tenantId = tenant_id_col" } void "test getSchema and getCatalog"() { @@ -175,6 +171,40 @@ class GrailsHibernatePersistentEntitySpec extends HibernateGormDatastoreSpec { then: entities.every { it.dataSourceName == "customDS" } } + + void "test buildDiscriminatorSet with dataSourceName"() { + given: + def context = getMappingContext() + GrailsHibernatePersistentEntity vehicle = Spy(HibernatePersistentEntity, constructorArgs: [Vehicle, context]) + GrailsHibernatePersistentEntity car = Spy(HibernatePersistentEntity, constructorArgs: [Car, context]) + GrailsHibernatePersistentEntity truck = Spy(HibernatePersistentEntity, constructorArgs: [Truck, context]) + + // Mock discriminator values + vehicle.getDiscriminatorValue() >> "VEHICLE" + car.getDiscriminatorValue() >> "CAR" + truck.getDiscriminatorValue() >> "TRUCK" + + // Ensure child Spies don't try to call real buildDiscriminatorSet if it's too complex, + // but here we want to test the recursion. + car.getChildEntities(_) >> [] + truck.getChildEntities(_) >> [] + + when: "Testing for DS1" + vehicle.setDataSourceName("DS1") + vehicle.getChildEntities("DS1") >> [car] + Set<String> result1 = vehicle.buildDiscriminatorSet() + + then: + result1 == ["'VEHICLE'", "'CAR'"] as Set + + when: "Testing for DS2" + vehicle.setDataSourceName("DS2") + vehicle.getChildEntities("DS2") >> [truck] + Set<String> result2 = vehicle.buildDiscriminatorSet() + + then: + result2 == ["'VEHICLE'", "'TRUCK'"] as Set + } } @Entity
