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 34866ea7fa1a024c3870bb47712ddef45b6cf0c4 Author: Walter Duque de Estrada <[email protected]> AuthorDate: Sun Feb 15 17:09:37 2026 -0600 Refactor natural identifier binding to support composite primary keys and fix mapping DSL configuration for natural identifiers. --- .../orm/hibernate/cfg/CompositeIdentity.groovy | 17 ++++++++ .../org/grails/orm/hibernate/cfg/Identity.groovy | 3 +- .../binder/NaturalIdentifierBinder.java | 5 +-- .../domainbinding/hibernate/HibernateIdentity.java | 12 ++++++ .../hibernate/HibernateMappingBuilder.groovy | 46 +++++++++++----------- .../NaturalIdentifierBinderSpec.groovy | 33 ++++++++++++++++ 6 files changed, 89 insertions(+), 27 deletions(-) diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/CompositeIdentity.groovy b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/CompositeIdentity.groovy index bdb85a70b2..b39454ddc3 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/CompositeIdentity.groovy +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/CompositeIdentity.groovy @@ -40,4 +40,21 @@ class CompositeIdentity extends Property implements HibernateIdentity { * The composite id class */ Class compositeClass + /** + * The natural id definition + */ + NaturalId natural + + /** + * Define the natural id + * @param naturalIdDef The callable + * @return This id + */ + CompositeIdentity naturalId(@DelegatesTo(NaturalId) Closure naturalIdDef) { + this.natural = new NaturalId() + naturalIdDef.setDelegate(this.natural) + naturalIdDef.setResolveStrategy(Closure.DELEGATE_ONLY) + naturalIdDef.call() + return this + } } diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/Identity.groovy b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/Identity.groovy index 87fdc352a4..45039669ce 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/Identity.groovy +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/Identity.groovy @@ -80,7 +80,8 @@ class Identity extends Property implements HibernateIdentity { * @return This id */ Identity naturalId(@DelegatesTo(NaturalId) Closure naturalIdDef) { - naturalIdDef.setDelegate(new NaturalId()) + this.natural = new NaturalId() + naturalIdDef.setDelegate(this.natural) naturalIdDef.setResolveStrategy(Closure.DELEGATE_ONLY) naturalIdDef.call() return this diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/NaturalIdentifierBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/NaturalIdentifierBinder.java index 0dec49abab..b27f4c1e8d 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/NaturalIdentifierBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/NaturalIdentifierBinder.java @@ -3,6 +3,7 @@ package org.grails.orm.hibernate.cfg.domainbinding.binder; import org.apache.commons.collections.CollectionUtils; import org.grails.orm.hibernate.cfg.Identity; import org.grails.orm.hibernate.cfg.Mapping; +import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateIdentity; import org.grails.orm.hibernate.cfg.domainbinding.util.UniqueNameGenerator; import org.hibernate.mapping.PersistentClass; @@ -26,9 +27,7 @@ public class NaturalIdentifierBinder { public void bindNaturalIdentifier(Mapping mapping, PersistentClass persistentClass) { Optional.ofNullable(mapping.getIdentity()) - .filter(Identity.class::isInstance) - .map(Identity.class::cast) - .map(Identity::getNatural) + .map(HibernateIdentity::getNatural) .ifPresent(naturalId -> { if(CollectionUtils.isEmpty(naturalId.getPropertyNames())) { return; diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateIdentity.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateIdentity.java index 2694ce20fd..3e1355a7c9 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateIdentity.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateIdentity.java @@ -1,7 +1,19 @@ package org.grails.orm.hibernate.cfg.domainbinding.hibernate; +import org.grails.orm.hibernate.cfg.NaturalId; + /** * A marker interface for single and composite identity configurations in GORM for Hibernate. */ public interface HibernateIdentity { + /** + * @return The natural id definition + */ + NaturalId getNatural(); + + /** + * Sets the natural id definition + * @param natural The natural id definition + */ + void setNatural(NaturalId natural); } diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingBuilder.groovy b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingBuilder.groovy index 7fc2f09ca6..048ccc2489 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingBuilder.groovy +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingBuilder.groovy @@ -28,6 +28,7 @@ import jakarta.persistence.AccessType import org.grails.orm.hibernate.cfg.CacheConfig import org.grails.orm.hibernate.cfg.ColumnConfig import org.grails.orm.hibernate.cfg.CompositeIdentity +import org.grails.orm.hibernate.cfg.Identity import org.grails.orm.hibernate.cfg.JoinTable import org.grails.orm.hibernate.cfg.Mapping import org.grails.orm.hibernate.cfg.NaturalId @@ -393,43 +394,42 @@ class HibernateMappingBuilder implements MappingConfigurationBuilder<Mapping, Pr if (args.composite) { mapping.identity = new CompositeIdentity(propertyNames:args.composite as String[]) if (args.compositeClass) { - mapping.identity.compositeClass = args.compositeClass + ((CompositeIdentity)mapping.identity).compositeClass = (Class)args.compositeClass } } else { if (args?.generator) { - mapping.identity.generator = args.remove('generator') + ((Identity)mapping.identity).generator = args.remove('generator').toString() } if (args?.name) { - mapping.identity.name = args.remove('name').toString() + ((Identity)mapping.identity).name = args.remove('name').toString() } if (args?.params) { - def params = args.remove('params') + def params = (Map)args.remove('params') for (entry in params) { params[entry.key] = entry.value?.toString() } - mapping.identity.params = params + ((Identity)mapping.identity).params = params } - if (args?.natural) { - def naturalArgs = args.remove('natural') - def propertyNames = naturalArgs instanceof Map ? naturalArgs.remove('properties') : naturalArgs - - if (propertyNames) { - def ni = new NaturalId() - ni.mutable = (naturalArgs instanceof Map) && naturalArgs.mutable ?: false - if (propertyNames instanceof List) { - ni.propertyNames = propertyNames - } - else { - ni.propertyNames = [propertyNames.toString()] - } - mapping.identity.natural = ni + } + if (args?.natural) { + def naturalArgs = args.remove('natural') + def propertyNames = naturalArgs instanceof Map ? ((Map)naturalArgs).remove('properties') : naturalArgs + + if (propertyNames) { + def ni = new NaturalId() + ni.mutable = (naturalArgs instanceof Map) && ((Map)naturalArgs).mutable ?: false + if (propertyNames instanceof List) { + ni.propertyNames = (List<String>)propertyNames } + else { + ni.propertyNames = [propertyNames.toString()] + } + mapping.identity.natural = ni } - // still more arguments? - if (args) { - handleMethodMissing("id", [args] as Object[]) - } + } + if (!args.composite && args) { + handleMethodMissing("id", [args] as Object[]) } } diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/NaturalIdentifierBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/NaturalIdentifierBinderSpec.groovy index 036d04e14b..6abb9c8a30 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/NaturalIdentifierBinderSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/NaturalIdentifierBinderSpec.groovy @@ -1,6 +1,7 @@ package org.grails.orm.hibernate.cfg.domainbinding import grails.gorm.specs.HibernateGormDatastoreSpec +import org.grails.orm.hibernate.cfg.CompositeIdentity import org.grails.orm.hibernate.cfg.Identity import org.grails.orm.hibernate.cfg.Mapping import org.grails.orm.hibernate.cfg.NaturalId @@ -103,6 +104,38 @@ class NaturalIdentifierBinderSpec extends HibernateGormDatastoreSpec { } } + void "test bindNaturalIdentifier with CompositeIdentity"() { + given: + def mapping = Mock(Mapping.class) + def identity = Mock(CompositeIdentity) + def naturalId = Mock(NaturalId) + def property = new Property() + property.setName("id1") + def value = Mock(Value) + property.setValue(value) + Table table = Mock(Table) + def id1 = "id1" + mapping.getIdentity() >> identity + identity.getNatural() >> naturalId + naturalId.getPropertyNames() >> [id1] + naturalId.isMutable() >> false + def rootClass = new RootClass(getGrailsDomainBinder().getMetadataBuildingContext()) + rootClass.addProperty(property) + rootClass.setTable(table) + value.getSelectables() >> [] + + def uniqueNameGenerator = Mock(UniqueNameGenerator) + def binder = new NaturalIdentifierBinder(uniqueNameGenerator) + + when: + binder.bindNaturalIdentifier(mapping, rootClass) + + then: + 1 * uniqueNameGenerator.setGeneratedUniqueName(_) + property.isNaturalIdentifier() + 1 * table.addUniqueKey(_) + } + void "test bindNaturalIdentifier when no natural id is defined"() { given: def mapping = Mock(Mapping.class)
