HHH-17106 Fix ClassCastException when using length 1 named enum mapping

This commit is contained in:
Christian Beikov 2024-01-08 18:37:10 +01:00
parent 66a723e251
commit 3f8e696dc3
5 changed files with 128 additions and 25 deletions

View File

@ -149,9 +149,9 @@ public class EnumeratedValueResolution<E extends Enum<E>,R> implements BasicValu
}
else if ( style == EnumType.STRING ) {
jdbcType = jdbcTypeRegistry.getDescriptor( jdbcTypeIndicators.getColumnLength() == 1 ? CHAR : VARCHAR );
final JavaType<String> jdbcJavaType = jdbcType.getJdbcRecommendedJavaTypeMapping(
jdbcTypeIndicators.getColumnPrecision(),
jdbcTypeIndicators.getColumnScale(),
final JavaType<Object> jdbcJavaType = jdbcType.getJdbcRecommendedJavaTypeMapping(
(int) jdbcTypeIndicators.getColumnLength(),
null,
typeConfiguration
);
converter = new NamedEnumValueConverter<>( enumJavaType, jdbcType, jdbcJavaType );

View File

@ -334,7 +334,7 @@ public class InferredBasicValueResolver {
if ( enumStyle == EnumType.STRING ) {
//noinspection unchecked
return (EnumeratedValueResolution<E, R>) stringEnumValueResolution(
return (EnumeratedValueResolution<E, R>) namedEnumValueResolution(
enumJavaType,
explicitJavaType,
explicitJdbcType,
@ -418,7 +418,7 @@ public class InferredBasicValueResolver {
}
}
private static <E extends Enum<E>> EnumeratedValueResolution<E,String> stringEnumValueResolution(
private static <E extends Enum<E>> EnumeratedValueResolution<E, Object> namedEnumValueResolution(
EnumJavaType<E> enumJavaType,
BasicJavaType<?> explicitJavaType,
JdbcType explicitJdbcType,
@ -427,7 +427,7 @@ public class InferredBasicValueResolver {
final JdbcType jdbcType = explicitJdbcType == null
? enumJavaType.getRecommendedJdbcType( stdIndicators )
: explicitJdbcType;
final JavaType<String> relationalJtd = stringJavaType( explicitJavaType, stdIndicators, context );
final JavaType<Object> relationalJtd = namedJavaType( explicitJavaType, stdIndicators, context );
return new EnumeratedValueResolution<>(
jdbcType,
@ -442,7 +442,7 @@ public class InferredBasicValueResolver {
: relationalJtd.getRecommendedJdbcType( stdIndicators );
}
private static JavaType<String> stringJavaType(
private static JavaType<Object> namedJavaType(
BasicJavaType<?> explicitJavaType,
JdbcTypeIndicators stdIndicators,
MetadataBuildingContext context) {
@ -454,7 +454,7 @@ public class InferredBasicValueResolver {
" should handle `java.lang.String` as its relational type descriptor"
);
}
return (JavaType<String>) explicitJavaType;
return (JavaType<Object>) explicitJavaType;
}
else {
return context.getMetadataCollector().getTypeConfiguration().getJavaTypeRegistry()

View File

@ -273,19 +273,22 @@ public class EnumType<T extends Enum<T>>
);
}
private JavaType<String> getStringType() {
return typeConfiguration.getJavaTypeRegistry().getDescriptor(String.class);
private JavaType<Object> namedJavaType(JdbcTypeIndicators stdIndicators) {
return typeConfiguration.getJavaTypeRegistry().getDescriptor(
stdIndicators.getColumnLength() == 1 ? Character.class : String.class
);
}
private EnumValueConverter<T,?> getConverter(
EnumJavaType<T> enumJavaType,
EnumType<T>.LocalJdbcTypeIndicators localIndicators,
boolean useNamed) {
if (useNamed) {
if ( useNamed ) {
final JavaType<Object> relationalJavaType = namedJavaType( localIndicators );
return new NamedEnumValueConverter<>(
enumJavaType,
getStringType().getRecommendedJdbcType( localIndicators ),
getStringType()
relationalJavaType.getRecommendedJdbcType( localIndicators ),
relationalJavaType
);
}
else {
@ -310,11 +313,12 @@ public class EnumType<T extends Enum<T>>
relationalJavaType
);
}
else if ( isCharacterType(type) ) {
else if ( isCharacterType( type ) ) {
final JavaType<Object> relationalJavaType = namedJavaType( localIndicators );
return new NamedEnumValueConverter<>(
enumJavaType,
getStringType().getRecommendedJdbcType( localIndicators ),
getStringType()
relationalJavaType.getRecommendedJdbcType( localIndicators ),
relationalJavaType
);
}
else {

View File

@ -24,18 +24,19 @@ import static org.hibernate.type.descriptor.converter.internal.EnumHelper.getEnu
*
* @author Steve Ebersole
*/
public class NamedEnumValueConverter<E extends Enum<E>> implements EnumValueConverter<E,String>, Serializable {
public class NamedEnumValueConverter<E extends Enum<E>> implements EnumValueConverter<E, Object>, Serializable {
private final EnumJavaType<E> domainTypeDescriptor;
private final JdbcType jdbcType;
private final JavaType<String> relationalTypeDescriptor;
private final JavaType<Object> relationalTypeDescriptor;
public NamedEnumValueConverter(
EnumJavaType<E> domainTypeDescriptor,
JdbcType jdbcType,
JavaType<String> relationalTypeDescriptor) {
JavaType<?> relationalTypeDescriptor) {
this.domainTypeDescriptor = domainTypeDescriptor;
this.jdbcType = jdbcType;
this.relationalTypeDescriptor = relationalTypeDescriptor;
//noinspection unchecked
this.relationalTypeDescriptor = (JavaType<Object>) relationalTypeDescriptor;
}
@Override
@ -44,18 +45,21 @@ public class NamedEnumValueConverter<E extends Enum<E>> implements EnumValueConv
}
@Override
public JavaType<String> getRelationalJavaType() {
public JavaType<Object> getRelationalJavaType() {
return relationalTypeDescriptor;
}
@Override
public E toDomainValue(String relationalForm) {
return domainTypeDescriptor.fromName( relationalForm );
public E toDomainValue(Object relationalForm) {
return relationalForm == null
? null
: domainTypeDescriptor.fromName( relationalTypeDescriptor.toString( relationalForm ) );
}
@Override
public String toRelationalValue(E domainForm) {
return domainTypeDescriptor.toName( domainForm );
public Object toRelationalValue(E domainForm) {
final String name = domainTypeDescriptor.toName( domainForm );
return name == null ? null : relationalTypeDescriptor.fromString( name );
}
@Override

View File

@ -0,0 +1,95 @@
/*
* 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.orm.test.mapping.basic;
import java.util.List;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SessionFactory
@DomainModel(annotatedClasses = EnumSingleCharTest.Person.class)
@JiraKey("HHH-17106")
public class EnumSingleCharTest {
@BeforeAll
public void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
session.persist( new Person( 1L, SinglecharEnum.B ) );
session.persist( new Person( 2L, SinglecharEnum.R ) );
} );
}
@AfterAll
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction( session -> session.createMutationQuery( "delete from Person" ).executeUpdate() );
}
@Test
public void testUpdate(SessionFactoryScope scope) {
scope.inTransaction( session -> {
List<Person> resultList = session.createQuery( "from Person order by id", Person.class ).getResultList();
assertEquals( 2, resultList.size() );
assertEquals( SinglecharEnum.B, resultList.get( 0 ).getSingleChar() );
assertEquals( SinglecharEnum.R, resultList.get( 1 ).getSingleChar() );
} );
}
public enum SinglecharEnum {
B("first"),
R("second"),
O("third"),
K("fourth"),
E("fifth"),
N("sixth");
private final String item;
SinglecharEnum(String item) {
this.item = item;
}
public String getItem() {
return item;
}
}
@Entity(name = "Person")
public static class Person {
@Id
private Long id;
@Column(name="SINGLE_CHAR", length = 1)
@Enumerated(EnumType.STRING)
private SinglecharEnum singleChar;
public Person() {
}
public Person(Long id, SinglecharEnum singleChar) {
this.id = id;
this.singleChar = singleChar;
}
public SinglecharEnum getSingleChar() {
return singleChar;
}
}
}