Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA enumerated types mapping. Best approach

As usual Java enum types have corresponding codes and name description. And Java classes that contain such fields, contain them as Enum:

public enum MyEnum{     SOMEINSTANCE(1, "test1"),     SOMEINSTANCE(2, "test2");      private final int code;     private final String name;     private MyEnum(int code, String name){         this.code = code;         this.name = name;     }     ... helper getter for code and name }  @Entity puclic class EnumHolder{     private MyEnum myEnum; } 

I'm a newbie to JPA, but I wish to have the 'myEnums' table, looked like:

code int not null, name varchar(50) not null) 

And in my enumHolder table I want to have the myEnumCode field that points to the myEnums table.

Using currenlty supported both EnumType.ORDINAL and EnumType.STRING I suppose not a good idea.

And another question. How can I fill in the myEnums table using Java MyEnum class data? How would you do it? The best approach please.

PS: here are solutions I can offer:

Let's suppose there is table myEnum with code and name fields. Java MyEnum enum, that is described in the question. enumHolder table need to have myEnumCode reference to myEnum.code field. Please comment the solution if you disagree.

@Entity @Access(AccessType.FIELD) public class EnumHolder {     @Id private int id;     @Transient private MyEnum myEnum;     …     public int getId() { return id; }     public void setId(int id) { this.id = id; }     public MyEnum getMyEnum() { return MyEnum; }     public void setMyEnum(MyEnum myEnum) { this.myEnum = myEnum; }      @Access(AccessType.PROPERTY) @Column(name="myEnumCode")     protected int getMyEnumForDb() {         return myEnum.getCode();     }      protected void setMyEnumForDb(int enumCode) {         myEnum = MyEnum.getByCode( enumCode);     } … } 

Of course there are drawbacks here. But at the moment I cannot see the better approach. Alternatives with EnumType.ORDINAL and EnumType.STRING please don't offer. I don't want to write here all problems that can exist with its usage (in Effective Java it is described concerning ordinals usage). Using EnumType.STRING I don't like either cause it does not allow to have discription in database and request it from db.

Concerning fillind database. I think it's not difficult to write a script, that clears the myEnum table and then for each Java enum istance makes insert into the table. And always do it during a deployment phase.

like image 494
Alexandr Avatar asked Apr 22 '13 05:04

Alexandr


People also ask

What annotation is used if enum is used in an entity?

The most common option to map an enum value to and from its database representation in JPA before 2.1 is to use the @Enumerated annotation.

Which annotation in JPA may be used to specify whether enum should be persisted as number in database column?

The @Enumerated annotation enables you to define how an enum attribute gets persisted in the database. By default, all JPA implementations map the ordinal value of the enum to a numeric database column.

How do I map an enum to a column in a database?

To map the Enum to a String database column type, you need to specify the EnumType. STRING value when using the @Enumerated annotation. As expected, the String representation of the Java Enum was used to populate the associated database column value.

Which interface is used to implement custom enum mapping in hibernate?

You implement the AttributeConverter interface, annotate the class with @Converter, and implement the 2 methods that provide the mapping between the entity and the database representation.


Video Answer


1 Answers

The best approach would be to map a unique ID to each enum type, thus avoiding the pitfalls of ORDINAL and STRING. See this post which outlines 5 ways you can map an enum.

Taken from the link above:

1&2. Using @Enumerated

There are currently 2 ways you can map enums within your JPA entities using the @Enumerated annotation. Unfortunately both EnumType.STRING and EnumType.ORDINAL have their limitations.

If you use EnumType.String then renaming one of your enum types will cause your enum value to be out of sync with the values saved in the database. If you use EnumType.ORDINAL then deleting or reordering the types within your enum will cause the values saved in the database to map to the wrong enums types.

Both of these options are fragile. If the enum is modified without performing a database migration, you could jeopodise the integrity of your data.

3. Lifecycle Callbacks

A possible solution would to use the JPA lifecycle call back annotations, @PrePersist and @PostLoad. This feels quite ugly as you will now have two variables in your entity. One mapping the value stored in the database, and the other, the actual enum.

4. Mapping unique ID to each enum type

The preferred solution is to map your enum to a fixed value, or ID, defined within the enum. Mapping to predefined, fixed value makes your code more robust. Any modification to the order of the enums types, or the refactoring of the names, will not cause any adverse effects.

5. Using Java EE7 @Convert

If you are using JPA 2.1 you have the option to use the new @Convert annotation. This requires the creation of a converter class, annotated with @Converter, inside which you would define what values are saved into the database for each enum type. Within your entity you would then annotate your enum with @Convert.

My preference: (Number 4)

The reason why I prefer to define my ID's within the enum as oppose to using a converter, is good encapsulation. Only the enum type should know of its ID, and only the entity should know about how it maps the enum to the database.

See the original post for the code example.

like image 119
Chris Ritchie Avatar answered Oct 08 '22 04:10

Chris Ritchie