saravana_pc
saravana_pc

Reputation: 2687

Hibernate @Enumerated mapping

Hibernate provides @Enumerated annotation which supports two types of Enum mapping either using ORDINAL or STRING. When we map using EnumType.STRING, it takes the "name" of the Enum and not the toString() representation of the Enum. This is a problem in scenarios where the database column consists of only one character. For example, I have the following Enum:

public enum Status{
  OPEN{
   @Override
   public String toString(){
     return "O";}
   },

  WAITLIST{
   @Override
   public String toString(){
     return "W";}
   },

  COMPLETE{
   @Override
   public String toString(){
     return "C";}
   }

}

When I persist the enum Status.OPEN using @Enumerated(EnumType.STRING), the value that Hibernate tries to store in the database is OPEN. However, my database column consists of only one character and hence it throws an exception.

One way to overcome this issue is to change the Enum type to hold single characters (like STATUS.O, STATUS.W instead of STATUS.OPEN, STATUS.WAITLIST). However, this reduces readability. Any suggestions to preserve the readability as well as mapping the Enum to a single character column?

Thanks.

Upvotes: 28

Views: 36271

Answers (3)

Ondrej Bozek
Ondrej Bozek

Reputation: 11501

The best to customize mapping for enums is to use AttributeConverter i.e:

@Entity
public class Person {
    ...
    @Basic
    @Convert( converter=GenderConverter.class )
    public Gender gender;
}

public enum Gender {
    MALE( 'M' ),
    FEMALE( 'F' );

    private final char code;

    private Gender(char code) {
        this.code = code;
    }

    public char getCode() {
        return code;
    }

    public static Gender fromCode(char code) {
        if ( code == 'M' || code == 'm' ) {
            return MALE;
        }
        if ( code == 'F' || code == 'f' ) {
            return FEMALE;
        }
        throw ...
    }
}

@Converter
public class GenderConverter
        implements AttributeConverter<Gender, Character> {

    public Character convertToDatabaseColumn(Gender value) {
        if ( value == null ) {
            return null;
        }

        return value.getCode();
    }

    public Gender convertToEntityAttribute(Character value) {
        if ( value == null ) {
            return null;
        }

        return Gender.fromCode( value );
    }
}

You can find it in Hibernate docs: http://docs.jboss.org/hibernate/orm/5.0/mappingGuide/en-US/html_single/#d5e678

Upvotes: 17

The Gilbert Arenas Dagger
The Gilbert Arenas Dagger

Reputation: 12751

Here is an example which uses annotations.

http://www.gabiaxel.com/2011/01/better-enum-mapping-with-hibernate.html

It's still based off of a custom UserType. Four and a half years later and I'm still not aware of a better way to do this.

Typically, I persist my enum values as a char / int, some simple ID, then use a transient method that finds the appropriate enum by the simple ID value, e.g.

@Transient
public MyEnum getMyEnum() {
    return MyEnum.findById(simpleId); // 
}

and...

public enum MyEnum {
    FOO('F'),
    BAR('B');

    private char id;
    private static Map<Character, MyEnum> myEnumById = new HashMap<Character, MyEnum>();

    static {
        for (MyEnum myEnum : values()) {
            myEnumById.put(myEnum.getId(), myEnum);
        }
    }

    private MyEnum(char id) {
        this.id = id;
    }

    public static MyEnum findById(char id) {
        return myEnumById.get(id);
    }

    public char getId() {
        return id;
    }
}

Upvotes: 1

nes
nes

Reputation: 1

public enum Status
{
    OPEN("O"),
    WAITLIST("W"),
    COMPLETE("C")

private String description;

private Status(String description)
{
   this.description = description;
}

public String getDescription()
{
    return description;
}
}

and then when you read it:

Status.OPEN.getDescription()

Upvotes: -5

Related Questions