Reputation: 2687
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
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
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
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