8

I am using a SQL query and then transforming the result using Hibernates's Transformers.aliasToBean(). One of the columns in my query is an enum. The transformation somehow fails for the enum. What should I do? Which datatype should I use? I want more than 1 character to transform the result into my enum type.

This is how the simplified version of my query/code looks like (b is an enum in the table profiles):

session.createSQLQuery("select a, b from profiles").setResultTransformer(Transformers.aliasToBean(Profile.class))
                    .list();

Exception : expected type: Foo.ProfileStateEnum, actual value: java.lang.Character

2
  • do you want to use only Transformers.aliasToBean or a HQL will do? Commented Mar 9, 2012 at 13:27
  • I am writing a native SQL query, hence I guess HQL will not be enough. Commented Mar 10, 2012 at 5:11

3 Answers 3

14

Assuming that the java enum type that corresponds to column b is Foo.ProfileStateEnum, the following code snippet should work for you. (I tested with 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();
Sign up to request clarification or add additional context in comments.

8 Comments

This definitely looks interesting. I will try it out. Thanks.
Not working in hibernate 5.x. Am looking for a quick solution for hibernate 5.x.
For those as absent-minded as me: that part .custom(EnumType.class, params); must be typed as is, since it's org.hibernate.type.EnumType in there, not your custom com.company.MyEnumClass
It's good idea to use constants instead of strings: EnumType.ENUM instead of "enumClass" and EnumType.NAMED instead of "useNamed".
@IliaNedoluzhko just use Hibernate 5. The magic constant 12 is for Hibernate 4 only.
|
5

I found two ways to achieve it.

  1. Use org.hibernate.type.CustomType with org.hibernate.type.EnumType(put either EnumType.NAMED or EnumType.TYPE, see EnumType#interpretParameters). Like below:

    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);
    
  2. Another simple way. Use org.hibernate.type.StandardBasicTypeTemplate with org.hibernate.type.descriptor.sql.*TypeDescriptor. Like below:

    StandardBasicTypeTemplate<MyEnum> enumType =
            new StandardBasicTypeTemplate<>(VarcharTypeDescriptor.INSTANCE,
                    new EnumJavaTypeDescriptor<>(MyEnum.class));
    

3 Comments

Option 2 definitely seems to be the simplest and worked for me.
Option 1 works for Hibernate 5.4.32.Final.
Option 2 worked for me with Hibernate 5.6.5.
0

Let's see why you are getting this exception.

From the question it is obvious that you have used @Enumerated(EnumType.STRING) annotation for the field 'b' in you model class. So the field is an enum for your model class and a varchar for your database. Native SQL is not concerned about you model class and returns what ever is there in the database table as it is. So in your case, the SQLQuery you are using will return a String for 'b' instead of a ProfileStateEnum type. But your setter method for 'b' in the Profile class takes a ProfileStateEnum type argument.

Thus you get the exception "expected type: Foo.ProfileStateEnum, actual value: java.lang.Character"

You can use Aliasing to solve the problem.

What I suggest is, alias your column with any name you want and create a setter method for that alias in your model/dto.

For example, lets alias your column as 'enumStr'.

Then your query will look like this : "select a, b as enumStr from profiles"

Now, create a setter method for that alias in the your Profile class.

(Assuming that the enum ProfileStateEnum can have any of the two values STATE1 and 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;
    }
}

Now on transforming, the setter for the alias setEnumStr(String) will be invoked instead of setter for the field setB(ProfileStateEnum) and the string will be converted and saved to the type you want without any exceptions.

I am a beginner in Hibernate and the solution worked for me. I am using PostgreSQL. But I believe it works for other databases too.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.