You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by bo...@apache.org on 2026/02/16 00:14:23 UTC

(grails-core) 16/16: Refactor class properties binding logic from GrailsDomainBinder to ClassPropertiesBinder and clean up binder signatures.

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 cbbc30c78c9d774f7dae6ab3631bec5eafc0b769
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Sun Feb 15 18:13:51 2026 -0600

    Refactor class properties binding logic from GrailsDomainBinder to ClassPropertiesBinder and clean up binder signatures.
---
 .../orm/hibernate/cfg/GrailsDomainBinder.java      | 50 ++++++-------------
 .../binder/ClassPropertiesBinder.java              | 56 +++++++++++++++++++++
 .../binder/ClassPropertiesBinderSpec.groovy        | 57 ++++++++++++++++++++++
 3 files changed, 127 insertions(+), 36 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 11358ac2fc..d64e6b569a 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
@@ -18,6 +18,7 @@ import groovy.lang.Closure;
 
 import org.grails.datastore.mapping.model.config.GormProperties;
 import org.grails.datastore.mapping.model.types.TenantId;
+import org.grails.orm.hibernate.cfg.domainbinding.binder.ClassPropertiesBinder;
 import org.grails.orm.hibernate.cfg.domainbinding.binder.ClassBinder;
 import org.grails.orm.hibernate.cfg.domainbinding.binder.ColumnConfigToColumnBinder;
 import org.grails.orm.hibernate.cfg.domainbinding.binder.ComponentPropertyBinder;
@@ -230,6 +231,8 @@ public class GrailsDomainBinder
         SimpleIdBinder simpleIdBinder = new SimpleIdBinder(metadataBuildingContext, namingStrategy, jdbcEnvironment, new BasicValueIdCreator(jdbcEnvironment), simpleValueBinder, propertyBinder);
         IdentityBinder identityBinder = new IdentityBinder(simpleIdBinder, compositeIdBinder);
         VersionBinder versionBinder = new VersionBinder(metadataBuildingContext, simpleValueBinder, propertyBinder, BasicValue::new);
+        NaturalIdentifierBinder naturalIdentifierBinder = new NaturalIdentifierBinder();
+        ClassPropertiesBinder classPropertiesBinder = new ClassPropertiesBinder(grailsPropertyBinder, propertyFromValueCreator, naturalIdentifierBinder);
         MultiTenantFilterBinder multiTenantFilterBinder = new MultiTenantFilterBinder();
         JoinedSubClassBinder joinedSubClassBinder = new JoinedSubClassBinder(metadataBuildingContext, namingStrategy, new SimpleValueColumnBinder(), columnNameForPropertyAndPathFetcher, classBinder);
         UnionSubclassBinder unionSubclassBinder = new UnionSubclassBinder(metadataBuildingContext, namingStrategy, classBinder);
@@ -239,7 +242,7 @@ public class GrailsDomainBinder
                 .getHibernatePersistentEntities(dataSourceName)
                 .stream()
                 .filter(persistentEntity -> persistentEntity.forGrailsDomainMapping(dataSourceName))
-                .forEach(hibernatePersistentEntity -> bindRoot(hibernatePersistentEntity, metadataCollector, sessionFactoryName, defaultColumnNameFetcher, columnNameForPropertyAndPathFetcher, identityBinder, versionBinder, grailsPropertyBinder, classBinder, propertyFromValueCreator, multiTenantFilterBinder, joinedSubClassBinder, unionSubclassBinder, singleTableSubclassBinder));
+                .forEach(hibernatePersistentEntity -> bindRoot(hibernatePersistentEntity, metadataCollector, sessionFactoryName, defaultColumnNameFetcher, identityBinder, versionBinder, classBinder, classPropertiesBinder, multiTenantFilterBinder, joinedSubClassBinder, unionSubclassBinder, singleTableSubclassBinder));
     }
 
 
@@ -270,20 +273,20 @@ public class GrailsDomainBinder
      * @param mappings    The Hibernate Mappings object
      * @param sessionFactoryBeanName  the session factory bean name
      */
-    protected void bindRoot(@Nonnull GrailsHibernatePersistentEntity entity,@Nonnull InFlightMetadataCollector mappings, String sessionFactoryBeanName, DefaultColumnNameFetcher defaultColumnNameFetcher, ColumnNameForPropertyAndPathFetcher columnNameForPropertyAndPathFetcher, IdentityBinder identityBinder, VersionBinder versionBinder, GrailsPropertyBinder grailsPropertyBinder, ClassBinder classBinder, PropertyFromValueCreator propertyFromValueCreator, MultiTenantFilterBinder multiTenantFi [...]
+    protected void bindRoot(@Nonnull GrailsHibernatePersistentEntity entity,@Nonnull InFlightMetadataCollector mappings, String sessionFactoryBeanName, DefaultColumnNameFetcher defaultColumnNameFetcher, IdentityBinder identityBinder, VersionBinder versionBinder, ClassBinder classBinder, ClassPropertiesBinder classPropertiesBinder, MultiTenantFilterBinder multiTenantFilterBinder, JoinedSubClassBinder joinedSubClassBinder, UnionSubclassBinder unionSubclassBinder, SingleTableSubclassBinder  [...]
         if (mappings.getEntityBinding(entity.getName()) != null) {
             LOG.info("[GrailsDomainBinder] Class [" + entity.getName() + "] is already mapped, skipping.. ");
             return;
         }
         var children = entity.getChildEntities(dataSourceName);
-        RootClass root = bindRootPersistentClassCommonValues(entity, children, mappings, sessionFactoryBeanName, identityBinder, versionBinder, grailsPropertyBinder, classBinder, propertyFromValueCreator);
+        RootClass root = bindRootPersistentClassCommonValues(entity, children, mappings, sessionFactoryBeanName, identityBinder, versionBinder, classBinder, classPropertiesBinder);
         Mapping m = entity.getMappedForm();
         final Mapping finalMapping = m;
         if (!children.isEmpty() && entity.isTablePerHierarchy()) {
             bindDiscriminatorProperty(root, m);
         }
         // bind the sub classes
-        children.forEach(sub -> bindSubClass(sub, root, mappings, sessionFactoryBeanName, finalMapping,mappingCacheHolder, defaultColumnNameFetcher, columnNameForPropertyAndPathFetcher, grailsPropertyBinder, classBinder, propertyFromValueCreator, multiTenantFilterBinder, joinedSubClassBinder, unionSubclassBinder, singleTableSubclassBinder));
+        children.forEach(sub -> bindSubClass(sub, root, mappings, sessionFactoryBeanName, finalMapping,mappingCacheHolder, defaultColumnNameFetcher, classBinder, classPropertiesBinder, multiTenantFilterBinder, joinedSubClassBinder, unionSubclassBinder, singleTableSubclassBinder));
 
         multiTenantFilterBinder.addMultiTenantFilterIfNecessary(entity, root, mappings, defaultColumnNameFetcher);
 
@@ -312,9 +315,9 @@ public class GrailsDomainBinder
                               PersistentClass parent,
                               @Nonnull InFlightMetadataCollector mappings,
                               String sessionFactoryBeanName
-                            , Mapping m, MappingCacheHolder mappingCacheHolder, DefaultColumnNameFetcher defaultColumnNameFetcher, ColumnNameForPropertyAndPathFetcher columnNameForPropertyAndPathFetcher, GrailsPropertyBinder grailsPropertyBinder, ClassBinder classBinder, PropertyFromValueCreator propertyFromValueCreator, MultiTenantFilterBinder multiTenantFilterBinder, JoinedSubClassBinder joinedSubClassBinder, UnionSubclassBinder unionSubclassBinder, SingleTableSubclassBinder singleTabl [...]
+                            , Mapping m, MappingCacheHolder mappingCacheHolder, DefaultColumnNameFetcher defaultColumnNameFetcher, ClassBinder classBinder, ClassPropertiesBinder classPropertiesBinder, MultiTenantFilterBinder multiTenantFilterBinder, JoinedSubClassBinder joinedSubClassBinder, UnionSubclassBinder unionSubclassBinder, SingleTableSubclassBinder singleTableSubclassBinder) {
         mappingCacheHolder.cacheMapping(sub);
-        Subclass subClass = createSubclassMapping(sub, parent, mappings, sessionFactoryBeanName, m, defaultColumnNameFetcher, columnNameForPropertyAndPathFetcher, grailsPropertyBinder, classBinder, propertyFromValueCreator, multiTenantFilterBinder, joinedSubClassBinder, unionSubclassBinder, singleTableSubclassBinder);
+        Subclass subClass = createSubclassMapping(sub, parent, mappings, sessionFactoryBeanName, m, defaultColumnNameFetcher, classBinder, classPropertiesBinder, multiTenantFilterBinder, joinedSubClassBinder, unionSubclassBinder, singleTableSubclassBinder);
 
 
         parent.addSubclass(subClass);
@@ -325,11 +328,11 @@ public class GrailsDomainBinder
         var children = sub.getChildEntities(dataSourceName);
         if (!children.isEmpty()) {
             // bind the sub classes
-            children.forEach(sub1 -> bindSubClass(sub1, subClass, mappings, sessionFactoryBeanName, m,mappingCacheHolder, defaultColumnNameFetcher, columnNameForPropertyAndPathFetcher, grailsPropertyBinder, classBinder, propertyFromValueCreator, multiTenantFilterBinder, joinedSubClassBinder, unionSubclassBinder, singleTableSubclassBinder ));
+            children.forEach(sub1 -> bindSubClass(sub1, subClass, mappings, sessionFactoryBeanName, m,mappingCacheHolder, defaultColumnNameFetcher, classBinder, classPropertiesBinder, multiTenantFilterBinder, joinedSubClassBinder, unionSubclassBinder, singleTableSubclassBinder ));
         }
     }
 
-    private @NonNull Subclass createSubclassMapping(@NonNull GrailsHibernatePersistentEntity subEntity, PersistentClass parent, @NonNull InFlightMetadataCollector mappings, String sessionFactoryBeanName, Mapping m, DefaultColumnNameFetcher defaultColumnNameFetcher, ColumnNameForPropertyAndPathFetcher columnNameForPropertyAndPathFetcher, GrailsPropertyBinder grailsPropertyBinder, ClassBinder classBinder, PropertyFromValueCreator propertyFromValueCreator, MultiTenantFilterBinder multiTenan [...]
+    private @NonNull Subclass createSubclassMapping(@NonNull GrailsHibernatePersistentEntity subEntity, PersistentClass parent, @NonNull InFlightMetadataCollector mappings, String sessionFactoryBeanName, Mapping m, DefaultColumnNameFetcher defaultColumnNameFetcher, ClassBinder classBinder, ClassPropertiesBinder classPropertiesBinder, MultiTenantFilterBinder multiTenantFilterBinder, JoinedSubClassBinder joinedSubClassBinder, UnionSubclassBinder unionSubclassBinder, SingleTableSubclassBind [...]
         Subclass subClass;
         subEntity.configureDerivedProperties();
         if (!m.getTablePerHierarchy() && !m.isTablePerConcreteClass()) {
@@ -355,7 +358,7 @@ public class GrailsDomainBinder
         subClass.setAbstract(subEntity.isAbstract());
         subClass.setEntityName(subEntity.getName());
         subClass.setJpaEntityName(GrailsHibernateUtil.unqualify(subEntity.getName()));
-        createClassProperties(subEntity, subClass, mappings, sessionFactoryBeanName, grailsPropertyBinder, propertyFromValueCreator);
+        classPropertiesBinder.bindClassProperties(subEntity, subClass, mappings, sessionFactoryBeanName);
         return subClass;
     }
 
@@ -427,9 +430,8 @@ public class GrailsDomainBinder
                                                        String sessionFactoryBeanName,
                                                        IdentityBinder identityBinder,
                                                        VersionBinder versionBinder,
-                                                       GrailsPropertyBinder grailsPropertyBinder,
                                                        ClassBinder classBinder,
-                                                       PropertyFromValueCreator propertyFromValueCreator
+                                                       ClassPropertiesBinder classPropertiesBinder
     ) {
 
         RootClass root = new RootClass(this.metadataBuildingContext);
@@ -475,36 +477,12 @@ public class GrailsDomainBinder
         identityBinder.bindIdentity(domainClass, root, mappings, gormMapping, sessionFactoryBeanName);
         versionBinder.bindVersion(domainClass.getVersion(), root);
         root.createPrimaryKey();
-        createClassProperties(domainClass, root, mappings, sessionFactoryBeanName, grailsPropertyBinder, propertyFromValueCreator);
+        classPropertiesBinder.bindClassProperties(domainClass, root, mappings, sessionFactoryBeanName);
 
         return root;
     }
 
 
-    /**
-     * Creates and binds the properties for the specified Grails domain class and PersistentClass
-     * and binds them to the Hibernate runtime meta model
-     *
-     * @param domainClass     The Grails domain class
-     * @param persistentClass The Hibernate PersistentClass instance
-     * @param mappings        The Hibernate Mappings instance
-     * @param sessionFactoryBeanName  the session factory bean name
-     */
-    private void createClassProperties(@Nonnull GrailsHibernatePersistentEntity domainClass, PersistentClass persistentClass,
-                                         @Nonnull InFlightMetadataCollector mappings, String sessionFactoryBeanName, GrailsPropertyBinder grailsPropertyBinder, PropertyFromValueCreator propertyFromValueCreator) {
-
-
-
-        for (GrailsHibernatePersistentProperty currentGrailsProp : domainClass.getPersistentPropertiesToBind()) {
-           var value = grailsPropertyBinder.bindProperty(persistentClass, currentGrailsProp, mappings, sessionFactoryBeanName);
-           persistentClass.addProperty(propertyFromValueCreator.createProperty(value, currentGrailsProp));
-        }
-
-        new NaturalIdentifierBinder().bindNaturalIdentifier(domainClass.getMappedForm(), persistentClass);
-    }
-
-
-
     public MetadataBuildingContext getMetadataBuildingContext() {
         return metadataBuildingContext;
     }
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
new file mode 100644
index 0000000000..f8f32804c4
--- /dev/null
+++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassPropertiesBinder.java
@@ -0,0 +1,56 @@
+package org.grails.orm.hibernate.cfg.domainbinding.binder;
+
+import jakarta.annotation.Nonnull;
+import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentEntity;
+import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentProperty;
+import org.grails.orm.hibernate.cfg.domainbinding.util.PropertyFromValueCreator;
+import org.hibernate.boot.spi.InFlightMetadataCollector;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Value;
+
+/**
+ * Binds the properties of a Grails domain class to the Hibernate meta-model.
+ *
+ * @author Graeme Rocher
+ * @since 7.0
+ */
+public class ClassPropertiesBinder {
+
+    private final GrailsPropertyBinder grailsPropertyBinder;
+    private final PropertyFromValueCreator propertyFromValueCreator;
+    private final NaturalIdentifierBinder naturalIdentifierBinder;
+
+    public ClassPropertiesBinder(GrailsPropertyBinder grailsPropertyBinder,
+                                 PropertyFromValueCreator propertyFromValueCreator,
+                                 NaturalIdentifierBinder naturalIdentifierBinder) {
+        this.grailsPropertyBinder = grailsPropertyBinder;
+        this.propertyFromValueCreator = propertyFromValueCreator;
+        this.naturalIdentifierBinder = naturalIdentifierBinder;
+    }
+
+    public ClassPropertiesBinder(GrailsPropertyBinder grailsPropertyBinder,
+                                 PropertyFromValueCreator propertyFromValueCreator) {
+        this(grailsPropertyBinder, propertyFromValueCreator, new NaturalIdentifierBinder());
+    }
+
+    /**
+     * Binds the properties of the specified Grails domain class to the Hibernate meta-model.
+     *
+     * @param domainClass The Grails domain class
+     * @param persistentClass The Hibernate PersistentClass instance
+     * @param mappings The Hibernate InFlightMetadataCollector instance
+     * @param sessionFactoryBeanName The session factory bean name
+     */
+    public void bindClassProperties(@Nonnull GrailsHibernatePersistentEntity domainClass,
+                                    PersistentClass persistentClass,
+                                    @Nonnull InFlightMetadataCollector mappings,
+                                    String sessionFactoryBeanName) {
+
+        for (GrailsHibernatePersistentProperty currentGrailsProp : domainClass.getPersistentPropertiesToBind()) {
+            Value value = grailsPropertyBinder.bindProperty(persistentClass, currentGrailsProp, mappings, sessionFactoryBeanName);
+            persistentClass.addProperty(propertyFromValueCreator.createProperty(value, currentGrailsProp));
+        }
+
+        naturalIdentifierBinder.bindNaturalIdentifier(domainClass.getMappedForm(), persistentClass);
+    }
+}
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
new file mode 100644
index 0000000000..c096318426
--- /dev/null
+++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassPropertiesBinderSpec.groovy
@@ -0,0 +1,57 @@
+package org.grails.orm.hibernate.cfg.domainbinding.binder
+
+import grails.gorm.specs.HibernateGormDatastoreSpec
+import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentEntity
+import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentProperty
+import org.grails.orm.hibernate.cfg.Mapping
+import org.grails.orm.hibernate.cfg.domainbinding.util.PropertyFromValueCreator
+import org.hibernate.boot.spi.InFlightMetadataCollector
+import org.hibernate.mapping.Property
+import org.hibernate.mapping.RootClass
+import org.hibernate.mapping.Value
+
+class ClassPropertiesBinderSpec extends HibernateGormDatastoreSpec {
+
+    void "test bindClassProperties"() {
+        given:
+        def grailsPropertyBinder = Mock(GrailsPropertyBinder)
+        def propertyFromValueCreator = Mock(PropertyFromValueCreator)
+        def naturalIdentifierBinder = Mock(NaturalIdentifierBinder)
+        def binder = new ClassPropertiesBinder(grailsPropertyBinder, propertyFromValueCreator, naturalIdentifierBinder)
+
+        def domainClass = Mock(GrailsHibernatePersistentEntity)
+        def persistentClass = new RootClass(getGrailsDomainBinder().getMetadataBuildingContext())
+        def mappings = Mock(InFlightMetadataCollector)
+        def sessionFactoryBeanName = "sessionFactory"
+
+        def prop1 = Mock(GrailsHibernatePersistentProperty)
+        def prop2 = Mock(GrailsHibernatePersistentProperty)
+        domainClass.getPersistentPropertiesToBind() >> [prop1, prop2]
+        
+        def value1 = Mock(Value)
+        def value2 = Mock(Value)
+        
+        def hibernateProp1 = new Property()
+        hibernateProp1.setName("hibernateProp1")
+        def hibernateProp2 = new Property()
+        hibernateProp2.setName("hibernateProp2")
+        
+        def mapping = Mock(Mapping)
+        domainClass.getMappedForm() >> mapping
+
+        when:
+        binder.bindClassProperties(domainClass, persistentClass, mappings, sessionFactoryBeanName)
+
+        then:
+        1 * grailsPropertyBinder.bindProperty(persistentClass, prop1, mappings, sessionFactoryBeanName) >> value1
+        1 * propertyFromValueCreator.createProperty(value1, prop1) >> hibernateProp1
+
+        1 * grailsPropertyBinder.bindProperty(persistentClass, prop2, mappings, sessionFactoryBeanName) >> value2
+        1 * propertyFromValueCreator.createProperty(value2, prop2) >> hibernateProp2
+
+        persistentClass.getProperty("hibernateProp1") == hibernateProp1
+        persistentClass.getProperty("hibernateProp2") == hibernateProp2
+
+        1 * naturalIdentifierBinder.bindNaturalIdentifier(mapping, persistentClass)
+    }
+}