You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by bo...@apache.org on 2025/09/08 03:09:07 UTC
(grails-core) 07/08: Created HibernateLegacyEnumType as a copy of the deprecated Hibernate EnumType
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 80dc4f4bd559b6cdb5eefe2638bd0eb0115c411a
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Sun Sep 7 21:53:14 2025 -0500
Created HibernateLegacyEnumType as a copy of the deprecated Hibernate EnumType
---
.../orm/hibernate/HibernateLegacyEnumType.java | 345 +++++++++++++++++++++
.../orm/hibernate/cfg/GrailsDomainBinder.java | 2 +-
.../cfg/domainbinding/EnumTypeBinder.java | 12 +-
.../cfg/domainbinding/EnumTypeBinderSpec.groovy | 12 +-
4 files changed, 358 insertions(+), 13 deletions(-)
diff --git a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/HibernateLegacyEnumType.java b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/HibernateLegacyEnumType.java
new file mode 100644
index 0000000000..f5554bc4d2
--- /dev/null
+++ b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/HibernateLegacyEnumType.java
@@ -0,0 +1,345 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
+ * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
+ */
+package org.grails.orm.hibernate;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.MapKeyEnumerated;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.annotations.Nationalized;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.spi.SessionFactoryImplementor;
+import org.hibernate.engine.spi.SharedSessionContractImplementor;
+import org.hibernate.internal.CoreLogging;
+import org.hibernate.internal.util.ReflectHelper;
+import org.hibernate.type.SqlTypes;
+import org.hibernate.type.descriptor.java.EnumJavaType;
+import org.hibernate.type.descriptor.java.JavaType;
+import org.hibernate.type.descriptor.jdbc.JdbcType;
+import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
+import org.hibernate.type.spi.TypeConfiguration;
+import org.hibernate.type.spi.TypeConfigurationAware;
+import org.hibernate.usertype.DynamicParameterizedType;
+import org.hibernate.usertype.EnhancedUserType;
+import org.hibernate.usertype.LoggableUserType;
+
+import org.jboss.logging.Logger;
+
+import static jakarta.persistence.EnumType.ORDINAL;
+import static jakarta.persistence.EnumType.STRING;
+import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean;
+
+/**
+ * Value type mapper for enumerations.
+ *
+ * @author Emmanuel Bernard
+ * @author Hardy Ferentschik
+ * @author Steve Ebersole
+ *
+ */
+public class HibernateLegacyEnumType<T extends Enum<T>>
+ implements EnhancedUserType<T>, DynamicParameterizedType, LoggableUserType, TypeConfigurationAware, Serializable {
+ private static final Logger LOG = CoreLogging.logger( EnumType.class );
+
+ public static final String ENUM = "enumClass";
+ public static final String NAMED = "useNamed";
+ public static final String TYPE = "type";
+
+ private Class<T> enumClass;
+
+ private boolean isOrdinal;
+ private JdbcType jdbcType;
+ private EnumJavaType<T> enumJavaType;
+
+ private TypeConfiguration typeConfiguration;
+
+ public HibernateLegacyEnumType() {
+ }
+
+ public Class<T> getEnumClass() {
+ return enumClass;
+ }
+
+ @Override
+ public JdbcType getJdbcType(TypeConfiguration typeConfiguration) {
+ return jdbcType;
+ }
+
+ /**
+ * <p>
+ * An instance of this class is "configured" by a call to {@link #setParameterValues},
+ * where configuration parameters are given as entries in a {@link Properties} object.
+ * There are two distinct ways an instance may be configured:
+ * <ul>
+ * <li>one for {@code hbm.xml}-based mapping, and
+ * <li>another for annotation-based or {@code orm.xml}-based mapping.
+ * </ul>
+ * <p>
+ * In the case of annotations or {@code orm.xml}, a {@link ParameterType} is passed to
+ * {@link #setParameterValues} under the key {@value #PARAMETER_TYPE}.
+ * <p>
+ * But in the case of {@code hbm.xml}, there are multiple parameters:
+ * <ul>
+ * <li>
+ * {@value #ENUM}, the name of the Java enumeration class.
+ * </li>
+ * <li>
+ * {@value #NAMED}, specifies if the enum should be mapped by name.
+ * Default is to map as ordinal.
+ * </li>
+ * <li>
+ * {@value #TYPE}, a JDBC type code (legacy alternative to {@value #NAMED}).
+ * </li>
+ * </ul>
+ */
+ @Override
+ public void setParameterValues(Properties parameters) {
+ // IMPL NOTE: we handle 2 distinct cases here:
+ // 1) we are passed a ParameterType instance in the incoming Properties - generally
+ // speaking this indicates the annotation-binding case, and the passed ParameterType
+ // represents information about the attribute and annotation
+ // 2) we are not passed a ParameterType - generally this indicates a hbm.xml binding case.
+ final ParameterType reader = (ParameterType) parameters.get( PARAMETER_TYPE );
+
+ if ( parameters.containsKey( ENUM ) ) {
+ final String enumClassName = (String) parameters.get( ENUM );
+ try {
+ enumClass = (Class<T>) ReflectHelper.classForName( enumClassName, this.getClass() ).asSubclass( Enum.class );
+ }
+ catch ( ClassNotFoundException exception ) {
+ throw new HibernateException("Enum class not found: " + enumClassName, exception);
+ }
+ }
+ else if ( reader != null ) {
+ enumClass = (Class<T>) reader.getReturnedClass().asSubclass( Enum.class );
+ }
+
+ final JavaType<T> descriptor = typeConfiguration.getJavaTypeRegistry().getDescriptor( enumClass );
+ enumJavaType = (EnumJavaType<T>) descriptor;
+
+ if ( parameters.containsKey( TYPE ) ) {
+ int jdbcTypeCode = Integer.parseInt( (String) parameters.get( TYPE ) );
+ jdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( jdbcTypeCode );
+ isOrdinal = jdbcType.isInteger()
+ // Both, ENUM and NAMED_ENUM are treated like ordinal with respect to the ordering
+ || jdbcType.getDefaultSqlTypeCode() == SqlTypes.ENUM
+ || jdbcType.getDefaultSqlTypeCode() == SqlTypes.NAMED_ENUM;
+ }
+ else {
+ final LocalJdbcTypeIndicators indicators;
+ final Long columnLength = reader == null ? null : reader.getColumnLengths()[0];
+ if ( parameters.containsKey (NAMED ) ) {
+ indicators = new LocalJdbcTypeIndicators(
+ // use ORDINAL as default for hbm.xml mappings
+ getBoolean( NAMED, parameters ) ? STRING : ORDINAL,
+ false,
+ columnLength
+ );
+ }
+ else {
+ indicators = new LocalJdbcTypeIndicators(
+ getEnumType( reader ),
+ isNationalized( reader ),
+ columnLength
+ );
+ }
+ jdbcType = descriptor.getRecommendedJdbcType( indicators );
+ isOrdinal = indicators.getEnumeratedType() != STRING;
+ }
+
+ if ( LOG.isDebugEnabled() ) {
+ LOG.debugf(
+ "Using %s-based conversion for Enum %s",
+ isOrdinal() ? "ORDINAL" : "NAMED",
+ enumClass.getName()
+ );
+ }
+ }
+
+ private jakarta.persistence.EnumType getEnumType(ParameterType reader) {
+ if ( reader != null ) {
+ if ( reader.isPrimaryKey() ) {
+ final MapKeyEnumerated enumAnn = getAnnotation( reader.getAnnotationsMethod(), MapKeyEnumerated.class );
+ if ( enumAnn != null ) {
+ return enumAnn.value();
+ }
+ }
+ final Enumerated enumAnn = getAnnotation( reader.getAnnotationsMethod(), Enumerated.class );
+ if ( enumAnn != null ) {
+ return enumAnn.value();
+ }
+ }
+ return ORDINAL;
+ }
+
+ private boolean isNationalized(ParameterType reader) {
+ return typeConfiguration.getCurrentBaseSqlTypeIndicators().isNationalized()
+ || reader!=null && getAnnotation( reader.getAnnotationsMethod(), Nationalized.class ) != null;
+ }
+
+ @SuppressWarnings("unchecked")
+ private <A extends Annotation> A getAnnotation(Annotation[] annotations, Class<A> annotationType) {
+ for ( Annotation annotation : annotations ) {
+ if ( annotationType.isInstance( annotation ) ) {
+ return (A) annotation;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public int getSqlType() {
+ verifyConfigured();
+ return jdbcType.getJdbcTypeCode();
+ }
+
+ @Override
+ public Class<T> returnedClass() {
+ return enumClass;
+ }
+
+ @Override
+ public boolean equals(T x, T y) throws HibernateException {
+ return x == y;
+ }
+
+ @Override
+ public int hashCode(T x) throws HibernateException {
+ return x == null ? 0 : x.hashCode();
+ }
+
+ @Override
+ public T nullSafeGet(ResultSet rs, int position, SharedSessionContractImplementor session, Object owner) throws SQLException {
+ verifyConfigured();
+ return jdbcType.getExtractor( enumJavaType ).extract( rs, position, session );
+ }
+
+ private void verifyConfigured() {
+ if ( enumJavaType == null ) {
+ throw new AssertionFailure("EnumType (" + enumClass.getName() + ") not properly, fully configured");
+ }
+ }
+
+ @Override
+ public void nullSafeSet(PreparedStatement st, T value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
+ verifyConfigured();
+ jdbcType.getBinder( enumJavaType ).bind( st, value, index, session );
+ }
+
+ @Override
+ public T deepCopy(T value) throws HibernateException {
+ return value;
+ }
+
+ @Override
+ public boolean isMutable() {
+ return false;
+ }
+
+ @Override
+ public Serializable disassemble(T value) throws HibernateException {
+ return value;
+ }
+
+ @Override
+ public T assemble(Serializable cached, Object owner) throws HibernateException {
+ return (T) cached;
+ }
+
+ @Override
+ public T replace(T original, T target, Object owner) throws HibernateException {
+ return original;
+ }
+
+ @Override
+ public TypeConfiguration getTypeConfiguration() {
+ return typeConfiguration;
+ }
+
+ @Override
+ public void setTypeConfiguration(TypeConfiguration typeConfiguration) {
+ this.typeConfiguration = typeConfiguration;
+ }
+
+ @Override
+ public String toSqlLiteral(T value) {
+ verifyConfigured();
+ return isOrdinal()
+ ? Integer.toString( value.ordinal() )
+ : "'" + value.name() + "'";
+ }
+
+ @Override
+ public String toString(T value) {
+ verifyConfigured();
+ return enumJavaType.toName( value );
+ }
+
+ @Override
+ public T fromStringValue(CharSequence sequence) {
+ verifyConfigured();
+ return enumJavaType.fromName( sequence.toString() );
+ }
+
+ @Override @SuppressWarnings("unchecked")
+ public String toLoggableString(Object value, SessionFactoryImplementor factory) {
+ verifyConfigured();
+ return enumJavaType.extractLoggableRepresentation( (T) value );
+ }
+
+ public boolean isOrdinal() {
+ verifyConfigured();
+ return isOrdinal;
+ }
+
+ private class LocalJdbcTypeIndicators implements JdbcTypeIndicators {
+ private final jakarta.persistence.EnumType enumType;
+ private final boolean nationalized;
+ private final Long columnLength;
+
+ private LocalJdbcTypeIndicators(jakarta.persistence.EnumType enumType, boolean nationalized, Long columnLength) {
+ this.enumType = enumType;
+ this.nationalized = nationalized;
+ this.columnLength = columnLength;
+ }
+
+ @Override
+ public TypeConfiguration getTypeConfiguration() {
+ return typeConfiguration;
+ }
+
+ @Override
+ public jakarta.persistence.EnumType getEnumeratedType() {
+ return enumType != null ? enumType : typeConfiguration.getCurrentBaseSqlTypeIndicators().getEnumeratedType();
+ }
+
+ @Override
+ public boolean isNationalized() {
+ return nationalized;
+ }
+
+
+ @Override
+ public long getColumnLength() {
+ return columnLength == null ? NO_COLUMN_LENGTH : columnLength;
+ }
+
+ @Override
+ public Dialect getDialect() {
+ return typeConfiguration.getCurrentBaseSqlTypeIndicators().getDialect();
+ }
+ }
+}
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 02f0e183f7..4585678f47 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
@@ -130,7 +130,7 @@ public class GrailsDomainBinder implements MetadataContributor {
public static final String CASCADE_NONE = "none";
private static final String BACKTICK = "`";
- public static final String ENUM_TYPE_CLASS = "org.hibernate.type.EnumType";
+ public static final String ENUM_TYPE_CLASS = org.grails.orm.hibernate.HibernateLegacyEnumType.class.getName();
public static final String ENUM_CLASS_PROP = "enumClass";
private static final String ENUM_TYPE_PROP = "type";
public static final String DEFAULT_ENUM_TYPE = "default";
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
index 9b98e8c915..6a2de0ee0e 100644
--- 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
@@ -10,7 +10,7 @@ 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.grails.orm.hibernate.HibernateLegacyEnumType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -51,13 +51,13 @@ public class EnumTypeBinder {
} else {
if (GrailsEnumType.DEFAULT.getType().equals(enumType) || GrailsEnumType.STRING.getType().equalsIgnoreCase(enumType)) {
simpleValue.setTypeName(ENUM_TYPE_CLASS);
- enumProperties.put(EnumType.TYPE, String.valueOf(Types.VARCHAR));
- enumProperties.put(EnumType.NAMED, Boolean.TRUE.toString());
+ enumProperties.put(HibernateLegacyEnumType.TYPE, String.valueOf(Types.VARCHAR));
+ enumProperties.put(HibernateLegacyEnumType.NAMED, Boolean.TRUE.toString());
} else if (GrailsEnumType.ORDINAL.getType().equalsIgnoreCase(enumType)) {
simpleValue.setTypeName(ENUM_TYPE_CLASS);
- enumProperties.put(EnumType.TYPE, String.valueOf(Types.INTEGER));
- enumProperties.put(EnumType.NAMED, Boolean.FALSE.toString());
- } else if (GrailsEnumType.ORDINAL.getType().equals(enumType)) {
+ enumProperties.put(HibernateLegacyEnumType.TYPE, String.valueOf(Types.INTEGER));
+ enumProperties.put(HibernateLegacyEnumType.NAMED, Boolean.FALSE.toString());
+ } else if (GrailsEnumType.IDENTITY.getType().equals(enumType)) {
simpleValue.setTypeName(IdentityEnumType.class.getName());
} else {
throw new MappingException("Invalid enum type [" + enumType + "].");
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
index 3a5d305dbc..be7041668e 100644
--- 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
@@ -9,7 +9,7 @@ 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.grails.orm.hibernate.HibernateLegacyEnumType
import org.hibernate.usertype.UserType
import spock.lang.Subject
import spock.lang.Unroll
@@ -50,14 +50,14 @@ class EnumTypeBinderSpec extends HibernateGormDatastoreSpec {
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
+ (props.getProperty(HibernateLegacyEnumType.TYPE) == String.valueOf(expectedSqlType)) == typeExpected
+ (props.getProperty(HibernateLegacyEnumType.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
+ Person01| "default" | HibernateLegacyEnumType.class.getName() | Types.VARCHAR | true | true | true | false
+ Person02|"string" | HibernateLegacyEnumType.class.getName() | Types.VARCHAR | true | true | true | true
+ Person03|"ordinal" | HibernateLegacyEnumType.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
}