This is an automated email from the ASF dual-hosted git repository. borinquenkid pushed a commit to branch merge-hibernate6 in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 381b749fa926f6c4a7656f9326766c982805f2b5 Author: Walter Duque de Estrada <[email protected]> AuthorDate: Sun Jul 6 09:54:10 2025 -0500 refactoring and testing Binding of Class --- .../orm/hibernate/cfg/GrailsDomainBinder.java | 82 ++++++++------------- .../hibernate/cfg/domainbinding/ClassBinder.java | 43 +++++++++++ .../gorm/specs/HibernateGormDatastoreSpec.groovy | 79 ++++++++++++++++++-- .../hibernate/cfg/GrailsDomainBinderSpec.groovy | 74 +------------------ .../cfg/domainbinding/ClassBinderSpec.groovy | 85 ++++++++++++++++++++++ 5 files changed, 235 insertions(+), 128 deletions(-) diff --git a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java index d8ca9123d3..4e57e4739b 100644 --- a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java +++ b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java @@ -34,6 +34,7 @@ import org.grails.datastore.mapping.model.types.ToOne; import org.grails.datastore.mapping.reflect.EntityReflector; import org.grails.datastore.mapping.reflect.NameUtils; import org.grails.orm.hibernate.access.TraitPropertyAccessStrategy; +import org.grails.orm.hibernate.cfg.domainbinding.ClassBinder; import org.hibernate.FetchMode; import org.hibernate.MappingException; import org.hibernate.boot.internal.MetadataBuildingContextRootImpl; @@ -109,7 +110,9 @@ import java.util.Set; import java.util.SortedSet; import java.util.StringTokenizer; -;import static org.hibernate.boot.model.naming.Identifier.toIdentifier; +import static org.hibernate.boot.model.naming.Identifier.toIdentifier; + +; /** * Handles the binding Grails domain classes and properties to the Hibernate runtime meta model. @@ -155,6 +158,7 @@ public class GrailsDomainBinder implements MetadataContributor { private final String sessionFactoryName; private final String dataSourceName; private final HibernateMappingContext hibernateMappingContext; + private final ClassBinder classBinding; private Closure defaultMapping; private PersistentEntityNamingStrategy namingStrategy; private MetadataBuildingContext metadataBuildingContext; @@ -163,21 +167,35 @@ public class GrailsDomainBinder implements MetadataContributor { return metadataBuildingContext.getMetadataCollector().getDatabase().getJdbcEnvironment(); } - - - public GrailsDomainBinder( - String dataSourceName, - String sessionFactoryName, - HibernateMappingContext hibernateMappingContext) { + public GrailsDomainBinder(String dataSourceName + , String sessionFactoryName + , HibernateMappingContext hibernateMappingContext + , ClassBinder classBinding) { this.sessionFactoryName = sessionFactoryName; this.dataSourceName = dataSourceName; this.hibernateMappingContext = hibernateMappingContext; + this.classBinding = classBinding; // pre-build mappings for (PersistentEntity persistentEntity : hibernateMappingContext.getPersistentEntities()) { evaluateMapping(persistentEntity); } } + + + + public GrailsDomainBinder( + String dataSourceName, + String sessionFactoryName, + HibernateMappingContext hibernateMappingContext) { + this(dataSourceName + , sessionFactoryName + , hibernateMappingContext + , new ClassBinder() + ); + + } + /** * The default mapping defined by {@link org.grails.datastore.mapping.config.Settings#SETTING_DEFAULT_MAPPING} * @param defaultMapping The default mapping @@ -1379,46 +1397,6 @@ public class GrailsDomainBinder implements MetadataContributor { // no-op, here for compatibility } - /** - * Binds the specified persistant class to the runtime model based on the - * properties defined in the domain class - * - * @param domainClass The Grails domain class - * @param persistentClass The persistant class - * @param mappings Existing mappings - */ - private void bindClass(PersistentEntity domainClass, PersistentClass persistentClass, InFlightMetadataCollector mappings) { - - boolean autoImport = mappings.getMetadataBuildingOptions().getMappingDefaults().isAutoImportEnabled(); - org.grails.datastore.mapping.config.Entity mappedForm = domainClass.getMapping().getMappedForm(); - if (mappedForm instanceof Mapping) { - autoImport = ((Mapping) mappedForm).isAutoImport(); - } - - // set lazy loading for now - persistentClass.setLazy(true); - final String entityName = domainClass.getName(); - persistentClass.setEntityName(entityName); - persistentClass.setJpaEntityName(autoImport ? unqualify(entityName) : entityName); - persistentClass.setProxyInterfaceName(entityName); - persistentClass.setClassName(entityName); - - // set dynamic insert to false - persistentClass.setDynamicInsert(false); - // set dynamic update to false - persistentClass.setDynamicUpdate(false); - // set select before update to false - persistentClass.setSelectBeforeUpdate(false); - - // add import to mappings - String en = persistentClass.getEntityName(); - - if (autoImport && en.indexOf('.') > 0) { - String unqualified = unqualify(en); - mappings.addImport(unqualified, en); - } - } - /** * Binds a root class (one with no super classes) to the runtime meta model * based on the supplied Grails domain class @@ -1434,7 +1412,7 @@ public class GrailsDomainBinder implements MetadataContributor { } RootClass root = new RootClass(this.metadataBuildingContext); root.setAbstract(entity.isAbstract()); - bindClass(entity, root, mappings); + classBinding.bindClass(entity, root, mappings); bindRootPersistentClassCommonValues(entity, root, mappings, sessionFactoryBeanName); var children = entity.getMappingContext() @@ -1604,7 +1582,7 @@ public class GrailsDomainBinder implements MetadataContributor { private void bindUnionSubclass(HibernatePersistentEntity subClass, UnionSubclass unionSubclass, InFlightMetadataCollector mappings, String sessionFactoryBeanName) throws MappingException { - bindClass(subClass, unionSubclass, mappings); + classBinding.bindClass(subClass, unionSubclass, mappings); Mapping subMapping = getMapping(subClass.getJavaClass()); @@ -1650,7 +1628,7 @@ public class GrailsDomainBinder implements MetadataContributor { */ private void bindJoinedSubClass(HibernatePersistentEntity sub, JoinedSubclass joinedSubclass, InFlightMetadataCollector mappings, Mapping gormMapping, String sessionFactoryBeanName) { - bindClass(sub, joinedSubclass, mappings); + classBinding.bindClass(sub, joinedSubclass, mappings); String schemaName = getSchemaName(mappings); String catalogName = getCatalogName(mappings); @@ -1700,7 +1678,7 @@ public class GrailsDomainBinder implements MetadataContributor { */ private void bindSubClass(HibernatePersistentEntity sub, Subclass subClass, InFlightMetadataCollector mappings, String sessionFactoryBeanName) { - bindClass(sub, subClass, mappings); + classBinding.bindClass(sub, subClass, mappings); if (LOG.isDebugEnabled()) LOG.debug("Mapping subclass: " + subClass.getEntityName() + @@ -3415,7 +3393,7 @@ public class GrailsDomainBinder implements MetadataContributor { return GrailsHibernateUtil.unqualify(qualifiedName); } - private MetadataBuildingContext getMetadataBuildingContext() { + public MetadataBuildingContext getMetadataBuildingContext() { return metadataBuildingContext; } diff --git a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ClassBinder.java b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ClassBinder.java new file mode 100644 index 0000000000..3629b1d20e --- /dev/null +++ b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ClassBinder.java @@ -0,0 +1,43 @@ +package org.grails.orm.hibernate.cfg.domainbinding; + +import org.grails.datastore.mapping.model.PersistentEntity; +import org.grails.orm.hibernate.cfg.Mapping; +import org.hibernate.boot.spi.InFlightMetadataCollector; +import org.hibernate.mapping.PersistentClass; + +import static org.grails.orm.hibernate.cfg.GrailsHibernateUtil.unqualify; + +public class ClassBinder { + + /** + * Binds the specified persistant class to the runtime model based on the + * properties defined in the domain class + * + * @param persistentEntity The Grails domain class + * @param persistentClass The persistant class + * @param collector Existing collector + */ + public void bindClass(PersistentEntity persistentEntity, PersistentClass persistentClass, InFlightMetadataCollector collector) { + persistentClass.setLazy(true); + var entityName = persistentEntity.getName(); + persistentClass.setEntityName(entityName); + persistentClass.setJpaEntityName(entityName); + persistentClass.setProxyInterfaceName(entityName); + persistentClass.setClassName(entityName); + persistentClass.setDynamicInsert(false); + persistentClass.setDynamicUpdate(false); + persistentClass.setSelectBeforeUpdate(false); + + boolean autoImport; + if (persistentEntity.getMapping().getMappedForm() instanceof Mapping mappedForm) { + autoImport = mappedForm.isAutoImport(); + } else { + autoImport = collector.getMetadataBuildingOptions().getMappingDefaults().isAutoImportEnabled(); + } + if (autoImport) { + String unqualified = unqualify(entityName); + persistentClass.setJpaEntityName(unqualified); + collector.addImport(unqualified, entityName); + } + } +} diff --git a/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy b/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy index 0832bd7334..11b5c31bb1 100644 --- a/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy +++ b/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy @@ -2,11 +2,20 @@ package grails.gorm.specs import org.apache.grails.data.hibernate6.core.GrailsDataHibernate6TckManager import org.apache.grails.data.testing.tck.base.GrailsDataTckSpec -import org.grails.datastore.mapping.core.DatastoreUtils -import org.grails.datastore.mapping.core.Session -import org.springframework.transaction.support.DefaultTransactionDefinition -import spock.lang.Shared -import spock.lang.Specification +import org.grails.orm.hibernate.cfg.GrailsDomainBinder +import org.grails.orm.hibernate.cfg.HibernateMappingContext +import org.grails.orm.hibernate.cfg.HibernatePersistentEntity +import org.hibernate.boot.MetadataSources +import org.hibernate.boot.internal.BootstrapContextImpl +import org.hibernate.boot.internal.InFlightMetadataCollectorImpl +import org.hibernate.boot.internal.MetadataBuilderImpl +import org.hibernate.boot.registry.BootstrapServiceRegistry +import org.hibernate.boot.registry.StandardServiceRegistryBuilder +import org.hibernate.boot.registry.classloading.spi.ClassLoaderService +import org.hibernate.boot.spi.MetadataContributor +import org.hibernate.dialect.H2Dialect +import org.hibernate.internal.SessionFactoryImpl +import org.hibernate.service.spi.ServiceRegistryImplementor /** * The original GormDataStoreSpec destroyed the setup @@ -30,4 +39,64 @@ class HibernateGormDatastoreSpec extends GrailsDataTckSpec<GrailsDataHibernate6T 'hibernate.jpa.compliance.cascade': 'true', ] } + + HibernatePersistentEntity createPersistentEntity(String className + , Map<String, Class> fieldProperties + , Map<String, String> staticMapping + + ) { + def classLoader = new GroovyClassLoader() + def classText = """ + package foo + import grails.gorm.annotation.Entity + import grails.gorm.hibernate.HibernateEntity + @Entity + class ${className} implements HibernateEntity<${className}> { + + ${fieldProperties.collect { name, type -> "${type.simpleName} ${name}" }.join('\n ')} + + static mapping = { + ${staticMapping.collect { name, value -> "${name} ${value}" }.join('\n ')} + } + } + """ + + def clazz = classLoader.parseClass(classText) + getMappingContext().addPersistentEntity(clazz) as HibernatePersistentEntity + } + + protected InFlightMetadataCollectorImpl getCollector() { + def bootstrapServiceRegistry = getServiceRegistry() + .getParentServiceRegistry() + .getParentServiceRegistry() as BootstrapServiceRegistry + def serviceRegistry = new StandardServiceRegistryBuilder(bootstrapServiceRegistry) + .applySetting("hibernate.dialect", H2Dialect.class.getName()) + .applySetting("jakarta.persistence.jdbc.url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1") + .applySetting("jakarta.persistence.jdbc.driver", "org.h2.Driver") + .build() + def options = new MetadataBuilderImpl( + new MetadataSources(serviceRegistry) + ).getMetadataBuildingOptions() + new InFlightMetadataCollectorImpl( + new BootstrapContextImpl( serviceRegistry, options) + , options); + } + + protected HibernateMappingContext getMappingContext() { + manager.hibernateDatastore.getMappingContext() + } + + protected GrailsDomainBinder getGrailsDomainBinder() { + def registry = getServiceRegistry() + registry + .getParentServiceRegistry() + .getService(ClassLoaderService.class) + .loadJavaServices(MetadataContributor.class) + .find { it instanceof GrailsDomainBinder } + } + + protected ServiceRegistryImplementor getServiceRegistry() { + (manager.hibernateDatastore.sessionFactory as SessionFactoryImpl) + .getServiceRegistry() + } } diff --git a/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinderSpec.groovy b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinderSpec.groovy index 6d9c841f62..eb3b349bb6 100644 --- a/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinderSpec.groovy +++ b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinderSpec.groovy @@ -1,21 +1,6 @@ package org.grails.orm.hibernate.cfg import grails.gorm.specs.HibernateGormDatastoreSpec -import org.apache.grails.data.testing.tck.domains.EagerOwner -import org.apache.grails.data.testing.tck.domains.Face -import org.apache.grails.data.testing.tck.domains.Person -import org.apache.grails.data.testing.tck.domains.Pet -import org.hibernate.boot.MetadataSources -import org.hibernate.boot.internal.BootstrapContextImpl -import org.hibernate.boot.internal.InFlightMetadataCollectorImpl -import org.hibernate.boot.internal.MetadataBuilderImpl -import org.hibernate.boot.registry.BootstrapServiceRegistry -import org.hibernate.boot.registry.StandardServiceRegistryBuilder -import org.hibernate.boot.registry.classloading.spi.ClassLoaderService -import org.hibernate.internal.SessionFactoryImpl -import org.hibernate.service.spi.ServiceRegistryImplementor -import org.hibernate.dialect.H2Dialect -import org.hibernate.boot.spi.MetadataContributor class GrailsDomainBinderSpec extends HibernateGormDatastoreSpec { @@ -25,67 +10,14 @@ class GrailsDomainBinderSpec extends HibernateGormDatastoreSpec { def collector = getCollector() def grailsDomainBinder = getGrailsDomainBinder() - - def args = ["Book", [title: String, author: String, publishedYear: Integer]] - def persistentEntity = createPersistentEntity(args[0] as String, args[1] as Map<String, Class>) + def simpleName = "Book" + def fieldProperties = [title: String] + def persistentEntity = createPersistentEntity(simpleName, fieldProperties, [:]) grailsDomainBinder.bindRoot(persistentEntity, collector,"sessionFactoryName") println("when") then: 1 == 1 } - HibernatePersistentEntity createPersistentEntity(String className, Map<String, Class> properties) { - def classLoader = new GroovyClassLoader() - def classText = """ - import grails.gorm.annotation.Entity - import grails.gorm.hibernate.HibernateEntity - @Entity - class ${className} implements HibernateEntity<${className}> { - ${properties.collect { name, type -> "${type.simpleName} ${name}" }.join('\n ')} - - static mapping = { - id generator: 'native' - version false - } - } - """ - - def clazz = classLoader.parseClass(classText) - getMappingContext().addPersistentEntity(clazz) as HibernatePersistentEntity - } - - private InFlightMetadataCollectorImpl getCollector() { - def bootstrapServiceRegistry = getServiceRegistry() - .getParentServiceRegistry() - .getParentServiceRegistry() as BootstrapServiceRegistry - def serviceRegistry = new StandardServiceRegistryBuilder(bootstrapServiceRegistry) - .applySetting("hibernate.dialect", H2Dialect.class.getName()) - .applySetting("jakarta.persistence.jdbc.url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1") - .applySetting("jakarta.persistence.jdbc.driver", "org.h2.Driver") - .build() - def options = new MetadataBuilderImpl( - new MetadataSources(serviceRegistry) - ).getMetadataBuildingOptions() - new InFlightMetadataCollectorImpl( - new BootstrapContextImpl( serviceRegistry, options) - , options); - } - private HibernateMappingContext getMappingContext() { - manager.hibernateDatastore.getMappingContext() - } - - private GrailsDomainBinder getGrailsDomainBinder() { - def registry = getServiceRegistry() - registry - .getParentServiceRegistry() - .getService(ClassLoaderService.class) - .loadJavaServices(MetadataContributor.class) - .find { it instanceof GrailsDomainBinder } - } - - private ServiceRegistryImplementor getServiceRegistry() { - (manager.hibernateDatastore.sessionFactory as SessionFactoryImpl) - .getServiceRegistry() - } } \ No newline at end of file diff --git a/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ClassBinderSpec.groovy b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ClassBinderSpec.groovy new file mode 100644 index 0000000000..478a048d13 --- /dev/null +++ b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ClassBinderSpec.groovy @@ -0,0 +1,85 @@ +package org.grails.orm.hibernate.cfg.domainbinding + +import grails.gorm.specs.HibernateGormDatastoreSpec +import org.hibernate.mapping.RootClass + +class ClassBinderSpec extends HibernateGormDatastoreSpec { + + + void "Test defaults"() { + when: + + def collector = getCollector() + def grailsDomainBinder = getGrailsDomainBinder() + + def simpleName = "Book" + def persistentName = "foo.Book" + def persistentEntity = createPersistentEntity(simpleName, [:], [:]) + def root = new RootClass(grailsDomainBinder.metadataBuildingContext); + def binder = new ClassBinder() + + binder.bindClass(persistentEntity,root, collector) + then: + root.getEntityName() == persistentName + root.getJpaEntityName() == simpleName + root.getProxyInterfaceName() == persistentName + root.getClassName() == persistentName + root.isLazy() + !root.useDynamicInsert() + !root.useDynamicUpdate() + !root.hasSelectBeforeUpdate() + collector.getImports()[simpleName] == persistentName + } + + void "Test autoImport true"() { + when: + + def collector = getCollector() + def grailsDomainBinder = getGrailsDomainBinder() + + def simpleName = "Book" + def persistentName = "foo.Book" + def persistentEntity = createPersistentEntity(simpleName, [:], [autoImport: "true"]) + def root = new RootClass(grailsDomainBinder.metadataBuildingContext); + def binder = new ClassBinder() + + binder.bindClass(persistentEntity,root, collector) + then: + root.getEntityName() == persistentName + root.getJpaEntityName() == simpleName + root.getProxyInterfaceName() == persistentName + root.getClassName() == persistentName + root.isLazy() + !root.useDynamicInsert() + !root.useDynamicUpdate() + !root.hasSelectBeforeUpdate() + collector.getImports()[simpleName] == persistentName + } + + void "Test autoImport false"() { + when: + + def collector = getCollector() + def grailsDomainBinder = getGrailsDomainBinder() + + def simpleName = "Book" + def persistentName = "foo.Book" + def persistentEntity = createPersistentEntity(simpleName, [:], [autoImport: "false"]) + def root = new RootClass(grailsDomainBinder.metadataBuildingContext); + def binder = new ClassBinder() + + binder.bindClass(persistentEntity,root, collector) + then: + root.getEntityName() == persistentName + root.getJpaEntityName() == persistentName + root.getProxyInterfaceName() == persistentName + root.getClassName() == persistentName + root.isLazy() + !root.useDynamicInsert() + !root.useDynamicUpdate() + !root.hasSelectBeforeUpdate() + !collector.getImports()[simpleName] + } + + +}
