org.hibernate.type.EnumType.java Source code

Java tutorial

Introduction

Here is the source code for org.hibernate.type.EnumType.java

Source

/*
 * 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.hibernate.type;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Locale;
import java.util.Properties;
import javax.persistence.Enumerated;
import javax.persistence.MapKeyEnumerated;

import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
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.internal.util.config.ConfigurationHelper;
import org.hibernate.metamodel.model.convert.internal.NamedEnumValueConverter;
import org.hibernate.metamodel.model.convert.internal.OrdinalEnumValueConverter;
import org.hibernate.metamodel.model.convert.spi.EnumValueConverter;
import org.hibernate.type.descriptor.java.EnumJavaTypeDescriptor;
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;

/**
 * Value type mapper for enumerations.
 *
 * Generally speaking, the proper configuration is picked up from the annotations associated with the mapped attribute.
 *
 * There are a few configuration parameters understood by this type mapper:<ul>
 *     <li>
 *         <strong>enumClass</strong> - Names the enumeration class.
 *     </li>
 *     <li>
 *         <strong>useNamed</strong> - Should enum be mapped via name.  Default is to map as ordinal.  Used when
 *         annotations are not used (otherwise {@link javax.persistence.EnumType} is used).
 *     </li>
 *     <li>
 *         <strong>type</strong> - Identifies the JDBC type (via type code) to be used for the column.
 *     </li>
 * </ul>
 *
 * @author Emmanuel Bernard
 * @author Hardy Ferentschik
 * @author Steve Ebersole
 */
@SuppressWarnings("unchecked")
public class EnumType<T extends Enum> implements EnhancedUserType, 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 enumClass;

    private EnumValueConverter enumValueConverter;

    private TypeConfiguration typeConfiguration;

    @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 (reader != null) {
            enumClass = reader.getReturnedClass().asSubclass(Enum.class);

            final boolean isOrdinal;
            final javax.persistence.EnumType enumType = getEnumType(reader);
            if (enumType == null) {
                isOrdinal = true;
            } else if (javax.persistence.EnumType.ORDINAL.equals(enumType)) {
                isOrdinal = true;
            } else if (javax.persistence.EnumType.STRING.equals(enumType)) {
                isOrdinal = false;
            } else {
                throw new AssertionFailure("Unknown EnumType: " + enumType);
            }

            final EnumJavaTypeDescriptor enumJavaDescriptor = (EnumJavaTypeDescriptor) typeConfiguration
                    .getJavaTypeDescriptorRegistry().getDescriptor(enumClass);

            if (isOrdinal) {
                this.enumValueConverter = new OrdinalEnumValueConverter(enumJavaDescriptor);
            } else {
                this.enumValueConverter = new NamedEnumValueConverter(enumJavaDescriptor);
            }
        } else {
            final String enumClassName = (String) parameters.get(ENUM);
            try {
                enumClass = ReflectHelper.classForName(enumClassName, this.getClass()).asSubclass(Enum.class);
            } catch (ClassNotFoundException exception) {
                throw new HibernateException("Enum class not found: " + enumClassName, exception);
            }

            this.enumValueConverter = interpretParameters(parameters);
        }

        LOG.debugf("Using %s-based conversion for Enum %s", isOrdinal() ? "ORDINAL" : "NAMED", enumClass.getName());
    }

    private javax.persistence.EnumType getEnumType(ParameterType reader) {
        javax.persistence.EnumType enumType = null;
        if (reader.isPrimaryKey()) {
            MapKeyEnumerated enumAnn = getAnnotation(reader.getAnnotationsMethod(), MapKeyEnumerated.class);
            if (enumAnn != null) {
                enumType = enumAnn.value();
            }
        } else {
            Enumerated enumAnn = getAnnotation(reader.getAnnotationsMethod(), Enumerated.class);
            if (enumAnn != null) {
                enumType = enumAnn.value();
            }
        }
        return enumType;
    }

    private <A extends Annotation> A getAnnotation(Annotation[] annotations, Class<A> anClass) {
        for (Annotation annotation : annotations) {
            if (anClass.isInstance(annotation)) {
                return (A) annotation;
            }
        }
        return null;
    }

    private EnumValueConverter interpretParameters(Properties parameters) {
        final EnumJavaTypeDescriptor javaTypeDescriptor = (EnumJavaTypeDescriptor) typeConfiguration
                .getJavaTypeDescriptorRegistry().getDescriptor(enumClass);
        if (parameters.containsKey(NAMED)) {
            final boolean useNamed = ConfigurationHelper.getBoolean(NAMED, parameters);
            if (useNamed) {
                return new NamedEnumValueConverter(javaTypeDescriptor);
            } else {
                return new OrdinalEnumValueConverter(javaTypeDescriptor);
            }
        }

        if (parameters.containsKey(TYPE)) {
            final int type = Integer.decode((String) parameters.get(TYPE));
            if (isNumericType(type)) {
                return new OrdinalEnumValueConverter(javaTypeDescriptor);
            } else if (isCharacterType(type)) {
                return new NamedEnumValueConverter(javaTypeDescriptor);
            } else {
                throw new HibernateException(String.format(Locale.ENGLISH,
                        "Passed JDBC type code [%s] not recognized as numeric nor character", type));
            }
        }

        // the fallback
        return new OrdinalEnumValueConverter(javaTypeDescriptor);
    }

    private boolean isCharacterType(int jdbcTypeCode) {
        switch (jdbcTypeCode) {
        case Types.CHAR:
        case Types.LONGVARCHAR:
        case Types.VARCHAR: {
            return true;
        }
        default: {
            return false;
        }
        }
    }

    private boolean isNumericType(int jdbcTypeCode) {
        switch (jdbcTypeCode) {
        case Types.INTEGER:
        case Types.NUMERIC:
        case Types.SMALLINT:
        case Types.TINYINT:
        case Types.BIGINT:
        case Types.DECIMAL:
        case Types.DOUBLE:
        case Types.FLOAT: {
            return true;
        }
        default:
            return false;
        }
    }

    @Override
    public int[] sqlTypes() {
        verifyConfigured();
        return new int[] { enumValueConverter.getJdbcTypeCode() };
    }

    @Override
    public Class<? extends Enum> returnedClass() {
        return enumClass;
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        return x == y;
    }

    @Override
    public int hashCode(Object x) throws HibernateException {
        return x == null ? 0 : x.hashCode();
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
            throws SQLException {
        verifyConfigured();
        return enumValueConverter.readValue(rs, names[0], session);
    }

    private void verifyConfigured() {
        if (enumValueConverter == null) {
            throw new AssertionFailure("EnumType (" + enumClass.getName() + ") not properly, fully configured");
        }
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
            throws HibernateException, SQLException {
        verifyConfigured();
        enumValueConverter.writeValue(st, (Enum) value, index, session);
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    @Override
    public boolean isMutable() {
        return false;
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable) value;
    }

    @Override
    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return cached;
    }

    @Override
    public Object replace(Object original, Object 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 objectToSQLString(Object value) {
        verifyConfigured();
        return enumValueConverter.toSqlLiteral(value);
    }

    @Override
    public String toXMLString(Object value) {
        verifyConfigured();
        return (String) enumValueConverter.getJavaDescriptor().unwrap((Enum) value, String.class, null);
    }

    @Override
    @SuppressWarnings("RedundantCast")
    public Object fromXMLString(String xmlValue) {
        verifyConfigured();
        return (T) enumValueConverter.getJavaDescriptor().wrap(xmlValue, null);
    }

    @Override
    public String toLoggableString(Object value, SessionFactoryImplementor factory) {
        verifyConfigured();
        return enumValueConverter.getJavaDescriptor().toString((Enum) value);
    }

    public boolean isOrdinal() {
        verifyConfigured();
        return enumValueConverter instanceof OrdinalEnumValueConverter;
    }
}