org.hibernate.type.EnumType Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-core Show documentation
Show all versions of hibernate-core Show documentation
The core O/RM functionality as provided by Hibernate
/*
* 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 .
*/
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:
* -
* enumClass - Names the enumeration class.
*
* -
* useNamed - 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).
*
* -
* type - Identifies the JDBC type (via type code) to be used for the column.
*
*
*
* @author Emmanuel Bernard
* @author Hardy Ferentschik
* @author Steve Ebersole
*/
@SuppressWarnings("unchecked")
public class EnumType
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 getAnnotation(Annotation[] annotations, Class 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 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] );
}
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 );
}
@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;
}
}