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 769a062fcdc073ee3a4f5145f9bc2c69863fece2 Author: Walter Duque de Estrada <[email protected]> AuthorDate: Thu Mar 5 21:08:44 2026 -0600 hibernate7: cleanup GrailsHibernateUtil --- .../orm/hibernate/cfg/GrailsHibernateUtil.java | 16 ++- .../hibernate/cfg/GrailsHibernateUtilSpec.groovy | 114 +++++++++++++++++++++ .../secondpass/ListSecondPassBinderSpec.groovy | 100 +++++++++++++++--- 3 files changed, 209 insertions(+), 21 deletions(-) diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernateUtil.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernateUtil.java index ef13486c3c..8282172c76 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernateUtil.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernateUtil.java @@ -95,9 +95,8 @@ public class GrailsHibernateUtil extends HibernateRuntimeUtils { /** @deprecated Use {@link org.grails.orm.hibernate.query.HibernateQueryArgument#LOCK} */ @Deprecated(since = "8.0", forRemoval = true) public static final String ARGUMENT_LOCK = HibernateQueryArgument.LOCK.value(); - public static final Class<?>[] EMPTY_CLASS_ARRAY = {}; - private static HibernateProxyHandler proxyHandler = new HibernateProxyHandler(); + private static final HibernateProxyHandler proxyHandler = new HibernateProxyHandler(); /** * Sets the target object to read-only using the given SessionFactory instance. This avoids @@ -182,9 +181,8 @@ public class GrailsHibernateUtil extends HibernateRuntimeUtils { */ @Deprecated public static void ensureCorrectGroovyMetaClass(Object target, Class<?> persistentClass) { - if (target instanceof GroovyObject) { - GroovyObject go = ((GroovyObject) target); - if (!go.getMetaClass().getTheClass().equals(persistentClass)) { + if (target instanceof GroovyObject go) { + if (!go.getMetaClass().getTheClass().equals(persistentClass)) { go.setMetaClass(GroovySystem.getMetaClassRegistry().getMetaClass(persistentClass)); } } @@ -247,7 +245,7 @@ public class GrailsHibernateUtil extends HibernateRuntimeUtils { return StringHelper.unqualify(qualifiedName); } - public static boolean isDomainClass(Class clazz) { + public static boolean isDomainClass(Class<?> clazz) { if (GormEntity.class.isAssignableFrom(clazz)) { return true; } @@ -257,9 +255,9 @@ public class GrailsHibernateUtil extends HibernateRuntimeUtils { return false; } - if (((Class<?>) clazz).isEnum()) return false; + if (clazz.isEnum()) return false; - Annotation[] allAnnotations = ((Class<?>) clazz).getAnnotations(); + Annotation[] allAnnotations = clazz.getAnnotations(); for (Annotation annotation : allAnnotations) { Class<? extends Annotation> type = annotation.annotationType(); String annName = type.getName(); @@ -271,7 +269,7 @@ public class GrailsHibernateUtil extends HibernateRuntimeUtils { } } - Class<?> testClass = (Class<?>) clazz; + Class<?> testClass = clazz; while (testClass != null && !testClass.equals(GroovyObject.class) && !testClass.equals(Object.class)) { diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/GrailsHibernateUtilSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/GrailsHibernateUtilSpec.groovy new file mode 100644 index 0000000000..a3e9c390b4 --- /dev/null +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/GrailsHibernateUtilSpec.groovy @@ -0,0 +1,114 @@ +/* + * 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 + +import grails.gorm.annotation.Entity +import grails.gorm.specs.HibernateGormDatastoreSpec +import org.grails.datastore.mapping.model.config.GormProperties +import org.hibernate.proxy.HibernateProxy +import org.hibernate.proxy.LazyInitializer +import spock.lang.Unroll + +class GrailsHibernateUtilSpec extends HibernateGormDatastoreSpec { + + @Unroll + def "test isDomainClass for #clazz.simpleName"() { + expect: + GrailsHibernateUtil.isDomainClass(clazz) == expected + + where: + clazz | expected + GHUBook | true + GHUNonDomain | false + String | false + GHUAnnotatedEntity | true + } + + def "test incrementVersion"() { + given: + def book = new GHUBook(version: 1L) + + when: + GrailsHibernateUtil.incrementVersion(book) + + then: + book.version == 2L + } + + def "test incrementVersion with non-long version"() { + given: + def obj = new GHUNonDomain() + + when: + GrailsHibernateUtil.incrementVersion(obj) + + then: + noExceptionThrown() + } + + def "test qualify and unqualify"() { + expect: + GrailsHibernateUtil.qualify("org.test", "MyClass") == "org.test.MyClass" + GrailsHibernateUtil.unqualify("org.test.MyClass") == "MyClass" + } + + def "test isNotEmpty"() { + expect: + GrailsHibernateUtil.isNotEmpty("test") + !GrailsHibernateUtil.isNotEmpty("") + !GrailsHibernateUtil.isNotEmpty(null) + } + + def "test unwrapIfProxy with non-proxy"() { + given: + def obj = new Object() + + expect: + GrailsHibernateUtil.unwrapIfProxy(obj).is(obj) + GrailsHibernateUtil.unwrapIfProxy(null) == null + } + + def "test unwrapIfProxy with EntityProxy"() { + given: + def implementation = new Object() + def proxy = [ + getTarget: { implementation }, + isInitialized: { true } + ] as org.grails.datastore.mapping.proxy.EntityProxy + + expect: + GrailsHibernateUtil.unwrapIfProxy(proxy).is(implementation) + } +} + +@Entity +class GHUBook { + Long id + Long version + String title +} + +class GHUNonDomain { + String name +} + [email protected] +class GHUAnnotatedEntity { + Long id +} diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPassBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPassBinderSpec.groovy index 5b6fd92cc8..1b3f1b68a8 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPassBinderSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/ListSecondPassBinderSpec.groovy @@ -1,8 +1,12 @@ package org.grails.orm.hibernate.cfg.domainbinding.secondpass import grails.gorm.specs.HibernateGormDatastoreSpec +import org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy +import org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueColumnBinder import org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyProperty +import org.hibernate.boot.spi.InFlightMetadataCollector +import org.hibernate.boot.spi.MetadataBuildingContext import org.hibernate.mapping.OneToMany import org.hibernate.mapping.RootClass import org.hibernate.boot.spi.MetadataBuildingContext @@ -50,8 +54,7 @@ import org.grails.orm.hibernate.cfg.domainbinding.binder.SingleTableSubclassBind class ListSecondPassBinderSpec extends HibernateGormDatastoreSpec { - protected Map getBinders(GrailsDomainBinder binder) { - def collector = getCollector() + protected Map getBinders(GrailsDomainBinder binder, InFlightMetadataCollector collector = getCollector()) { MetadataBuildingContext metadataBuildingContext = binder.getMetadataBuildingContext() PersistentEntityNamingStrategy namingStrategy = binder.getNamingStrategy() JdbcEnvironment jdbcEnvironment = binder.getJdbcEnvironment() @@ -85,7 +88,7 @@ class ListSecondPassBinderSpec extends HibernateGormDatastoreSpec { simpleValueColumnFetcher , collectionHolder, - getCollector() + collector ) PropertyFromValueCreator propertyFromValueCreator = new PropertyFromValueCreator() ComponentUpdater componentUpdater = new ComponentUpdater(propertyFromValueCreator) @@ -114,18 +117,18 @@ class ListSecondPassBinderSpec extends HibernateGormDatastoreSpec { IdentityBinder identityBinder = new IdentityBinder(simpleIdBinder, compositeIdBinder) VersionBinder versionBinder = new VersionBinder(metadataBuildingContext, simpleValueBinder, propertyBinderHelper, BasicValue::new) - ClassBinder classBinder = new ClassBinder(getCollector()) + ClassBinder classBinder = new ClassBinder(collector) ClassPropertiesBinder classPropertiesBinder = new ClassPropertiesBinder(propertyBinder, propertyFromValueCreator) - MultiTenantFilterBinder multiTenantFilterBinder = new MultiTenantFilterBinder(new org.grails.orm.hibernate.cfg.domainbinding.util.GrailsPropertyResolver(), new org.grails.orm.hibernate.cfg.domainbinding.util.MultiTenantFilterDefinitionBinder(), getCollector(), defaultColumnNameFetcher) - JoinedSubClassBinder joinedSubClassBinder = new JoinedSubClassBinder(metadataBuildingContext, namingStrategy, new org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueColumnBinder(), columnNameForPropertyAndPathFetcher, classBinder, getCollector()) - UnionSubclassBinder unionSubclassBinder = new UnionSubclassBinder(metadataBuildingContext, namingStrategy, classBinder, getCollector()) + MultiTenantFilterBinder multiTenantFilterBinder = new MultiTenantFilterBinder(new org.grails.orm.hibernate.cfg.domainbinding.util.GrailsPropertyResolver(), new org.grails.orm.hibernate.cfg.domainbinding.util.MultiTenantFilterDefinitionBinder(), collector, defaultColumnNameFetcher) + JoinedSubClassBinder joinedSubClassBinder = new JoinedSubClassBinder(metadataBuildingContext, namingStrategy, new org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueColumnBinder(), columnNameForPropertyAndPathFetcher, classBinder, collector) + UnionSubclassBinder unionSubclassBinder = new UnionSubclassBinder(metadataBuildingContext, namingStrategy, classBinder, collector) SingleTableSubclassBinder singleTableSubclassBinder = new SingleTableSubclassBinder(classBinder) SubclassMappingBinder subclassMappingBinder = new SubclassMappingBinder(metadataBuildingContext, joinedSubClassBinder, unionSubclassBinder, singleTableSubclassBinder, classPropertiesBinder) - SubClassBinder subClassBinder = new SubClassBinder(binder.getMappingCacheHolder(), subclassMappingBinder, multiTenantFilterBinder, "dataSource", getCollector()) - RootPersistentClassCommonValuesBinder rootPersistentClassCommonValuesBinder = new RootPersistentClassCommonValuesBinder(metadataBuildingContext, namingStrategy, identityBinder, versionBinder, classBinder, classPropertiesBinder, getCollector()) + SubClassBinder subClassBinder = new SubClassBinder(binder.getMappingCacheHolder(), subclassMappingBinder, multiTenantFilterBinder, "dataSource", collector) + RootPersistentClassCommonValuesBinder rootPersistentClassCommonValuesBinder = new RootPersistentClassCommonValuesBinder(metadataBuildingContext, namingStrategy, identityBinder, versionBinder, classBinder, classPropertiesBinder, collector) DiscriminatorPropertyBinder discriminatorPropertyBinder = new DiscriminatorPropertyBinder(metadataBuildingContext, binder.getMappingCacheHolder(), new org.grails.orm.hibernate.cfg.domainbinding.binder.ConfiguredDiscriminatorBinder(new org.grails.orm.hibernate.cfg.domainbinding.binder.SimpleValueColumnBinder(), new ColumnConfigToColumnBinder()), new org.grails.orm.hibernate.cfg.domainbinding.binder.DefaultDiscriminatorBinder(new org.grails.orm.hibernate.cfg.domainbinding.binder.Si [...] - RootBinder rootBinder = new RootBinder("default", multiTenantFilterBinder, subClassBinder, rootPersistentClassCommonValuesBinder, discriminatorPropertyBinder, getCollector()) + RootBinder rootBinder = new RootBinder("default", multiTenantFilterBinder, subClassBinder, rootPersistentClassCommonValuesBinder, discriminatorPropertyBinder, collector) return [ propertyBinder: propertyBinder, @@ -146,7 +149,7 @@ class ListSecondPassBinderSpec extends HibernateGormDatastoreSpec { } protected void bindRoot(GrailsDomainBinder binder, GrailsHibernatePersistentEntity entity, InFlightMetadataCollector mappings, String sessionFactoryBeanName) { - def binders = getBinders(binder) + def binders = getBinders(binder, mappings) binders.rootBinder.bindRoot(entity) } @@ -154,7 +157,9 @@ class ListSecondPassBinderSpec extends HibernateGormDatastoreSpec { manager.addAllDomainClasses([ org.apache.grails.data.testing.tck.domains.Pet, org.apache.grails.data.testing.tck.domains.Person, - org.apache.grails.data.testing.tck.domains.PetType + org.apache.grails.data.testing.tck.domains.PetType, + LSBAuthor, + LSBBook ]) } @@ -181,4 +186,75 @@ class ListSecondPassBinderSpec extends HibernateGormDatastoreSpec { collection.element instanceof OneToMany (collection.element as OneToMany).referencedEntityName == petEntity.name } + + void "test bidirectional list binding"() { + given: + def binder = getGrailsDomainBinder() + def collector = getCollector() + def metadataBuildingContext = binder.getMetadataBuildingContext() + def binders = getBinders(binder, collector) + def collectionBinder = binders.collectionBinder + def listBinder = collectionBinder.listSecondPassBinder + + def authorEntity = getPersistentEntity(LSBAuthor) as GrailsHibernatePersistentEntity + def bookEntity = getPersistentEntity(LSBBook) as GrailsHibernatePersistentEntity + def booksProp = authorEntity.getPropertyByName("books") as HibernateToManyProperty + + def rootClass = new RootClass(metadataBuildingContext) + rootClass.setEntityName(authorEntity.name) + rootClass.setJpaEntityName(authorEntity.name) + rootClass.setTable(collector.addTable(null, null, "LSB_AUTHOR", null, false, metadataBuildingContext)) + collector.addEntityBinding(rootClass) + + def bookRootClass = new RootClass(metadataBuildingContext) + bookRootClass.setEntityName(bookEntity.name) + bookRootClass.setJpaEntityName(bookEntity.name) + bookRootClass.setTable(collector.addTable(null, null, "LSB_BOOK", null, false, metadataBuildingContext)) + collector.addEntityBinding(bookRootClass) + + def authorValue = new org.hibernate.mapping.ManyToOne(metadataBuildingContext, bookRootClass.getTable()) + authorValue.setReferencedEntityName(authorEntity.name) + def authorCol = new org.hibernate.mapping.Column("author_id") + authorCol.setValue(authorValue) + authorValue.addColumn(authorCol) + def authorProp = new org.hibernate.mapping.Property() + authorProp.setName("author") + authorProp.setValue(authorValue) + bookRootClass.addProperty(authorProp) + + def persistentClasses = [ + (authorEntity.name): rootClass, + (bookEntity.name): bookRootClass + ] + + def list = new org.hibernate.mapping.List(metadataBuildingContext, rootClass) + list.setRole("${authorEntity.name}.books".toString()) + list.setCollectionTable(rootClass.getTable()) + + def element = new org.hibernate.mapping.ManyToOne(metadataBuildingContext, list.getCollectionTable()) + element.setReferencedEntityName(LSBBook.name) + list.setElement(element) + + when: + listBinder.bindListSecondPass(booksProp, persistentClasses, list) + + then: + noExceptionThrown() + list.index != null + list.baseIndex == 0 + } +} + [email protected] +class LSBAuthor { + Long id + List<LSBBook> books + static hasMany = [books: LSBBook] +} + [email protected] +class LSBBook { + Long id + LSBAuthor author + static belongsTo = [author: LSBAuthor] }
