Map enum in JPA with fixed values?

Kartoch picture Kartoch · May 2, 2010 · Viewed 138.4k times · Source

I'm looking for the different ways to map an enum using JPA. I especially want to set the integer value of each enum entry and to save only the integer value.

@Entity
@Table(name = "AUTHORITY_")
public class Authority implements Serializable {

  public enum Right {
      READ(100), WRITE(200), EDITOR (300);

      private int value;

      Right(int value) { this.value = value; }

      public int getValue() { return value; }
  };

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(name = "AUTHORITY_ID")
  private Long id;

  // the enum to map : 
  private Right right;
}

A simple solution is to use the Enumerated annotation with EnumType.ORDINAL:

@Column(name = "RIGHT")
@Enumerated(EnumType.ORDINAL)
private Right right;

But in this case JPA maps the enum index (0,1,2) and not the value I want (100,200,300).

Th two solutions I found do not seem simple...

First Solution

A solution, proposed here, uses @PrePersist and @PostLoad to convert the enum to an other field and mark the enum field as transient:

@Basic
private int intValueForAnEnum;

@PrePersist
void populateDBFields() {
  intValueForAnEnum = right.getValue();
}

@PostLoad
void populateTransientFields() {
  right = Right.valueOf(intValueForAnEnum);
}

Second Solution

The second solution proposed here proposed a generic conversion object, but still seems heavy and hibernate-oriented (@Type doesn't seem to exist in Java EE):

@Type(
    type = "org.appfuse.tutorial.commons.hibernate.GenericEnumUserType",
    parameters = {
            @Parameter(
                name  = "enumClass",                      
                value = "Authority$Right"),
            @Parameter(
                name  = "identifierMethod",
                value = "toInt"),
            @Parameter(
                name  = "valueOfMethod",
                value = "fromInt")
            }
)

Is there any other solutions ?

I've several ideas in mind but I don't know if they exist in JPA:

  • use the setter and getter methods of right member of Authority Class when loading and saving the Authority object
  • an equivalent idea would be to tell JPA what are the methods of Right enum to convert enum to int and int to enum
  • Because I'm using Spring, is there any way to tell JPA to use a specific converter (RightEditor) ?

Answer

Pascal Thivent picture Pascal Thivent · May 2, 2010

For versions earlier than JPA 2.1, JPA provides only two ways to deal with enums, by their name or by their ordinal. And the standard JPA doesn't support custom types. So:

  • If you want to do custom type conversions, you'll have to use a provider extension (with Hibernate UserType, EclipseLink Converter, etc). (the second solution). ~or~
  • You'll have to use the @PrePersist and @PostLoad trick (the first solution). ~or~
  • Annotate getter and setter taking and returning the int value ~or~
  • Use an integer attribute at the entity level and perform a translation in getters and setters.

I'll illustrate the latest option (this is a basic implementation, tweak it as required):

@Entity
@Table(name = "AUTHORITY_")
public class Authority implements Serializable {

    public enum Right {
        READ(100), WRITE(200), EDITOR (300);

        private int value;

        Right(int value) { this.value = value; }    

        public int getValue() { return value; }

        public static Right parse(int id) {
            Right right = null; // Default
            for (Right item : Right.values()) {
                if (item.getValue()==id) {
                    right = item;
                    break;
                }
            }
            return right;
        }

    };

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "AUTHORITY_ID")
    private Long id;

    @Column(name = "RIGHT_ID")
    private int rightId;

    public Right getRight () {
        return Right.parse(this.rightId);
    }

    public void setRight(Right right) {
        this.rightId = right.getValue();
    }

}