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 f254fa6fee11f28944f7e113e79ddc78226d5c42 Author: Walter Duque de Estrada <[email protected]> AuthorDate: Mon Feb 16 16:51:08 2026 -0600 Unify property binding and sort Embedded properties first - Update GrailsPropertyBinder.bindProperty to take path and parentProperty. - Refactor ComponentBinder to delegate to GrailsPropertyBinder, ensuring unified logic. - Sort properties in ClassPropertiesBinder and ComponentBinder to process HibernateEmbeddedProperty first. - Update CompositeIdBinder to use the unified bindProperty method. - Update unit tests to reflect dependency and signature changes. --- .../orm/hibernate/cfg/GrailsDomainBinder.java | 3 +- .../binder/ClassPropertiesBinder.java | 17 +- .../cfg/domainbinding/binder/ComponentBinder.java | 80 +++--- .../domainbinding/binder/CompositeIdBinder.java | 6 +- .../domainbinding/binder/GrailsPropertyBinder.java | 16 +- .../cfg/domainbinding/ComponentBinderSpec.groovy | 270 +-------------------- .../cfg/domainbinding/CompositeIdBinderSpec.groovy | 10 +- .../domainbinding/GrailsPropertyBinderSpec.groovy | 26 +- .../binder/ClassPropertiesBinderSpec.groovy | 6 +- 9 files changed, 92 insertions(+), 342 deletions(-) diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java index 43c7208175..1b44fb0c22 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java @@ -211,7 +211,8 @@ public class GrailsDomainBinder manyToOneBinder, propertyFromValueCreator ); - CompositeIdBinder compositeIdBinder = new CompositeIdBinder(metadataBuildingContext, componentBinder, componentUpdater); + componentBinder.setGrailsPropertyBinder(grailsPropertyBinder); + CompositeIdBinder compositeIdBinder = new CompositeIdBinder(metadataBuildingContext, componentBinder, componentUpdater, grailsPropertyBinder); PropertyBinder propertyBinder = new PropertyBinder(); SimpleIdBinder simpleIdBinder = new SimpleIdBinder(metadataBuildingContext, namingStrategy, jdbcEnvironment, new BasicValueIdCreator(jdbcEnvironment), simpleValueBinder, propertyBinder); IdentityBinder identityBinder = new IdentityBinder(simpleIdBinder, compositeIdBinder); diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassPropertiesBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassPropertiesBinder.java index cdfd1de212..e5d7f99d06 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassPropertiesBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassPropertiesBinder.java @@ -9,6 +9,9 @@ import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Table; import org.hibernate.mapping.Value; +import java.util.ArrayList; +import java.util.List; + /** * Binds the properties of a Grails domain class to the Hibernate meta-model. * @@ -48,8 +51,18 @@ public class ClassPropertiesBinder { persistentClass.getTable().setComment(domainClass.getMappedForm().getComment()); Table table = persistentClass.getTable(); - for (GrailsHibernatePersistentProperty currentGrailsProp : domainClass.getPersistentPropertiesToBind()) { - Value value = grailsPropertyBinder.bindProperty(persistentClass, table, currentGrailsProp, mappings); + List<GrailsHibernatePersistentProperty> properties = new ArrayList<>(domainClass.getPersistentPropertiesToBind()); + properties.sort((p1, p2) -> { + if (p1 instanceof org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateEmbeddedProperty && !(p2 instanceof org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateEmbeddedProperty)) { + return -1; + } else if (!(p1 instanceof org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateEmbeddedProperty) && p2 instanceof org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateEmbeddedProperty) { + return 1; + } + return p1.getName().compareTo(p2.getName()); + }); + + for (GrailsHibernatePersistentProperty currentGrailsProp : properties) { + Value value = grailsPropertyBinder.bindProperty(persistentClass, table, org.grails.orm.hibernate.cfg.GrailsDomainBinder.EMPTY_PATH, null, currentGrailsProp, mappings); persistentClass.addProperty(propertyFromValueCreator.createProperty(value, currentGrailsProp)); } 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 6fcf71d5ba..06f94232ef 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 @@ -2,11 +2,8 @@ package org.grails.orm.hibernate.cfg.domainbinding.binder; import org.hibernate.boot.spi.InFlightMetadataCollector; import org.hibernate.boot.spi.MetadataBuildingContext; -import org.hibernate.mapping.BasicValue; -import org.hibernate.mapping.Collection; import org.hibernate.mapping.Component; import org.hibernate.mapping.PersistentClass; -import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.Table; import org.hibernate.mapping.Value; import org.slf4j.Logger; @@ -18,7 +15,6 @@ import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentEntity; import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentProperty; import org.grails.orm.hibernate.cfg.GrailsHibernateUtil; import org.grails.orm.hibernate.cfg.MappingCacheHolder; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateBasicProperty; import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateEmbeddedProperty; import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToOneProperty; import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateOneToOneProperty; @@ -40,6 +36,7 @@ public class ComponentBinder { private final ColumnNameForPropertyAndPathFetcher columnNameForPropertyAndPathFetcher; private final SimpleValueBinder simpleValueBinder; private final ComponentUpdater componentUpdater; + private GrailsPropertyBinder grailsPropertyBinder; public ComponentBinder(MetadataBuildingContext metadataBuildingContext, MappingCacheHolder mappingCacheHolder, @@ -61,67 +58,48 @@ public class ComponentBinder { this.componentUpdater = componentUpdater; } + public void setGrailsPropertyBinder(GrailsPropertyBinder grailsPropertyBinder) { + this.grailsPropertyBinder = grailsPropertyBinder; + } + - public Component bindComponent(PersistentClass owner, HibernateEmbeddedProperty property, - @Nonnull InFlightMetadataCollector mappings) { + public Component bindComponent(PersistentClass owner, HibernateEmbeddedProperty embeddedProperty, + @Nonnull InFlightMetadataCollector mappings, String path) { Component component = new Component(metadataBuildingContext, owner); - Class<?> type = property.getType(); - String role = GrailsHibernateUtil.qualify(type.getName(), property.getName()); + Class<?> type = embeddedProperty.getType(); + String role = GrailsHibernateUtil.qualify(type.getName(), embeddedProperty.getName()); component.setRoleName(role); component.setComponentClassName(type.getName()); - GrailsHibernatePersistentEntity domainClass = (GrailsHibernatePersistentEntity) property.getAssociatedEntity(); + GrailsHibernatePersistentEntity domainClass = (GrailsHibernatePersistentEntity) embeddedProperty.getAssociatedEntity(); mappingCacheHolder.cacheMapping(domainClass); - var properties = domainClass.getHibernatePersistentProperties(); + var peerProperties = new java.util.ArrayList<>(domainClass.getHibernatePersistentProperties()); + peerProperties.sort((p1, p2) -> { + if (p1 instanceof HibernateEmbeddedProperty && !(p2 instanceof HibernateEmbeddedProperty)) { + return -1; + } else if (!(p1 instanceof HibernateEmbeddedProperty) && p2 instanceof HibernateEmbeddedProperty) { + return 1; + } + return p1.getName().compareTo(p2.getName()); + }); + Table table = component.getOwner().getTable(); PersistentClass persistentClass = component.getOwner(); - String path = property.getName(); - Class<?> propertyType = property.getOwner().getJavaClass(); + String currentPath = path.isEmpty() ? embeddedProperty.getName() : path + "." + embeddedProperty.getName(); + Class<?> propertyType = embeddedProperty.getOwner().getJavaClass(); - for (GrailsHibernatePersistentProperty currentGrailsProp : properties) { - if (currentGrailsProp.equals(domainClass.getIdentity())) continue; - if (currentGrailsProp.getName().equals(GormProperties.VERSION)) continue; + for (GrailsHibernatePersistentProperty peerProperty : peerProperties) { + if (peerProperty.equals(domainClass.getIdentity())) continue; + if (peerProperty.getName().equals(GormProperties.VERSION)) continue; - if (currentGrailsProp.getType().equals(propertyType)) { - component.setParentProperty(currentGrailsProp.getName()); + if (peerProperty.getType().equals(propertyType)) { + component.setParentProperty(peerProperty.getName()); continue; } - var value = bindComponentProperty(property, currentGrailsProp, persistentClass, path, table, mappings); - componentUpdater.updateComponent(component, property, currentGrailsProp, value); + var value = grailsPropertyBinder.bindProperty(persistentClass, table, currentPath, embeddedProperty, peerProperty, mappings); + componentUpdater.updateComponent(component, embeddedProperty, peerProperty, value); } return component; } - - public Value bindComponentProperty(GrailsHibernatePersistentProperty componentProperty, - GrailsHibernatePersistentProperty currentGrailsProp, - PersistentClass persistentClass, - String path, - Table table, - @Nonnull InFlightMetadataCollector mappings) { - Value value; - // work out what type of relationship it is and bind value - if (currentGrailsProp.isEnumType()) { - //HibernateEnumTypeProperty - value = enumTypeBinder.bindEnumType(currentGrailsProp, currentGrailsProp.getType(), table, path); - } else if (currentGrailsProp instanceof HibernateOneToOneProperty oneToOne) { - //HibernateOneToOneProperty - if (oneToOne.isHibernateOneToOne()) { - value = oneToOneBinder.bindOneToOne((org.grails.datastore.mapping.model.types.OneToOne) currentGrailsProp, persistentClass, table, path); - } else { - value = manyToOneBinder.bindManyToOne((Association) currentGrailsProp, table, path); - } - } else if (currentGrailsProp instanceof HibernateManyToOneProperty manyToOne) { - value = manyToOneBinder.bindManyToOne((Association) currentGrailsProp, table, path); - } else if (currentGrailsProp instanceof HibernateToManyProperty toMany && !currentGrailsProp.isSerializableType()) { - //HibernateToManyProperty - value = collectionBinder.bindCollection(toMany, persistentClass, mappings, path); - } else if (currentGrailsProp instanceof HibernateEmbeddedProperty embedded) { - value = bindComponent(persistentClass, embedded, mappings); - } else { - //HibernateSimpleProperty - value = this.simpleValueBinder.bindSimpleValue(currentGrailsProp, componentProperty, table, path); - } - return value; - } } diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CompositeIdBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CompositeIdBinder.java index b81297a81c..d1fd576507 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CompositeIdBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CompositeIdBinder.java @@ -18,11 +18,13 @@ public class CompositeIdBinder { private final MetadataBuildingContext metadataBuildingContext; private final ComponentBinder componentBinder; private final ComponentUpdater componentUpdater; + private final GrailsPropertyBinder grailsPropertyBinder; - public CompositeIdBinder(MetadataBuildingContext metadataBuildingContext, ComponentBinder componentBinder, ComponentUpdater componentUpdater) { + public CompositeIdBinder(MetadataBuildingContext metadataBuildingContext, ComponentBinder componentBinder, ComponentUpdater componentUpdater, GrailsPropertyBinder grailsPropertyBinder) { this.metadataBuildingContext = metadataBuildingContext; this.componentBinder = componentBinder; this.componentUpdater = componentUpdater; + this.grailsPropertyBinder = grailsPropertyBinder; } @@ -63,7 +65,7 @@ public class CompositeIdBinder { "] is not a valid property!"); } - var value = componentBinder.bindComponentProperty(identifierProp, property, root, "", root.getTable(), mappings); + var value = grailsPropertyBinder.bindProperty(root, root.getTable(), "", identifierProp, property, mappings); componentUpdater.updateComponent(id, identifierProp, property, value); } } 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 fccd94fd38..0c9aff1442 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 @@ -71,6 +71,8 @@ public class GrailsPropertyBinder { public Value bindProperty(PersistentClass persistentClass , Table table + , String path + , GrailsHibernatePersistentProperty parentProperty , @Nonnull GrailsHibernatePersistentProperty currentGrailsProp , @Nonnull InFlightMetadataCollector mappings) { if (LOG.isDebugEnabled()) { @@ -82,24 +84,24 @@ public class GrailsPropertyBinder { // 1. Create Value and apply binders (consolidated block) if (currentGrailsProp.isEnumType()) { //HibernateEnumTypeProperty - value = enumTypeBinder.bindEnumType(currentGrailsProp, currentGrailsProp.getType(), table, EMPTY_PATH); + value = enumTypeBinder.bindEnumType(currentGrailsProp, currentGrailsProp.getType(), table, path); } else if (currentGrailsProp instanceof HibernateOneToOneProperty oneToOne) { //HibernateOneToOneProperty if (oneToOne.isHibernateOneToOne()) { - value = oneToOneBinder.bindOneToOne((org.grails.datastore.mapping.model.types.OneToOne) currentGrailsProp, persistentClass, table, EMPTY_PATH); + value = oneToOneBinder.bindOneToOne((org.grails.datastore.mapping.model.types.OneToOne) currentGrailsProp, persistentClass, table, path); } else { - value = manyToOneBinder.bindManyToOne((Association) currentGrailsProp, table, EMPTY_PATH); + value = manyToOneBinder.bindManyToOne((Association) currentGrailsProp, table, path); } } else if (currentGrailsProp instanceof HibernateManyToOneProperty manyToOne) { - value = manyToOneBinder.bindManyToOne((Association) currentGrailsProp, table, EMPTY_PATH); + value = manyToOneBinder.bindManyToOne((Association) currentGrailsProp, table, path); } else if (currentGrailsProp instanceof HibernateToManyProperty toMany && !currentGrailsProp.isSerializableType()) { //HibernateToManyProperty - value = collectionBinder.bindCollection(toMany, persistentClass, mappings, EMPTY_PATH); + value = collectionBinder.bindCollection(toMany, persistentClass, mappings, path); } else if (currentGrailsProp instanceof HibernateEmbeddedProperty embedded) { - value = componentBinder.bindComponent(persistentClass, embedded, mappings); + value = componentBinder.bindComponent(persistentClass, embedded, mappings, path); } else { //HibernateSimpleProperty - value = simpleValueBinder.bindSimpleValue(currentGrailsProp, null, table, EMPTY_PATH); + value = simpleValueBinder.bindSimpleValue(currentGrailsProp, parentProperty, table, path); } return value; 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 48f9c6a7e2..30e5beacb2 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 @@ -87,6 +87,7 @@ class ComponentBinderSpec extends HibernateGormDatastoreSpec { ColumnNameForPropertyAndPathFetcher columnNameFetcher = Mock(ColumnNameForPropertyAndPathFetcher) ComponentUpdater componentUpdater = Mock(ComponentUpdater) SimpleValueBinder mockSimpleValueBinder = Mock(SimpleValueBinder) + org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsPropertyBinder grailsPropertyBinder = Mock(org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsPropertyBinder) @Subject ComponentBinder binder @@ -105,6 +106,7 @@ class ComponentBinderSpec extends HibernateGormDatastoreSpec { columnNameFetcher, componentUpdater ) + binder.setGrailsPropertyBinder(grailsPropertyBinder) } private void setupProperty(PersistentProperty prop, String name, Mapping mapping, PersistentEntity owner) { @@ -149,13 +151,13 @@ class ComponentBinderSpec extends HibernateGormDatastoreSpec { def mappings = metadataBuildingContext.getMetadataCollector() when: - def component = binder.bindComponent(root, embeddedProp, mappings) + def component = binder.bindComponent(root, embeddedProp, mappings, "") then: component.getComponentClassName() == Address.name component.getRoleName() == Address.name + ".address" 1 * mappingCacheHolder.cacheMapping(associatedEntity) - 1 * mockSimpleValueBinder.bindSimpleValue(prop1, embeddedProp, _ as Table, "address") >> new BasicValue(metadataBuildingContext, root.getTable()) + 1 * grailsPropertyBinder.bindProperty(root, root.getTable(), "address", embeddedProp, prop1, mappings) >> new BasicValue(metadataBuildingContext, root.getTable()) 1 * componentUpdater.updateComponent(_ as Component, embeddedProp, prop1, _ as Value) } @@ -195,12 +197,12 @@ class ComponentBinderSpec extends HibernateGormDatastoreSpec { def mappings = metadataBuildingContext.getMetadataCollector() when: - binder.bindComponent(root, embeddedProp, mappings) + binder.bindComponent(root, embeddedProp, mappings, "") then: 0 * componentUpdater.updateComponent(_, _, idProp, _) 0 * componentUpdater.updateComponent(_, _, versionProp, _) - 1 * mockSimpleValueBinder.bindSimpleValue(normalProp, embeddedProp, _ as Table, "address") >> new BasicValue(metadataBuildingContext, root.getTable()) + 1 * grailsPropertyBinder.bindProperty(root, root.getTable(), "address", embeddedProp, normalProp, mappings) >> new BasicValue(metadataBuildingContext, root.getTable()) 1 * componentUpdater.updateComponent(_, _, normalProp, _) } @@ -234,270 +236,14 @@ class ComponentBinderSpec extends HibernateGormDatastoreSpec { def mappings = metadataBuildingContext.getMetadataCollector() when: - def component = binder.bindComponent(root, embeddedProp, mappings) + def component = binder.bindComponent(root, embeddedProp, mappings, "") then: component.getParentProperty() == "myEntity" - 0 * mockSimpleValueBinder.bindSimpleValue(parentProp, _, _, _) + 0 * grailsPropertyBinder.bindProperty(_, _, _, _, parentProp, _) 0 * componentUpdater.updateComponent(_, _, parentProp, _) } - def "should bind simple property"() { - given: - def metadataBuildingContext = getGrailsDomainBinder().getMetadataBuildingContext() - def root = new RootClass(metadataBuildingContext) - def table = new Table("my_table") - def ownerEntity = Mock(GrailsHibernatePersistentEntity) - ownerEntity.isRoot() >> true - - def currentGrailsProp = Mock(TestSimple) - - def componentProperty = Mock(GrailsHibernatePersistentProperty) - def mappings = Mock(InFlightMetadataCollector) - def hibernateProperty = new Property() - hibernateProperty.setName("street") - - def mapping = new Mapping() - ownerEntity.getMappedForm() >> mapping - currentGrailsProp.getType() >> String - currentGrailsProp.isHibernateOneToOne() >> false - currentGrailsProp.isHibernateManyToOne() >> false - setupProperty(currentGrailsProp, "street", mapping, ownerEntity) - setupProperty(componentProperty, "address", mapping, ownerEntity) - - when: - binder.bindComponentProperty(componentProperty, currentGrailsProp, root, "address", table, mappings) - - then: - 1 * mockSimpleValueBinder.bindSimpleValue(currentGrailsProp, componentProperty, table, "address") >> new BasicValue(metadataBuildingContext, table) - 0 * componentUpdater.updateComponent(_, _, _, _) - } - - def "should bind many-to-one property"() { - given: - def metadataBuildingContext = getGrailsDomainBinder().getMetadataBuildingContext() - def root = new RootClass(metadataBuildingContext) - def table = new Table("my_table") - def ownerEntity = Mock(GrailsHibernatePersistentEntity) - ownerEntity.isRoot() >> true - def currentGrailsProp = Mock(TestManyToOne) - def componentProperty = Mock(GrailsHibernatePersistentProperty) - def mappings = Mock(InFlightMetadataCollector) - def hibernateProperty = new Property() - hibernateProperty.setName("owner") - def hibernateManyToOne = new HibernateManyToOne(metadataBuildingContext, table) - - def mapping = new Mapping() - ownerEntity.getMappedForm() >> mapping - currentGrailsProp.getAssociatedEntity() >> Mock(GrailsHibernatePersistentEntity) { - getName() >> "Owner" - getMappedForm() >> new Mapping() - isRoot() >> true - } - currentGrailsProp.getType() >> Object - currentGrailsProp.isHibernateOneToOne() >> false - currentGrailsProp.isHibernateManyToOne() >> true - setupProperty(currentGrailsProp, "owner", mapping, ownerEntity) - setupProperty(componentProperty, "address", mapping, ownerEntity) - - when: - binder.bindComponentProperty(componentProperty, currentGrailsProp, root, "address", table, mappings) - - then: - 1 * manyToOneBinder.bindManyToOne(currentGrailsProp, table, "address") >> hibernateManyToOne - 0 * componentUpdater.updateComponent(_, _, _, _) - } - - def "should bind one-to-one property"() { - given: - def metadataBuildingContext = getGrailsDomainBinder().getMetadataBuildingContext() - def root = new RootClass(metadataBuildingContext) - def table = new Table("my_table") - def ownerEntity = Mock(GrailsHibernatePersistentEntity) - ownerEntity.isRoot() >> true - def currentGrailsProp = Mock(TestOneToOne) - def componentProperty = Mock(GrailsHibernatePersistentProperty) - def mappings = Mock(InFlightMetadataCollector) - def hibernateProperty = new Property() - hibernateProperty.setName("detail") - - def mapping = new Mapping() - ownerEntity.getMappedForm() >> mapping - ((Association)currentGrailsProp).getInverseSide() >> Mock(Association) { - isHasOne() >> false - getOwner() >> Mock(GrailsHibernatePersistentEntity) { - getName() >> "Other" - isRoot() >> true - } - getName() >> "other" - } - currentGrailsProp.getType() >> Object - ((Association)currentGrailsProp).canBindOneToOneWithSingleColumnAndForeignKey() >> true - currentGrailsProp.isHibernateOneToOne() >> true - setupProperty(currentGrailsProp, "detail", mapping, ownerEntity) - setupProperty(componentProperty, "address", mapping, ownerEntity) - def hibernateOneToOne = new HibernateOneToOne(metadataBuildingContext, table, root) - - when: - binder.bindComponentProperty(componentProperty, currentGrailsProp, root, "address", table, mappings) - - then: - 1 * oneToOneBinder.bindOneToOne(currentGrailsProp, root, table, "address") >> hibernateOneToOne - 0 * componentUpdater.updateComponent(_, _, _, _) - } - - def "should bind enum property"() { - given: - def metadataBuildingContext = getGrailsDomainBinder().getMetadataBuildingContext() - def root = new RootClass(metadataBuildingContext) - def table = new Table("my_table") - def ownerEntity = Mock(GrailsHibernatePersistentEntity) - ownerEntity.isRoot() >> true - def currentGrailsProp = Mock(TestBasic) - def componentProperty = Mock(GrailsHibernatePersistentProperty) - def mappings = Mock(InFlightMetadataCollector) - def hibernateProperty = new Property() - hibernateProperty.setName("type") - - def mapping = new Mapping() - ownerEntity.getMappedForm() >> mapping - currentGrailsProp.getType() >> MyEnum - currentGrailsProp.isEnumType() >> true - currentGrailsProp.isHibernateOneToOne() >> false - currentGrailsProp.isHibernateManyToOne() >> false - setupProperty(currentGrailsProp, "type", mapping, ownerEntity) - setupProperty(componentProperty, "address", mapping, ownerEntity) - - columnNameFetcher.getColumnNameForPropertyAndPath(currentGrailsProp, "address", null) >> "address_type_col" - - when: - binder.bindComponentProperty(componentProperty, currentGrailsProp, root, "address", table, mappings) - - then: - 1 * enumTypeBinder.bindEnumType(currentGrailsProp, MyEnum, table, "address") >> new BasicValue(metadataBuildingContext, table) - 0 * componentUpdater.updateComponent(_, _, _, _) - } - - def "should set columns to nullable when component property is nullable"() { - given: - def metadataBuildingContext = getGrailsDomainBinder().getMetadataBuildingContext() - def root = new RootClass(metadataBuildingContext) - def table = new Table("my_table") - def ownerEntity = Mock(GrailsHibernatePersistentEntity) - ownerEntity.isRoot() >> true - def currentGrailsProp = Mock(TestSimple) - def componentProperty = Mock(GrailsHibernatePersistentProperty) - def mappings = Mock(InFlightMetadataCollector) - def hibernateProperty = new Property() - hibernateProperty.setName("street") - - def mapping = new Mapping() - ownerEntity.getMappedForm() >> mapping - currentGrailsProp.getType() >> String - currentGrailsProp.isHibernateOneToOne() >> false - currentGrailsProp.isHibernateManyToOne() >> false - setupProperty(currentGrailsProp, "street", mapping, ownerEntity) - setupProperty(componentProperty, "address", mapping, ownerEntity) - - ownerEntity.isComponentPropertyNullable(componentProperty) >> true - - when: - binder.bindComponentProperty(componentProperty, currentGrailsProp, root, "address", table, mappings) - - then: - 1 * mockSimpleValueBinder.bindSimpleValue( - currentGrailsProp, - componentProperty, - table, - "address" - ) >> new BasicValue(metadataBuildingContext, table) - 0 * componentUpdater.updateComponent(_, _, _, _) - } - - def "should bind collection property within component"() { - given: - def metadataBuildingContext = getGrailsDomainBinder().getMetadataBuildingContext() - def root = new RootClass(metadataBuildingContext) - def table = new Table("my_table") - def currentGrailsProp = Mock(org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyProperty) - def componentProperty = Mock(GrailsHibernatePersistentProperty) - def mappings = Mock(InFlightMetadataCollector) - - currentGrailsProp.getType() >> Set - currentGrailsProp.isHibernateOneToOne() >> false - currentGrailsProp.isHibernateManyToOne() >> false - - when: - def result = binder.bindComponentProperty(componentProperty, currentGrailsProp, root, "address", table, mappings) - - then: - 1 * collectionBinder.bindCollection(currentGrailsProp, root, mappings, "address") >> Mock(org.hibernate.mapping.Set) - } - - def "should bind nested component recursively"() { - given: - def metadataBuildingContext = getGrailsDomainBinder().getMetadataBuildingContext() - def root = new RootClass(metadataBuildingContext) - root.setTable(new Table("my_table")) - def table = root.getTable() - def mappings = Mock(InFlightMetadataCollector) - - def nestedEmbeddedProp = Mock(TestEmbedded) - def nestedAssociatedEntity = Mock(GrailsHibernatePersistentEntity) - def parentProp = Mock(GrailsHibernatePersistentProperty) - - nestedEmbeddedProp.getType() >> Address - nestedEmbeddedProp.getName() >> "nestedAddress" - nestedEmbeddedProp.getAssociatedEntity() >> nestedAssociatedEntity - nestedEmbeddedProp.getOwner() >> Mock(GrailsHibernatePersistentEntity) { - getJavaClass() >> Address - } - nestedEmbeddedProp.isHibernateOneToOne() >> false - nestedEmbeddedProp.isHibernateManyToOne() >> false - - nestedAssociatedEntity.getName() >> "NestedAddress" - def nestedProp1 = Mock(TestSimple) - nestedProp1.getName() >> "street" - nestedProp1.getType() >> String - nestedProp1.isHibernateOneToOne() >> false - nestedProp1.isHibernateManyToOne() >> false - nestedAssociatedEntity.getHibernatePersistentProperties() >> [nestedProp1] - nestedAssociatedEntity.getIdentity() >> null - - when: - def result = binder.bindComponentProperty(parentProp, nestedEmbeddedProp, root, "address.nested", table, mappings) - - then: - result instanceof Component - result.getComponentClassName() == Address.name - 1 * mappingCacheHolder.cacheMapping(nestedAssociatedEntity) - 1 * mockSimpleValueBinder.bindSimpleValue(nestedProp1, nestedEmbeddedProp, _ as Table, "nestedAddress") >> new BasicValue(metadataBuildingContext, table) - 1 * componentUpdater.updateComponent(_ as Component, nestedEmbeddedProp, nestedProp1, _ as Value) - } - - def "should bind one-to-one as many-to-one when it cannot be bound with single column"() { - given: - def metadataBuildingContext = getGrailsDomainBinder().getMetadataBuildingContext() - def root = new RootClass(metadataBuildingContext) - def table = new Table("my_table") - def currentGrailsProp = Mock(TestOneToOne) - def componentProperty = Mock(GrailsHibernatePersistentProperty) - def mappings = Mock(InFlightMetadataCollector) - - currentGrailsProp.canBindOneToOneWithSingleColumnAndForeignKey() >> false - currentGrailsProp.getType() >> Object - currentGrailsProp.isHibernateOneToOne() >> false - currentGrailsProp.isHibernateManyToOne() >> true - def hibernateManyToOne = new HibernateManyToOne(metadataBuildingContext, table) - - when: - def result = binder.bindComponentProperty(componentProperty, currentGrailsProp, root, "address", table, mappings) - - then: - 1 * manyToOneBinder.bindManyToOne(currentGrailsProp, table, "address") >> hibernateManyToOne - result == hibernateManyToOne - } - static class MyEntity {} static class Address {} enum MyEnum { VAL } diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CompositeIdBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CompositeIdBinderSpec.groovy index e243472a15..f485126876 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CompositeIdBinderSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CompositeIdBinderSpec.groovy @@ -14,18 +14,20 @@ import spock.lang.Subject import org.grails.orm.hibernate.cfg.domainbinding.binder.ComponentBinder import org.grails.orm.hibernate.cfg.domainbinding.binder.CompositeIdBinder import org.grails.orm.hibernate.cfg.domainbinding.binder.ComponentUpdater +import org.grails.orm.hibernate.cfg.domainbinding.binder.GrailsPropertyBinder import org.hibernate.mapping.Value class CompositeIdBinderSpec extends HibernateGormDatastoreSpec { def componentBinder = Mock(ComponentBinder) def componentUpdater = Mock(ComponentUpdater) + def grailsPropertyBinder = Mock(GrailsPropertyBinder) @Subject CompositeIdBinder binder def setup() { - binder = new CompositeIdBinder(getGrailsDomainBinder().getMetadataBuildingContext(), componentBinder, componentUpdater) + binder = new CompositeIdBinder(getGrailsDomainBinder().getMetadataBuildingContext(), componentBinder, componentUpdater, grailsPropertyBinder) } def "should bind composite id using property names from CompositeIdentity"() { @@ -56,8 +58,8 @@ class CompositeIdBinderSpec extends HibernateGormDatastoreSpec { root.getIdentifier() instanceof Component root.getIdentifierMapper() instanceof Component root.hasEmbeddedIdentifier() - 2 * componentBinder.bindComponentProperty(identifierProp, _ as PersistentProperty, root, "", table, mappings) >> Mock(Value) - 2 * componentUpdater.updateComponent(_ as Component, identifierProp, _ as PersistentProperty, _ as Value) + 2 * grailsPropertyBinder.bindProperty(root, table, "", identifierProp, _ as GrailsHibernatePersistentProperty, mappings) >> Mock(Value) + 2 * componentUpdater.updateComponent(_ as Component, identifierProp, _ as GrailsHibernatePersistentProperty, _ as Value) } def "should fallback to domainClass composite identity when CompositeIdentity is null"() { @@ -81,7 +83,7 @@ class CompositeIdBinderSpec extends HibernateGormDatastoreSpec { binder.bindCompositeId(domainClass, root, null, mappings) then: - 1 * componentBinder.bindComponentProperty(identifierProp, prop1, root, "", table, mappings) >> Mock(Value) + 1 * grailsPropertyBinder.bindProperty(root, table, "", identifierProp, prop1, mappings) >> Mock(Value) 1 * componentUpdater.updateComponent(_ as Component, identifierProp, prop1, _ as Value) } 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 355bf77bf9..1c1ec02083 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 @@ -46,6 +46,7 @@ import org.grails.orm.hibernate.cfg.domainbinding.binder.IdentityBinder 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.NaturalIdentifierBinder import org.grails.orm.hibernate.cfg.domainbinding.binder.PropertyBinder import org.grails.orm.hibernate.cfg.domainbinding.util.BasicValueIdCreator import org.hibernate.boot.spi.InFlightMetadataCollector @@ -164,14 +165,16 @@ class GrailsPropertyBinderSpec extends HibernateGormDatastoreSpec { manyToOneBinder, propertyFromValueCreator ) - CompositeIdBinder compositeIdBinder = new CompositeIdBinder(metadataBuildingContext, componentBinder, componentUpdater); + componentBinder.setGrailsPropertyBinder(propertyBinder) + CompositeIdBinder compositeIdBinder = new CompositeIdBinder(metadataBuildingContext, componentBinder, componentUpdater, propertyBinder); PropertyBinder propertyBinderHelper = new PropertyBinder() SimpleIdBinder simpleIdBinder = new SimpleIdBinder(metadataBuildingContext, namingStrategy, jdbcEnvironment, new BasicValueIdCreator(jdbcEnvironment), simpleValueBinder, propertyBinderHelper) IdentityBinder identityBinder = new IdentityBinder(simpleIdBinder, compositeIdBinder) VersionBinder versionBinder = new VersionBinder(metadataBuildingContext, simpleValueBinder, propertyBinderHelper, BasicValue::new) + NaturalIdentifierBinder naturalIdentifierBinder = new NaturalIdentifierBinder() ClassBinder classBinder = new ClassBinder() - ClassPropertiesBinder classPropertiesBinder = new ClassPropertiesBinder(propertyBinder, propertyFromValueCreator) + ClassPropertiesBinder classPropertiesBinder = new ClassPropertiesBinder(propertyBinder, propertyFromValueCreator, naturalIdentifierBinder) MultiTenantFilterBinder multiTenantFilterBinder = new MultiTenantFilterBinder() JoinedSubClassBinder joinedSubClassBinder = new JoinedSubClassBinder(metadataBuildingContext, namingStrategy, new org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueColumnBinder(), columnNameForPropertyAndPathFetcher, classBinder) UnionSubclassBinder unionSubclassBinder = new UnionSubclassBinder(metadataBuildingContext, namingStrategy, classBinder) @@ -187,6 +190,7 @@ class GrailsPropertyBinderSpec extends HibernateGormDatastoreSpec { classBinder: classBinder, classPropertiesBinder: classPropertiesBinder, multiTenantFilterBinder: multiTenantFilterBinder, + naturalIdentifierBinder: naturalIdentifierBinder, joinedSubClassBinder: joinedSubClassBinder, unionSubclassBinder: unionSubclassBinder, singleTableSubclassBinder: singleTableSubclassBinder @@ -228,7 +232,7 @@ class GrailsPropertyBinderSpec extends HibernateGormDatastoreSpec { when: def titleProp = persistentEntity.getPropertyByName("title") as GrailsHibernatePersistentProperty - Value value = propertyBinder.bindProperty(rootClass, rootClass.table, titleProp, collector) + Value value = propertyBinder.bindProperty(rootClass, rootClass.table, EMPTY_PATH, null, titleProp, collector) rootClass.addProperty(new PropertyFromValueCreator().createProperty(value, titleProp)) then: @@ -256,7 +260,7 @@ class GrailsPropertyBinderSpec extends HibernateGormDatastoreSpec { statusProp.isHibernateManyToOne() >> false when: - Value value = propertyBinder.bindProperty(rootClass, rootClass.table, statusProp, collector) + Value value = propertyBinder.bindProperty(rootClass, rootClass.table, EMPTY_PATH, null, statusProp, collector) rootClass.addProperty(new PropertyFromValueCreator().createProperty(value, statusProp)) then: @@ -282,7 +286,7 @@ class GrailsPropertyBinderSpec extends HibernateGormDatastoreSpec { when: def ownerProp = petEntity.getPropertyByName("owner") as GrailsHibernatePersistentProperty - Value value = propertyBinder.bindProperty(rootClass, rootClass.table, ownerProp, collector) + Value value = propertyBinder.bindProperty(rootClass, rootClass.table, EMPTY_PATH, null, ownerProp, collector) rootClass.addProperty(new PropertyFromValueCreator().createProperty(value, ownerProp)) then: @@ -305,7 +309,7 @@ class GrailsPropertyBinderSpec extends HibernateGormDatastoreSpec { when: def addressProp = persistentEntity.getPropertyByName("homeAddress") as GrailsHibernatePersistentProperty - Value value = propertyBinder.bindProperty(rootClass, rootClass.table, addressProp, collector) + Value value = propertyBinder.bindProperty(rootClass, rootClass.table, EMPTY_PATH, null, addressProp, collector) rootClass.addProperty(new PropertyFromValueCreator().createProperty(value, addressProp)) then: @@ -333,7 +337,7 @@ class GrailsPropertyBinderSpec extends HibernateGormDatastoreSpec { when: def petsProp = personEntity.getPropertyByName("pets") as GrailsHibernatePersistentProperty - Value value = propertyBinder.bindProperty(rootClass, rootClass.table, petsProp, collector) + Value value = propertyBinder.bindProperty(rootClass, rootClass.table, EMPTY_PATH, null, petsProp, collector) rootClass.addProperty(new PropertyFromValueCreator().createProperty(value, petsProp)) then: @@ -372,7 +376,7 @@ class GrailsPropertyBinderSpec extends HibernateGormDatastoreSpec { when: def booksProp = authorEntity.getPropertyByName("books") as GrailsHibernatePersistentProperty - Value value = propertyBinder.bindProperty(rootClass, rootClass.table, booksProp, collector) + Value value = propertyBinder.bindProperty(rootClass, rootClass.table, EMPTY_PATH, null, booksProp, collector) rootClass.addProperty(new PropertyFromValueCreator().createProperty(value, booksProp)) collector.processSecondPasses(binder.getMetadataBuildingContext()) @@ -411,7 +415,7 @@ class GrailsPropertyBinderSpec extends HibernateGormDatastoreSpec { when: def booksProp = authorEntity.getPropertyByName("books") as GrailsHibernatePersistentProperty - Value value = propertyBinder.bindProperty(rootClass, rootClass.table, booksProp, collector) + Value value = propertyBinder.bindProperty(rootClass, rootClass.table, EMPTY_PATH, null, booksProp, collector) rootClass.addProperty(new PropertyFromValueCreator().createProperty(value, booksProp)) collector.processSecondPasses(binder.getMetadataBuildingContext()) @@ -472,7 +476,7 @@ class GrailsPropertyBinderSpec extends HibernateGormDatastoreSpec { when: def childBookProp = authorEntity.getPropertyByName("childBook") as GrailsHibernatePersistentProperty - Value value = propertyBinder.bindProperty(rootClass, rootClass.table, childBookProp, collector) + Value value = propertyBinder.bindProperty(rootClass, rootClass.table, EMPTY_PATH, null, childBookProp, collector) rootClass.addProperty(new PropertyFromValueCreator().createProperty(value, childBookProp)) // Process second passes to ensure Hibernate's internal mappings are finalized collector.processSecondPasses(binder.getMetadataBuildingContext()) @@ -540,7 +544,7 @@ class GrailsPropertyBinderSpec extends HibernateGormDatastoreSpec { when: // Capture the return value of bindProperty - def resultValue = propertyBinder.bindProperty(rootClass, table, currentGrailsProp, mappings) + def resultValue = propertyBinder.bindProperty(rootClass, table, EMPTY_PATH, null, currentGrailsProp, mappings) then: // Assert that bindProperty returns a Value object diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassPropertiesBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassPropertiesBinderSpec.groovy index a28872e880..a9c222eb3a 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassPropertiesBinderSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassPropertiesBinderSpec.groovy @@ -26,7 +26,9 @@ class ClassPropertiesBinderSpec extends HibernateGormDatastoreSpec { def sessionFactoryBeanName = "sessionFactory" def prop1 = Mock(GrailsHibernatePersistentProperty) + prop1.getName() >> "prop1" def prop2 = Mock(GrailsHibernatePersistentProperty) + prop2.getName() >> "prop2" domainClass.getPersistentPropertiesToBind() >> [prop1, prop2] def value1 = Mock(Value) @@ -44,10 +46,10 @@ class ClassPropertiesBinderSpec extends HibernateGormDatastoreSpec { binder.bindClassProperties(domainClass, persistentClass, mappings) then: - 1 * grailsPropertyBinder.bindProperty(persistentClass, persistentClass.table, prop1, mappings) >> value1 + 1 * grailsPropertyBinder.bindProperty(persistentClass, persistentClass.table, org.grails.orm.hibernate.cfg.GrailsDomainBinder.EMPTY_PATH, null, prop1, mappings) >> value1 1 * propertyFromValueCreator.createProperty(value1, prop1) >> hibernateProp1 - 1 * grailsPropertyBinder.bindProperty(persistentClass, persistentClass.table, prop2, mappings) >> value2 + 1 * grailsPropertyBinder.bindProperty(persistentClass, persistentClass.table, org.grails.orm.hibernate.cfg.GrailsDomainBinder.EMPTY_PATH, null, prop2, mappings) >> value2 1 * propertyFromValueCreator.createProperty(value2, prop2) >> hibernateProp2 persistentClass.getProperty("hibernateProp1") == hibernateProp1
