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 ac81ba4c1694607440c9dfc13d27a9086a1fdf41
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Tue Mar 17 17:13:21 2026 -0500

    hibernate 7: refactor EnumTypeBinder
---
 .../cfg/domainbinding/binder/EnumTypeBinder.java   |  10 +-
 .../domainbinding/binder/GrailsPropertyBinder.java |   2 +-
 .../secondpass/BasicCollectionElementBinder.java   |   2 +-
 .../cfg/domainbinding/EnumTypeBinderSpec.groovy    | 163 +++++++--------------
 .../BasicCollectionElementBinderSpec.groovy        |   2 +-
 .../datastore/mapping/model/MappingFactory.java    |   7 +-
 6 files changed, 60 insertions(+), 126 deletions(-)

diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/EnumTypeBinder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/EnumTypeBinder.java
index 0f4cfd99e2..c6f4a607e0 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/EnumTypeBinder.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/EnumTypeBinder.java
@@ -71,18 +71,16 @@ public class EnumTypeBinder {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(EnumTypeBinder.class);
 
-    public BasicValue bindEnumType(
-            @Nonnull HibernateEnumProperty property, Class<?> propertyType, 
String path) {
+    public BasicValue bindEnumType(@Nonnull HibernateEnumProperty property, 
String path) {
         String columnName = 
columnNameForPropertyAndPathFetcher.getColumnNameForPropertyAndPath(property, 
path, null);
         BasicValue simpleValue = new BasicValue(metadataBuildingContext, 
property.getTable());
-        bindEnumType(property, propertyType, simpleValue, columnName);
+        bindEnumType(property, property.getType(), simpleValue, columnName);
         return simpleValue;
     }
 
-    public BasicValue bindEnumTypeForColumn(
-            @Nonnull HibernateToManyProperty property, Class<?> propertyType, 
@Nonnull String columnName) {
+    public BasicValue bindEnumTypeForColumn(@Nonnull HibernateToManyProperty 
property, @Nonnull String columnName) {
         BasicValue simpleValue = new BasicValue(metadataBuildingContext, 
property.getTable());
-        bindEnumType(property, propertyType, simpleValue, columnName);
+        bindEnumType(property,  property.getComponentType(), simpleValue, 
columnName);
         return simpleValue;
     }
 
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 faa0c85feb..ba19cfe1f7 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
@@ -75,7 +75,7 @@ public class GrailsPropertyBinder {
 
         // 1. Create Value and apply binders (consolidated block)
         if (currentGrailsProp instanceof HibernateEnumProperty 
hibernateEnumProperty) {
-            value = enumTypeBinder.bindEnumType(hibernateEnumProperty, 
currentGrailsProp.getType(), path);
+            value = enumTypeBinder.bindEnumType(hibernateEnumProperty, path);
         } else if (currentGrailsProp instanceof HibernateOneToOneProperty 
oneToOne
                 && oneToOne.isValidHibernateOneToOne()) {
             value = oneToOneBinder.bindOneToOne(oneToOne, path);
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/BasicCollectionElementBinder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/BasicCollectionElementBinder.java
index 80870f4fa8..e152f2b8e2 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/BasicCollectionElementBinder.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/BasicCollectionElementBinder.java
@@ -81,7 +81,7 @@ public class BasicCollectionElementBinder {
                     : new BackticksRemover().apply(prop) + UNDERSCORE + new 
BackticksRemover().apply(clazz);
         }
         if (isEnum) {
-            return enumTypeBinder.bindEnumTypeForColumn(property, 
referencedType, columnName);
+            return enumTypeBinder.bindEnumTypeForColumn(property, columnName);
         } else {
             String typeName = property.getTypeName(referencedType);
             BasicValue element = simpleValueColumnBinder.bindSimpleValue(
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/EnumTypeBinderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/EnumTypeBinderSpec.groovy
index 7b3561c451..25c2941784 100644
--- 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/EnumTypeBinderSpec.groovy
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/EnumTypeBinderSpec.groovy
@@ -1,22 +1,3 @@
-/*
- *  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.domainbinding
 
 import grails.gorm.specs.HibernateGormDatastoreSpec
@@ -31,8 +12,6 @@ import 
org.grails.orm.hibernate.cfg.domainbinding.util.BackticksRemover
 import 
org.grails.orm.hibernate.cfg.domainbinding.util.ColumnNameForPropertyAndPathFetcher
 import org.grails.orm.hibernate.cfg.domainbinding.util.DefaultColumnNameFetcher
 import org.hibernate.engine.spi.SharedSessionContractImplementor
-import org.hibernate.mapping.BasicValue
-import org.hibernate.mapping.Column
 import org.hibernate.mapping.Table
 import org.hibernate.mapping.RootClass
 import org.hibernate.usertype.UserType
@@ -65,11 +44,7 @@ class EnumTypeBinderSpec extends HibernateGormDatastoreSpec {
         binder = new EnumTypeBinder(metadataBuildingContext, 
columnNameFetcher, indexBinder, columnBinder)
     }
 
-    /**
-     * Helper to prevent the NullPointerException by linking the GORM entity
-     * to a Hibernate RootClass/Table.
-     */
-    private PersistentProperty setupEntity(Class clazz, Table table) {
+    private PersistentProperty setupProperty(Class clazz, String propertyName, 
Table table) {
         def grailsDomainBinder = getGrailsDomainBinder()
         def owner = createPersistentEntity(clazz, grailsDomainBinder) as 
GrailsHibernatePersistentEntity
 
@@ -77,122 +52,73 @@ class EnumTypeBinderSpec extends 
HibernateGormDatastoreSpec {
         rootClass.setTable(table)
         owner.setPersistentClass(rootClass)
 
-        return owner.getPropertyByName("status")
+        return owner.getPropertyByName(propertyName)
+    }
+
+    def "should bind enum type for a collection element"() {
+        given: "An entity with a collection of enums"
+        def table = new Table("person_statuses")
+        def property = setupProperty(PersonWithCollection, "statuses", table)
+
+        expect: "The property is a ToMany property"
+        property instanceof HibernateToManyProperty
+
+                when: "the enum is bound for the collection column"
+        // This will now successfully call property.getComponentType() 
internally
+        def result = binder.bindEnumTypeForColumn(property as 
HibernateToManyProperty, "status_name")
+
+        then: "The BasicValue is configured correctly"
+        result.getEnumerationStyle() == EnumType.STRING
+        
result.getTypeParameters().getProperty(GrailsDomainBinder.ENUM_CLASS_PROP) == 
Status01.name
     }
 
     @Unroll
     def "should bind enum type as #expectedHibernateType when mapping 
specifies enumType as '#enumTypeMapping'"() {
         given: "A root entity and its enum property"
         def table = new Table("person")
-        PersistentProperty property = setupEntity(clazz, table)
+        def property = setupProperty(clazz, "status", table)
 
-        when: "the enum is bound"
-        def simpleValue = binder.bindEnumTypeForColumn(property as 
HibernateToManyProperty, Status01, "status_col")
+        when: "the enum is bound via the standard path"
+        def simpleValue = binder.bindEnumType(property as 
HibernateEnumProperty, "")
 
         then: "the correct hibernate type is set"
         simpleValue.getTypeName() == expectedHibernateType
         simpleValue.getEnumerationStyle() == expectedEnumStyle
         simpleValue.isNullable() == nullable
 
-        and: "the enum class property is always set"
-        
simpleValue.getTypeParameters().getProperty(GrailsDomainBinder.ENUM_CLASS_PROP) 
== Status01.name
-
         where:
-        clazz    | enumTypeMapping  | expectedHibernateType                   
| expectedEnumStyle | nullable
-        Person01 | "default"        | null                                    
| EnumType.STRING   | false
-        Person02 | "string"         | null                                    
| EnumType.STRING   | true
-        Person03 | "ordinal"        | null                                    
| EnumType.ORDINAL  | true
-        Person04 | "identity"       | IdentityEnumType.class.getName()        
| null              | false
-        Person05 | UserTypeEnumType | UserTypeEnumType.class.getName()        
| null              | false
+        clazz    | enumTypeMapping  | expectedHibernateType            | 
expectedEnumStyle | nullable
+        Person01 | "default"        | null                             | 
EnumType.STRING   | false
+        Person02 | "string"         | null                             | 
EnumType.STRING   | true
+        Person03 | "ordinal"        | null                             | 
EnumType.ORDINAL  | true
+        Person04 | "identity"       | IdentityEnumType.class.getName() | null  
            | false
+        Person05 | UserTypeEnumType | UserTypeEnumType.class.getName() | null  
            | false
     }
 
     @Unroll
     def "should set column nullability"() {
         given: "A root entity and its enum property"
         def table = new Table("person")
-        def columnName = "status_col"
-        PersistentProperty property = setupEntity(clazz, table)
+        def property = setupProperty(clazz, "status", table)
 
         when: "the enum is bound"
-        def simpleValue = binder.bindEnumTypeForColumn(property as 
HibernateToManyProperty, Status01, columnName)
+        def simpleValue = binder.bindEnumType(property as 
HibernateEnumProperty, "")
 
         then:
-        table.columns.size() == 1
-        table.columns[0] == simpleValue.getColumn()
-        table.columns[0].isNullable() == nullable
-        table.columns[0].getValue() == simpleValue
-        table.columns[0].getName() == columnName
+        simpleValue.getColumns()[0].isNullable() == nullable
 
         where:
         clazz    | nullable
         Person01 | false
         Person02 | true
         Clown01  | true
-        Clown02  | true
-        Clown03  | true
-    }
-
-    @Unroll
-    def "should bind index and column constraints only when a column config is 
present"() {
-        given: "A root entity and its enum property"
-        def table = new Table("person")
-        def columnName = "status_col"
-        PersistentProperty property = setupEntity(clazz, table)
-
-        when: "the enum is bound"
-        binder.bindEnumTypeForColumn(property as HibernateToManyProperty, 
Status01, columnName)
-
-        then: "the index and column binders are invoked the correct number of 
times"
-        times * indexBinder.bindIndex(columnName, _ as Column, _, table)
-        times * columnBinder.bindColumnConfigToColumn(_ as Column, _, _)
-
-        where:
-        clazz    | times
-        Person01 | 0
-        Person02 | 1
-    }
-
-    def "should create BasicValue and bind enum type"() {
-        given: "A root entity and its enum property"
-        def table = new Table("person")
-        PersistentProperty property = setupEntity(Person01, table)
-
-        when: "the enum is bound using the new signature"
-        def result = binder.bindEnumType(property as HibernateEnumProperty, 
Status01, "")
-
-        then: "a BasicValue is returned and bound correctly"
-        result instanceof BasicValue
-        result.getTable() == table
-        result.getTypeName() == null
-        result.getEnumerationStyle() == EnumType.STRING
-        result.getColumns().size() == 1
-        result.getColumns()[0].getName() == "status"
     }
 }
 
 // --- Supporting Classes ---
 
-class UserTypeEnumType implements UserType {
-    @Override int getSqlType() { 0 }
-    @Override Class returnedClass() { null }
-    @Override boolean equals(Object x, Object y) { false }
-    @Override int hashCode(Object x) { 0 }
-    @Override Object nullSafeGet(ResultSet rs, int position, 
SharedSessionContractImplementor session, Object owner) throws SQLException { 
null }
-    @Override void nullSafeSet(PreparedStatement st, Object value, int index, 
SharedSessionContractImplementor session) throws SQLException {}
-    @Override Object deepCopy(Object value) { null }
-    @Override boolean isMutable() { false }
-    @Override Serializable disassemble(Object value) { null }
-    @Override Object assemble(Serializable cached, Object owner) { null }
-}
-
 enum Status01 { AVAILABLE, OUT_OF_STOCK }
 
-enum Status02 {
-    FOO(3), BAR(5)
-    Long id
-    Status02(Long id) { this.id = id }
-}
-
 @Entity class Person01 { Long id; Status01 status }
 @Entity class Person02 {
     Long id; Status01 status
@@ -200,16 +126,31 @@ enum Status02 {
 }
 @Entity class Person03 {
     Long id; Status01 status
-    static mapping = { status enumType: "ordinal", nullable: true; 
tablePerHierarchy false }
+    static mapping = { status enumType: "ordinal", nullable: true }
 }
 @Entity class Person04 {
-    Long id; Status02 status
-    static mapping = { status enumType: 'identity' }
+    Long id; Status01 status
+    static mapping = { status enumType: "identity" }
 }
 @Entity class Person05 {
-    Long id; Status02 status
+    Long id; Status01 status
     static mapping = { status type: UserTypeEnumType }
 }
+@Entity class PersonWithCollection {
+    Long id
+    Set<Status01> statuses
+}
 @Entity class Clown01 extends Person01 { String clownName }
-@Entity class Clown02 extends Person02 { String clownName }
-@Entity class Clown03 extends Person03 { String clownName }
\ No newline at end of file
+
+class UserTypeEnumType implements UserType {
+    @Override int getSqlType() { 0 }
+    @Override Class returnedClass() { Status01 }
+    @Override boolean equals(Object x, Object y) { x == y }
+    @Override int hashCode(Object x) { x.hashCode() }
+    @Override Object nullSafeGet(ResultSet rs, int position, 
SharedSessionContractImplementor session, Object owner) throws SQLException { 
null }
+    @Override void nullSafeSet(PreparedStatement st, Object value, int index, 
SharedSessionContractImplementor session) throws SQLException {}
+    @Override Object deepCopy(Object value) { value }
+    @Override boolean isMutable() { false }
+    @Override Serializable disassemble(Object value) { (Serializable)value }
+    @Override Object assemble(Serializable cached, Object owner) { cached }
+}
\ No newline at end of file
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/BasicCollectionElementBinderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/BasicCollectionElementBinderSpec.groovy
index f06343e511..2cace08b8c 100644
--- 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/BasicCollectionElementBinderSpec.groovy
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/BasicCollectionElementBinderSpec.groovy
@@ -78,7 +78,7 @@ class BasicCollectionElementBinderSpec extends 
HibernateGormDatastoreSpec {
         then:
         element != null
         // Corrected: Match the 3-argument signature (Property, Class, String)
-        1 * enumTypeBinder.bindEnumTypeForColumn(property, BCEBStatus, _ as 
String) >> mockValue
+        1 * enumTypeBinder.bindEnumTypeForColumn(property, _ as String) >> 
mockValue
     }
 }
 
diff --git 
a/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/model/MappingFactory.java
 
b/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/model/MappingFactory.java
index 4e1f914a4a..3faf9a4482 100644
--- 
a/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/model/MappingFactory.java
+++ 
b/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/model/MappingFactory.java
@@ -153,12 +153,7 @@ public abstract class MappingFactory<R extends Entity, T 
extends Property> {
     private Map<Class, Collection<CustomTypeMarshaller>> typeConverterMap = 
new ConcurrentHashMap<>();
 
     public void registerCustomType(CustomTypeMarshaller marshallerCustom) {
-        Collection<CustomTypeMarshaller> marshallers = 
typeConverterMap.get(marshallerCustom.getTargetType());
-        if (marshallers == null) {
-            marshallers = new ConcurrentLinkedQueue<>();
-            typeConverterMap.put(marshallerCustom.getTargetType(), 
marshallers);
-        }
-        marshallers.add(marshallerCustom);
+        typeConverterMap.computeIfAbsent(marshallerCustom.getTargetType(), k 
-> new ConcurrentLinkedQueue<>()).add(marshallerCustom);
     }
 
     public boolean isSimpleType(Class propType) {

Reply via email to