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 290bfad3dd38961193f28ab1e6fa6d754009c67d
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Sun Jul 20 18:08:49 2025 -0500

    EnumTypeBinder and other classes
---
 .../hibernate/cfg/AbstractGrailsDomainBinder.java  |   1 +
 .../orm/hibernate/cfg/GrailsDomainBinder.java      |  42 ++--
 .../hibernate/cfg/HibernatePersistentEntity.java   |   6 +
 .../domainbinding/ColumnConfigToColumnBinder.java  |   7 +-
 .../cfg/domainbinding/EnumTypeBinder.java          |  98 ++++++++
 .../hibernate/cfg/domainbinding/IndexBinder.java   |   8 +-
 .../PersistentPropertyToPropertyConfig.java        |   3 +-
 .../cfg/domainbinding/TypeNameProvider.java        |   5 +-
 .../gorm/specs/HibernateGormDatastoreSpec.groovy   |   4 +
 .../ColumnConfigToColumnBinderSpec.groovy          |   8 -
 .../cfg/domainbinding/EnumTypeBinderSpec.groovy    | 249 +++++++++++++++++++++
 .../cfg/domainbinding/IndexBinderSpec.groovy       |   7 -
 .../datastore/mapping/model/PersistentEntity.java  |   6 +-
 13 files changed, 400 insertions(+), 44 deletions(-)

diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/AbstractGrailsDomainBinder.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/AbstractGrailsDomainBinder.java
index 264fe90497..2fea30c3a3 100644
--- 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/AbstractGrailsDomainBinder.java
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/AbstractGrailsDomainBinder.java
@@ -61,6 +61,7 @@ public abstract class AbstractGrailsDomainBinder {
         return domainClass == null ? null : 
MAPPING_CACHE.get(domainClass.getJavaClass());
     }
 
+
     public static void clearMappingCache() {
         MAPPING_CACHE.clear();
     }
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 aef1092b71..9038bfc537 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
@@ -299,8 +299,8 @@ public class GrailsDomainBinder implements 
MetadataContributor {
             SimpleValue elt = new BasicValue(metadataBuildingContext, 
map.getCollectionTable());
             map.setElement(elt);
 
-            PersistentEntity owner = property.getOwner();
-            Mapping mapping = getMapping(owner);
+            HibernatePersistentEntity owner = (HibernatePersistentEntity) 
property.getOwner();
+            Mapping mapping =owner.getMappedForm();
             String typeName = new TypeNameProvider().getTypeName(property, 
mapping);
             if (typeName == null ) {
 
@@ -704,15 +704,12 @@ public class GrailsDomainBinder implements 
MetadataContributor {
     }
 
     private Mapping getRootMapping(PersistentEntity referenced) {
-        if (referenced == null) return null;
-        Class<?> current = referenced.getJavaClass();
-        while (true) {
-            Class<?> superClass = current.getSuperclass();
-            if (Object.class.equals(superClass)) break;
-            current = superClass;
-        }
-
-        return getMapping(current);
+        return Optional.of(referenced)
+                .map(PersistentEntity::getRootEntity)
+                .filter(HibernatePersistentEntity.class::isInstance)
+                .map(HibernatePersistentEntity.class::cast)
+                .map(HibernatePersistentEntity::getMappedForm)
+                .orElse(null);
     }
 
     private boolean isBidirectionalOneToManyMap(Association property) {
@@ -740,7 +737,7 @@ public class GrailsDomainBinder implements 
MetadataContributor {
 
         var joinColumnMappingOptional = 
Optional.ofNullable(config).map(PropertyConfig::getJoinTableColumnConfig);
         if (isBasicCollectionType) {
-            final Class<?> referencedType = 
((Basic)property).getComponentType();
+            final Class<?> referencedType = property.getType();
             String className = referencedType.getName();
             final boolean isEnum = referencedType.isEnum();
             if (joinColumnMappingOptional.isPresent()) {
@@ -1360,7 +1357,7 @@ public class GrailsDomainBinder implements 
MetadataContributor {
             root.setPolymorphic(false);
         } else {
             root.setPolymorphic(true);
-            Mapping m = getMapping(entity);
+            Mapping m =entity.getMappedForm();
             boolean tablePerSubclass = m != null && !m.getTablePerHierarchy();
             if (!tablePerSubclass) {
                 // if the root class has children create a discriminator 
property
@@ -1689,7 +1686,7 @@ public class GrailsDomainBinder implements 
MetadataContributor {
                                                        RootClass root, 
InFlightMetadataCollector mappings, String sessionFactoryBeanName) {
 
         // get the schema and catalog names from the configuration
-        Mapping m = 
ofNullable(getMapping(domainClass.getJavaClass())).orElseThrow();
+        Mapping m = domainClass.getMappedForm();
 
         configureDerivedProperties(domainClass, m);
         CacheConfig cc = m.getCache();
@@ -1812,7 +1809,7 @@ public class GrailsDomainBinder implements 
MetadataContributor {
         final List<PersistentProperty> persistentProperties = 
domainClass.getPersistentProperties();
         Table table = persistentClass.getTable();
 
-        Mapping gormMapping = domainClass.getMapping().getMappedForm();
+        Mapping gormMapping = domainClass.getMappedForm();
 
         if (gormMapping != null) {
             table.setComment(gormMapping.getComment());
@@ -2149,8 +2146,8 @@ public class GrailsDomainBinder implements 
MetadataContributor {
 
     private boolean isComponentPropertyNullable(PersistentProperty 
componentProperty) {
         if (componentProperty == null) return false;
-        final PersistentEntity domainClass = componentProperty.getOwner();
-        final Mapping mapping = getMapping(domainClass.getJavaClass());
+        final HibernatePersistentEntity domainClass = 
(HibernatePersistentEntity) componentProperty.getOwner();
+        final Mapping mapping = domainClass.getMappedForm();
         return !domainClass.isRoot() && (mapping == null || 
mapping.isTablePerHierarchy()) || componentProperty.isNullable();
     }
 
@@ -2189,7 +2186,7 @@ public class GrailsDomainBinder implements 
MetadataContributor {
 
         bindManyToOneValues(property, manyToOne);
         PersistentEntity refDomainClass = property instanceof ManyToMany ? 
property.getOwner() : property.getAssociatedEntity();
-        Mapping mapping = getMapping(refDomainClass);
+        Mapping mapping = 
((HibernatePersistentEntity)refDomainClass).getMappedForm();
         boolean isComposite = hasCompositeIdentifier(mapping);
         if (isComposite) {
             CompositeIdentity ci = (CompositeIdentity) mapping.getIdentity();
@@ -2416,7 +2413,7 @@ public class GrailsDomainBinder implements 
MetadataContributor {
     private void bindSimpleId(PersistentProperty identifier, RootClass entity,
                                 InFlightMetadataCollector mappings, Identity 
mappedId, String sessionFactoryBeanName) {
 
-        Mapping mapping = getMapping(identifier.getOwner());
+        Mapping mapping = 
((HibernatePersistentEntity)identifier.getOwner()).getMappedForm();
         boolean useSequence = mapping != null && 
mapping.isTablePerConcreteClass();
 
         // create the id value
@@ -2620,7 +2617,7 @@ public class GrailsDomainBinder implements 
MetadataContributor {
                 if (referenced != null && 
referenced.isOwningEntity(domainClass) && 
!isCircularAssociation(grailsProperty)) {
                     cascadeStrategy = CASCADE_ALL;
                 }
-                else if(isCompositeIdProperty((Mapping) 
domainClass.getMapping().getMappedForm(), grailsProperty)) {
+                else if(isCompositeIdProperty(getMapping(domainClass), 
grailsProperty)) {
                     cascadeStrategy = CASCADE_ALL;
                 }
                 else {
@@ -2886,7 +2883,7 @@ public class GrailsDomainBinder implements 
MetadataContributor {
         String columnName = null;
         if (cc == null) {
             // No column config given, so try to fetch it from the mapping
-            PersistentEntity domainClass = grailsProp.getOwner();
+            PersistentEntity domainClass =  grailsProp.getOwner();
             Mapping m = getMapping(domainClass);
             if (m != null) {
                 PropertyConfig c = m.getPropertyConfig(grailsProp.getName());
@@ -2988,8 +2985,7 @@ public class GrailsDomainBinder implements 
MetadataContributor {
     private String getIndexColumnType(PersistentProperty property, String 
defaultType) {
         PropertyConfig pc = new 
PersistentPropertyToPropertyConfig().apply(property);
         if (pc != null && pc.getIndexColumn() != null && 
pc.getIndexColumn().getType() != null) {
-            PropertyConfig config = pc.getIndexColumn();
-            Mapping mapping = getMapping(property.getOwner());
+            Mapping mapping = 
((HibernatePersistentEntity)property.getOwner()).getMappedForm();
             return new TypeNameProvider().getTypeName(property, mapping);
         }
         return defaultType;
diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernatePersistentEntity.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernatePersistentEntity.java
index b23dd23a99..29b07d9527 100644
--- 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernatePersistentEntity.java
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernatePersistentEntity.java
@@ -17,6 +17,8 @@ package org.grails.orm.hibernate.cfg;
 
 import org.grails.datastore.mapping.model.*;
 
+import java.util.Optional;
+
 /**
  * Persistent entity implementation for Hibernate
  *
@@ -57,4 +59,8 @@ public class HibernatePersistentEntity extends 
AbstractPersistentEntity<Mapping>
     public ClassMapping<Mapping> getMapping() {
         return this.classMapping;
     }
+
+    public Mapping getMappedForm() {
+        return 
Optional.ofNullable(getMapping()).map(ClassMapping::getMappedForm).orElse(null);
+    }
 }
diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ColumnConfigToColumnBinder.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ColumnConfigToColumnBinder.java
index 795b31d740..bbeb4aa75a 100644
--- 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ColumnConfigToColumnBinder.java
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ColumnConfigToColumnBinder.java
@@ -1,12 +1,17 @@
 package org.grails.orm.hibernate.cfg.domainbinding;
 
+import jakarta.annotation.Nonnull;
 import org.grails.orm.hibernate.cfg.ColumnConfig;
 import org.grails.orm.hibernate.cfg.PropertyConfig;
 import org.hibernate.mapping.Column;
 
 public class ColumnConfigToColumnBinder {
 
-    public void bindColumnConfigToColumn(Column column, ColumnConfig 
columnConfig, PropertyConfig mappedForm) {
+    public void bindColumnConfigToColumn(
+             Column column,
+             ColumnConfig columnConfig,
+            PropertyConfig mappedForm
+        ) {
         if (columnConfig == null) {
             return;
         }
diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/EnumTypeBinder.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/EnumTypeBinder.java
new file mode 100644
index 0000000000..d0357a756d
--- /dev/null
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/EnumTypeBinder.java
@@ -0,0 +1,98 @@
+package org.grails.orm.hibernate.cfg.domainbinding;
+
+import org.grails.datastore.mapping.model.PersistentProperty;
+import org.grails.orm.hibernate.cfg.ColumnConfig;
+import org.grails.orm.hibernate.cfg.HibernatePersistentEntity;
+import org.grails.orm.hibernate.cfg.IdentityEnumType;
+import org.grails.orm.hibernate.cfg.Mapping;
+import org.grails.orm.hibernate.cfg.PropertyConfig;
+import org.hibernate.MappingException;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.SimpleValue;
+import org.hibernate.mapping.Table;
+import org.hibernate.type.EnumType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.sql.Types;
+import java.util.Properties;
+
+import static 
org.grails.orm.hibernate.cfg.GrailsDomainBinder.DEFAULT_ENUM_TYPE;
+import static org.grails.orm.hibernate.cfg.GrailsDomainBinder.ENUM_CLASS_PROP;
+import static org.grails.orm.hibernate.cfg.GrailsDomainBinder.ENUM_TYPE_CLASS;
+
+
+public class EnumTypeBinder {
+
+    private final IndexBinder indexBinder;
+    private ColumnConfigToColumnBinder columnConfigToColumnBinder;
+
+    public EnumTypeBinder() {
+        this(new IndexBinder(), new ColumnConfigToColumnBinder());
+    }
+
+    protected EnumTypeBinder(IndexBinder indexBinder, 
ColumnConfigToColumnBinder columnConfigToColumnBinder) {
+        this.indexBinder = indexBinder;
+        this.columnConfigToColumnBinder = columnConfigToColumnBinder;
+    }
+
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(EnumTypeBinder.class);
+
+    public void bindEnumType(PersistentProperty property, Class<?> 
propertyType, SimpleValue simpleValue, String columnName) {
+        // Create config and mapping objects ONCE and reuse them.
+        PropertyConfig pc = new 
PersistentPropertyToPropertyConfig().apply(property);
+        final HibernatePersistentEntity owner = (HibernatePersistentEntity) 
property.getOwner();
+        Mapping ownerMapping = owner.getMappedForm();
+        String enumType = pc.getEnumType();
+        Properties enumProperties = new Properties();
+        enumProperties.put(ENUM_CLASS_PROP, propertyType.getName());
+        String typeName = new TypeNameProvider().getTypeName(property, 
ownerMapping);
+        if (typeName != null) {
+            simpleValue.setTypeName(typeName);
+        } else {
+            if (DEFAULT_ENUM_TYPE.equals(enumType) || 
"string".equalsIgnoreCase(enumType)) {
+                simpleValue.setTypeName(ENUM_TYPE_CLASS);
+                enumProperties.put(EnumType.TYPE, 
String.valueOf(Types.VARCHAR));
+                enumProperties.put(EnumType.NAMED, Boolean.TRUE.toString());
+            } else if ("ordinal".equalsIgnoreCase(enumType)) {
+                simpleValue.setTypeName(ENUM_TYPE_CLASS);
+                enumProperties.put(EnumType.TYPE, 
String.valueOf(Types.INTEGER));
+                enumProperties.put(EnumType.NAMED, Boolean.FALSE.toString());
+            } else if ("identity".equals(enumType)) {
+                simpleValue.setTypeName(IdentityEnumType.class.getName());
+            } else {
+                throw new MappingException("Invalid enum type [" + enumType + 
"].");
+            }
+
+        }
+        simpleValue.setTypeParameters(enumProperties);
+
+        Column column = new Column();
+        boolean isTablePerHierarchySubclass = !owner.isRoot() && (ownerMapping 
== null || ownerMapping.getTablePerHierarchy());
+        if (isTablePerHierarchySubclass) {
+            // Properties on subclasses in a table-per-hierarchy strategy must 
be nullable.
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("[GrailsDomainBinder] Sub class property [{}] for 
column name [{}] forced to nullable",
+                        property.getName(), columnName);
+            }
+            column.setNullable(true);
+        }
+        else {
+            column.setNullable(property.isNullable());
+        }
+
+        column.setValue(simpleValue);
+        column.setName(columnName);
+        Table t = simpleValue.getTable();
+        t.addColumn(column);
+        simpleValue.addColumn(column);
+
+        if (!pc.getColumns().isEmpty()) {
+            ColumnConfig columnConfig = pc.getColumns().get(0);
+            indexBinder.bindIndex(columnName, column, columnConfig, t);
+            columnConfigToColumnBinder.bindColumnConfigToColumn(column, 
columnConfig, pc);
+        }
+    }
+
+}
\ No newline at end of file
diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/IndexBinder.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/IndexBinder.java
index da654bf363..52261107c9 100644
--- 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/IndexBinder.java
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/IndexBinder.java
@@ -1,5 +1,6 @@
 package org.grails.orm.hibernate.cfg.domainbinding;
 
+import jakarta.annotation.Nonnull;
 import org.grails.orm.hibernate.cfg.ColumnConfig;
 import org.hibernate.mapping.Column;
 import org.hibernate.mapping.Table;
@@ -7,7 +8,12 @@ import static java.lang.String.format;
 import static java.util.Optional.*;
 
 public class IndexBinder {
-    public void bindIndex(String columnName, Column column, ColumnConfig cc, 
Table table) {
+    public void bindIndex(
+           @Nonnull String columnName,
+           @Nonnull Column column,
+           ColumnConfig cc,
+           @Nonnull Table table
+    ) {
        ofNullable(cc)
                 .map(ColumnConfig::getIndex)
                 .flatMap(indexObj -> {
diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/PersistentPropertyToPropertyConfig.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/PersistentPropertyToPropertyConfig.java
index 810f6752ad..53be315bf3 100644
--- 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/PersistentPropertyToPropertyConfig.java
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/PersistentPropertyToPropertyConfig.java
@@ -2,6 +2,7 @@ package org.grails.orm.hibernate.cfg.domainbinding;
 
 import org.grails.datastore.mapping.model.PersistentProperty;
 import org.grails.orm.hibernate.cfg.PropertyConfig;
+import org.hibernate.MappingException;
 
 import java.util.Optional;
 import java.util.function.Function;
@@ -12,6 +13,6 @@ public class PersistentPropertyToPropertyConfig implements 
Function<PersistentPr
         return Optional.ofNullable(persistentProperty)
                 .map(PersistentProperty::getMappedForm)
                 .map(PropertyConfig.class::cast)
-                .orElse(null);
+                .orElseThrow(() -> new MappingException("No PropertyConfig 
found for property"));
     }
 }
diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/TypeNameProvider.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/TypeNameProvider.java
index 39ba0d930f..4ff3ce94da 100644
--- 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/TypeNameProvider.java
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/TypeNameProvider.java
@@ -18,7 +18,10 @@ public class TypeNameProvider {
                 .map(typeObj -> typeObj instanceof Class<?> clazz ?
                         clazz.getName() : typeObj.toString()
                 )
-                .orElse(mapping.getTypeName(property.getType()));
+                .orElseGet(() -> property != null && mapping != null
+                        ? mapping.getTypeName(property.getType()) : null
+                );
+
     }
 
 
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 cb0697bf1f..0ccfeaa366 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
@@ -63,6 +63,10 @@ class HibernateGormDatastoreSpec extends 
GrailsDataTckSpec<GrailsDataHibernate6T
     """
 
         def clazz = classLoader.parseClass(classText)
+        createPersistentEntity(clazz, binder)
+    }
+
+    public HibernatePersistentEntity createPersistentEntity(Class clazz, 
GrailsDomainBinder binder) {
         def entity = getMappingContext().addPersistentEntity(clazz) as 
HibernatePersistentEntity
         binder.evaluateMapping(entity)
         entity
diff --git 
a/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ColumnConfigToColumnBinderSpec.groovy
 
b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ColumnConfigToColumnBinderSpec.groovy
index 84c12f7390..0df0939a41 100644
--- 
a/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ColumnConfigToColumnBinderSpec.groovy
+++ 
b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ColumnConfigToColumnBinderSpec.groovy
@@ -10,14 +10,6 @@ class ColumnConfigToColumnBinderSpec extends Specification {
     def binder = new ColumnConfigToColumnBinder()
     def column = Mock(Column)
 
-    def "should handle null column config"() {
-        when:
-        binder.bindColumnConfigToColumn(column, null, null)
-
-        then:
-        0 * column._  // verifies no interactions with column
-    }
-
     def "should bind column properties when values are valid"() {
         given:
         def columnConfig = new ColumnConfig()
diff --git 
a/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/EnumTypeBinderSpec.groovy
 
b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/EnumTypeBinderSpec.groovy
new file mode 100644
index 0000000000..3a5d305dbc
--- /dev/null
+++ 
b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/EnumTypeBinderSpec.groovy
@@ -0,0 +1,249 @@
+package org.grails.orm.hibernate.cfg.domainbinding
+
+import grails.gorm.specs.HibernateGormDatastoreSpec
+import grails.persistence.Entity
+import org.grails.datastore.mapping.model.PersistentProperty
+import org.grails.orm.hibernate.cfg.IdentityEnumType
+import org.hibernate.engine.spi.SharedSessionContractImplementor
+import org.hibernate.mapping.BasicValue
+import org.hibernate.mapping.Column
+import org.hibernate.mapping.Selectable
+import org.hibernate.mapping.Table
+import org.hibernate.type.EnumType
+import org.hibernate.usertype.UserType
+import spock.lang.Subject
+import spock.lang.Unroll
+
+import java.sql.PreparedStatement
+import java.sql.ResultSet
+import java.sql.SQLException
+import java.sql.Types
+
+class EnumTypeBinderSpec extends HibernateGormDatastoreSpec {
+
+
+
+
+    def indexBinder = Mock(IndexBinder)
+    def columnBinder = Mock(ColumnConfigToColumnBinder)
+
+    @Subject
+    EnumTypeBinder binder = new EnumTypeBinder(indexBinder, columnBinder)
+
+
+
+    @Unroll
+    def "should bind enum type as #expectedHibernateType when mapping 
specifies enumType as '#enumTypeMapping'"() {
+        given: "A root entity and its enum property"
+        def grailsDomainBinder = getGrailsDomainBinder()
+        def owner = createPersistentEntity(clazz, grailsDomainBinder)
+        PersistentProperty property = owner.getPropertyByName("status")
+        def table = new Table("person")
+        def simpleValue = new 
BasicValue(grailsDomainBinder.metadataBuildingContext, table)
+
+        when: "the enum is bound"
+        binder.bindEnumType(property, Status01, simpleValue, "status_col")
+
+        then: "the correct hibernate type is set"
+        simpleValue.getTypeName() == expectedHibernateType
+        simpleValue.isNullable() == nullable
+
+        and: "the type parameters are configured correctly"
+        def props = simpleValue.getTypeParameters()
+        (props.getProperty(EnumType.TYPE) == String.valueOf(expectedSqlType)) 
== typeExpected
+        (props.getProperty(EnumType.NAMED) == String.valueOf(namedExpected)) 
== namedIsExpected
+
+        where:
+        clazz | enumTypeMapping   | expectedHibernateType            | 
expectedSqlType   | typeExpected | namedExpected | namedIsExpected | nullable
+        Person01| "default"       | EnumType.class.getName()         | 
Types.VARCHAR     | true         | true          | true            | false
+        Person02|"string"         | EnumType.class.getName()         | 
Types.VARCHAR     | true         | true          | true            | true
+        Person03|"ordinal"        | EnumType.class.getName()         | 
Types.INTEGER     | true         | false         | true            | true
+        Person04|"identity"       | IdentityEnumType.class.getName() | null    
          | false        | null          | false           | false
+        Person05|UserTypeEnumType | UserTypeEnumType.class.getName() | null    
          | false        | null          | false           | false
+    }
+
+
+
+
+    @Unroll
+    def "should set column nullability "() {
+        given: "A root entity and its enum property"
+        def grailsDomainBinder = getGrailsDomainBinder()
+        def owner = createPersistentEntity( clazz, grailsDomainBinder)
+        PersistentProperty property = owner.getPropertyByName("status")
+        def table = new Table("person")
+        def simpleValue = new 
BasicValue(grailsDomainBinder.metadataBuildingContext, table)
+        def columnName = "status_col"
+        when: "the enum is bound"
+        binder.bindEnumType(property, Status01, simpleValue, columnName)
+
+        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
+
+        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"
+        // This test assumes 'indexBinder' and 'columnBinder' are mocked 
collaborators
+        // injected via a setup() block, as they are created inside the binder.
+        def grailsDomainBinder = getGrailsDomainBinder()
+        def owner = createPersistentEntity(clazz, grailsDomainBinder)
+        PersistentProperty property = owner.getPropertyByName("status")
+        def table = new Table("person")
+        def simpleValue = new 
BasicValue(grailsDomainBinder.metadataBuildingContext, table)
+        def columnName = "status_col"
+
+        when: "the enum is bound"
+        binder.bindEnumType(property, Status01, simpleValue, 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
+    }
+
+
+
+}
+
+class UserTypeEnumType implements UserType {
+
+    @Override
+    int getSqlType() {
+        return 0
+    }
+
+    @Override
+    Class returnedClass() {
+        return null
+    }
+
+    @Override
+    boolean equals(Object x, Object y) {
+        return false
+    }
+
+    @Override
+    int hashCode(Object x) {
+        return 0
+    }
+
+    @Override
+    Object nullSafeGet(ResultSet rs, int position, 
SharedSessionContractImplementor session, @Deprecated Object owner) throws 
SQLException {
+        return null
+    }
+
+    @Override
+    void nullSafeSet(PreparedStatement st, Object value, int index, 
SharedSessionContractImplementor session) throws SQLException {
+
+    }
+
+    @Override
+    Object deepCopy(Object value) {
+        return null
+    }
+
+    @Override
+    boolean isMutable() {
+        return false
+    }
+
+    @Override
+    Serializable disassemble(Object value) {
+        return null
+    }
+
+    @Override
+    Object assemble(Serializable cached, Object owner) {
+        return 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
+    static mapping = {
+        status enumType: "string", nullable :true
+
+    }
+}
+
+@Entity
+class Person03 {
+    Long id
+    Status01 status
+    static mapping = {
+        status enumType: "ordinal", nullable :true
+        tablePerHierarchy false
+
+    }
+}
+
+@Entity
+class Person04 {
+    Long id
+    Status02 status
+    static mapping = {
+        status enumType: 'identity'
+    }
+}
+
+@Entity
+class Person05 {
+    Long id
+    Status02 status
+    static mapping = {
+        status type: UserTypeEnumType
+    }
+}
+
+@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
diff --git 
a/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/IndexBinderSpec.groovy
 
b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/IndexBinderSpec.groovy
index 6ba548370b..fe16ebb41c 100644
--- 
a/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/IndexBinderSpec.groovy
+++ 
b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/IndexBinderSpec.groovy
@@ -13,13 +13,6 @@ class IndexBinderSpec extends Specification {
     def column = Mock(Column)
     def index = Mock(Index)
 
-    def "should not create index when ColumnConfig is null"() {
-        when:
-        indexBinder.bindIndex("colName", column, null, table)
-
-        then:
-        0 * table._  // verifies no interactions with table
-    }
 
     def "should create default index when index is true"() {
         given:
diff --git 
a/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/model/PersistentEntity.java
 
b/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/model/PersistentEntity.java
index 293142ce88..7897702b39 100644
--- 
a/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/model/PersistentEntity.java
+++ 
b/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/model/PersistentEntity.java
@@ -18,14 +18,14 @@
  */
 package org.grails.datastore.mapping.model;
 
-import java.util.List;
-
 import org.grails.datastore.mapping.model.lifecycle.Initializable;
 import org.grails.datastore.mapping.model.types.Association;
 import org.grails.datastore.mapping.model.types.Embedded;
 import org.grails.datastore.mapping.model.types.TenantId;
 import org.grails.datastore.mapping.reflect.EntityReflector;
 
+import java.util.List;
+
 /**
  * Represents a persistent entity.
  *
@@ -226,4 +226,6 @@ public interface PersistentEntity extends Initializable {
      * @return True if the operation was successful
      */
     boolean addOwner(Class type);
+
+
 }

Reply via email to