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]
 }

Reply via email to