Преобразование Hibernate SQL не выполняется для типа поля Enum
Я использую запрос SQL, а затем преобразовываю результат, используя Hibernates's Transformers.aliasToBean()
, Один из столбцов в моем запросе - enum. Преобразование так или иначе терпит неудачу для перечисления. Что я должен делать? Какой тип данных я должен использовать? Я хочу, чтобы более одного символа преобразовали результат в мой тип enum.
Вот как выглядит упрощенная версия моего запроса / кода (b- перечисление в профилях таблицы):
session.createSQLQuery("select a, b from profiles").setResultTransformer(Transformers.aliasToBean(Profile.class))
.list();
Исключение: expected type: Foo.ProfileStateEnum, actual value: java.lang.Character
2 ответа
Предполагая, что типом перечисления java, который соответствует столбцу b, является Foo.ProfileStateEnum, следующий фрагмент кода должен работать для вас. (Я тестировал с Hibernate 4.1.6)
import java.util.Properties;
import org.hibernate.type.Type;
import org.hibernate.type.IntegerType;
import org.hibernate.internal.TypeLocatorImpl.TypeLocatorImpl;
import org.hibernate.type.TypeResolver.TypeResolver;
import org.hibernate.type.EnumType;
Properties params = new Properties();
params.put("enumClass", "Foo.ProfileStateEnum");
params.put("type", "12"); /*type 12 instructs to use the String representation of enum value*/
/*If you are using Hibernate 5.x then try:
params.put("useNamed", true);*/
Type myEnumType = new TypeLocatorImpl(new TypeResolver()).custom(EnumType.class, params);
List<Profile> profileList= getSession().createSQLQuery("select a as ID, b from profiles")
.addScalar("ID", IntegerType.INSTANCE)
.addScalar("b", myEnumType )
.setResultTransformer(Transformers.aliasToBean(Profile.class))
.list();
Я нашел два способа добиться этого.
Использовать
org.hibernate.type.CustomType
с участиемorg.hibernate.type.EnumType
(поставить либоEnumType.NAMED
или жеEnumType.TYPE
, видетьEnumType#interpretParameters
). Как показано ниже:Properties parameters = new Properties(); parameters.put(EnumType.ENUM, MyEnum.class.getName()); // boolean or string type of true/false; declare database type parameters.put(EnumType.NAMED, true); // string only; declare database type parameters.put(EnumType.TYPE, String.valueOf(Types.VARCHAR)); EnumType<MyEnum> enumType = new EnumType<>(); enumType.setTypeConfiguration(new TypeConfiguration()); enumType.setParameterValues(parameters); CustomType customEnumType = new CustomType(enumType);
Другой простой способ. Использовать
org.hibernate.type.StandardBasicTypeTemplate
с участиемorg.hibernate.type.descriptor.sql.*TypeDescriptor
. Как показано ниже:StandardBasicTypeTemplate<MyEnum> enumType = new StandardBasicTypeTemplate<>(VarcharTypeDescriptor.INSTANCE, new EnumJavaTypeDescriptor<>(MyEnum.class));
Давайте посмотрим, почему вы получаете это исключение.
Из вопроса очевидно, что вы использовали @Enumerated(EnumType.STRING)
аннотация для поля 'b' в вашем классе модели. Таким образом, поле - это enum для вашего модельного класса и varchar для вашей базы данных. Собственный SQL не заботится о вашем классе модели и возвращает то, что есть в таблице базы данных, как есть. Так что в вашем случае SQLQuery
вы используете вернет String
для "б" вместо ProfileStateEnum
тип. Но ваш метод установки для "б" в Profile
класс занимает ProfileStateEnum
введите аргумент.
Таким образом вы получаете исключение "ожидаемый тип: Foo.ProfileStateEnum, фактическое значение: java.lang.Character"
Вы можете использовать Aliasing для решения проблемы.
Я предлагаю создать псевдоним вашего столбца с любым именем и создать метод установки для этого псевдонима в вашей модели /dto.
Например, пусть псевдоним вашего столбца как "enumStr".
Тогда ваш запрос будет выглядеть так: " выберите a, b как enumStr из профилей "
Теперь создайте метод установки для этого псевдонима в вашем Profile
учебный класс.
(Предполагая, что перечисление ProfileStateEnum
может иметь любое из двух значений STATE1
а также STATE2
)
public void setEnumStr(String str){
/*Convert the string to enum and set the field 'b'*/
if(str.equals(ProfileStateEnum.STATE1.toString())){
b = ProfileStateEnum.STATE1;
} else {
b = ProfileStateEnum.STATE2;
}
}
Теперь о трансформации, сеттер для псевдонима setEnumStr(String)
будет вызываться вместо установщика для поля setB(ProfileStateEnum)
и строка будет преобразована и сохранена в нужный вам тип без каких-либо исключений.
Я новичок в Hibernate, и решение сработало для меня. Я использую PostgreSQL. Но я считаю, что это работает и для других баз данных.