views:

1513

answers:

1

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 JEE):

@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) ?
+9  A: 

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();
    }

}
Pascal Thivent
Yes, that's seem the best solution...
Kartoch