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 3f99835b138b0c89807de36f3e8e9a706415e7fb Author: Walter Duque de Estrada <[email protected]> AuthorDate: Tue Mar 17 16:10:00 2026 -0500 hibernate 7: refactor ComponentBinder --- .../cfg/domainbinding/binder/ComponentBinder.java | 4 +- .../domainbinding/binder/GrailsPropertyBinder.java | 2 +- .../cfg/domainbinding/ComponentBinderSpec.groovy | 59 ++++++++++------------ 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ComponentBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ComponentBinder.java index a00e4f4633..2f30d6b40d 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ComponentBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ComponentBinder.java @@ -52,8 +52,8 @@ public class ComponentBinder { this.grailsPropertyBinder = grailsPropertyBinder; } - public Component bindComponent( - PersistentClass owner, @Nonnull HibernateEmbeddedProperty embeddedProperty, String path) { + public Component bindComponent(@Nonnull HibernateEmbeddedProperty embeddedProperty, String path) { + var owner = embeddedProperty.getPersistentClass(); Component component = new Component(metadataBuildingContext, owner); Class<?> type = embeddedProperty.getType(); String role = GrailsHibernateUtil.qualify(type.getName(), embeddedProperty.getName()); diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/GrailsPropertyBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/GrailsPropertyBinder.java index a409764173..faa0c85feb 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/GrailsPropertyBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/GrailsPropertyBinder.java @@ -87,7 +87,7 @@ public class GrailsPropertyBinder { && !currentGrailsProp.isSerializableType()) { value = collectionBinder.bindCollection(toMany, path); } else if (currentGrailsProp instanceof HibernateEmbeddedProperty embedded) { - value = componentBinder.bindComponent(persistentClass, embedded, path); + value = componentBinder.bindComponent(embedded, path); } else { // HibernateSimpleProperty value = simpleValueBinder.bindSimpleValue(currentGrailsProp, parentProperty, table, path); diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ComponentBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ComponentBinderSpec.groovy index 7d0127f761..a51f738920 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ComponentBinderSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ComponentBinderSpec.groovy @@ -1,33 +1,17 @@ -/* - * 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 import grails.gorm.specs.HibernateGormDatastoreSpec +import org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsPropertyBinder import org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity import org.grails.orm.hibernate.cfg.MappingCacheHolder import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateEmbeddedProperty import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateSimpleProperty +import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateIdentityProperty import org.grails.orm.hibernate.cfg.domainbinding.binder.ComponentBinder import org.grails.orm.hibernate.cfg.domainbinding.binder.ComponentUpdater import org.hibernate.mapping.BasicValue import org.hibernate.mapping.Component +import org.hibernate.mapping.PersistentClass import org.hibernate.mapping.RootClass import org.hibernate.mapping.Table import org.hibernate.mapping.Value @@ -38,7 +22,7 @@ class ComponentBinderSpec extends HibernateGormDatastoreSpec { // Mock Collaborators MappingCacheHolder mappingCacheHolder = Mock(MappingCacheHolder) ComponentUpdater componentUpdater = Mock(ComponentUpdater) - org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsPropertyBinder grailsPropertyBinder = Mock(org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsPropertyBinder) + GrailsPropertyBinder grailsPropertyBinder = Mock(GrailsPropertyBinder) @Subject ComponentBinder binder @@ -57,9 +41,9 @@ class ComponentBinderSpec extends HibernateGormDatastoreSpec { root.setTable(new Table("my_entity")) def associatedEntity = GroovyMock(GrailsHibernatePersistentEntity) - def embeddedProp = mockEmbeddedProperty(associatedEntity, "address", Address) + def embeddedProp = mockEmbeddedProperty(associatedEntity, "address", Address, root) - // The Fix: Mock must return the root class so .getTable() doesn't NPE + // Ensure the associated entity also knows its class for initialization logic associatedEntity.getPersistentClass() >> root def prop1 = Mock(HibernateSimpleProperty) @@ -70,7 +54,7 @@ class ComponentBinderSpec extends HibernateGormDatastoreSpec { associatedEntity.getHibernatePersistentProperties(MyEntity) >> [prop1] when: - def component = binder.bindComponent(root, embeddedProp, "") + def component = binder.bindComponent(embeddedProp, "") then: component.getComponentClassName() == Address.name @@ -80,18 +64,19 @@ class ComponentBinderSpec extends HibernateGormDatastoreSpec { 1 * componentUpdater.updateComponent(_ as Component, embeddedProp, prop1, _ as Value) } - def "should skip identity and version properties"() { + def "should skip identity properties during binding"() { given: def metadataBuildingContext = getGrailsDomainBinder().getMetadataBuildingContext() def root = new RootClass(metadataBuildingContext) root.setTable(new Table("my_entity")) def associatedEntity = GroovyMock(GrailsHibernatePersistentEntity) - def embeddedProp = mockEmbeddedProperty(associatedEntity, "address", Address) + def embeddedProp = mockEmbeddedProperty(associatedEntity, "address", Address, root) associatedEntity.getPersistentClass() >> root - def idProp = Mock(org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateIdentityProperty) + // HibernatePersistentProperty includes ID properties; usually filtered by the loop logic + def idProp = Mock(HibernateIdentityProperty) idProp.getName() >> "id" def normalProp = Mock(HibernateSimpleProperty) @@ -102,11 +87,11 @@ class ComponentBinderSpec extends HibernateGormDatastoreSpec { associatedEntity.getHibernatePersistentProperties(MyEntity) >> [normalProp] when: - binder.bindComponent(root, embeddedProp, "") + binder.bindComponent(embeddedProp, "") then: + // Logic check: if idProp is not in the list returned by getHibernatePersistentProperties, it's skipped 0 * componentUpdater.updateComponent(_, _, idProp, _) - 1 * grailsPropertyBinder.bindProperty(normalProp, embeddedProp, "address") >> new BasicValue(metadataBuildingContext, root.getTable()) 1 * componentUpdater.updateComponent(_, _, normalProp, _) } @@ -117,7 +102,7 @@ class ComponentBinderSpec extends HibernateGormDatastoreSpec { root.setTable(new Table("my_entity")) def associatedEntity = GroovyMock(GrailsHibernatePersistentEntity) - def embeddedProp = mockEmbeddedProperty(associatedEntity, "address", Address) + def embeddedProp = mockEmbeddedProperty(associatedEntity, "address", Address, root) associatedEntity.getPersistentClass() >> root @@ -128,18 +113,28 @@ class ComponentBinderSpec extends HibernateGormDatastoreSpec { associatedEntity.getHibernatePersistentProperties(MyEntity) >> [] when: - def component = binder.bindComponent(root, embeddedProp, "") + def component = binder.bindComponent(embeddedProp, "") then: component.getParentProperty() == "myEntity" } - // Helper to reduce boilerplate - private HibernateEmbeddedProperty mockEmbeddedProperty(GrailsHibernatePersistentEntity associatedEntity, String name, Class type) { + /** + * Helper to reduce boilerplate. + * The 'root' (PersistentClass) is required by the Component constructor to avoid NPE. + */ + private HibernateEmbeddedProperty mockEmbeddedProperty( + GrailsHibernatePersistentEntity associatedEntity, + String name, + Class type, + PersistentClass root) { + def embeddedProp = Mock(HibernateEmbeddedProperty) embeddedProp.getName() >> name embeddedProp.getType() >> type embeddedProp.getAssociatedEntity() >> associatedEntity + embeddedProp.getPersistentClass() >> root // CRITICAL FIX + embeddedProp.getOwner() >> Mock(GrailsHibernatePersistentEntity) { getJavaClass() >> MyEntity }
