[Java] ¿Mapa enum en JPA con valores fijos?


Answers

Esto ahora es posible con JPA 2.1:

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

Más detalles:

Question

Estoy buscando diferentes formas de mapear una enumeración usando JPA. Especialmente quiero establecer el valor entero de cada entrada enum y guardar solo el valor entero.

@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;
}

Una solución simple es usar la anotación enumerada con EnumType.ORDINAL:

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

Pero en este caso JPA mapea el índice enum (0,1,2) y no el valor que quiero (100,200,300).

Las dos soluciones que encontré no parecen simples ...

Primera solución

Una solución, propuesta aquí , usa @PrePersist y @PostLoad para convertir la enumeración en otro campo y marcar el campo enum como transitorio:

@Basic
private int intValueForAnEnum;

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

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

Segunda solución

La segunda solución propuesta aquí propuso un objeto de conversión genérico, pero aún parece pesado y orientado a la hibernación (@Type no parece existir en 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")
            }
)

¿Hay alguna otra solución?

Tengo varias ideas en mente pero no sé si existen en JPA:

  • utilice los métodos setter y getter del miembro correcto de la Clase de autoridad al cargar y guardar el objeto Authority
  • una idea equivalente sería decirle a JPA cuáles son los métodos de Enum correcto para convertir enum a int y int a enum
  • Debido a que estoy usando Spring, ¿hay alguna manera de decirle a JPA que use un convertidor específico (RightEditor)?



Posiblemente código relacionado cercano de Pascal

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

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

        private Integer value;

        private Right(Integer value) {
            this.value = value;
        }

        // Reverse lookup Right for getting a Key from it's values
        private static final Map<Integer, Right> lookup = new HashMap<Integer, Right>();
        static {
            for (Right item : Right.values())
                lookup.put(item.getValue(), item);
        }

        public Integer getValue() {
            return value;
        }

        public static Right getKey(Integer value) {
            return lookup.get(value);
        }

    };

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

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

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

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

}



El problema es que, creo, que JPA nunca tuvo en cuenta la idea en mente de que podríamos tener un esquema preexistente complejo ya en su lugar.

Creo que hay dos deficiencias principales que resultan de esto, específicas de Enum:

  1. La limitación de usar name () y ordinal (). ¿Por qué no solo marca un getter con @Id, de la misma manera que hacemos con @Entity?
  2. Enum generalmente tienen representación en la base de datos para permitir la asociación con todo tipo de metadatos, incluyendo un nombre propio, un nombre descriptivo, tal vez algo con localización, etc. Necesitamos la facilidad de uso de un Enum combinado con la flexibilidad de una Entidad.

Ayuda a mi causa y vota en JPA_SPEC-47

¿No sería más elegante que usar un @Converter para resolver el problema?

// Note: this code won't work!!
// it is just a sample of how I *would* want it to work!
@Enumerated
public enum Language {
  ENGLISH_US("en-US"),
  ENGLISH_BRITISH("en-BR"),
  FRENCH("fr"),
  FRENCH_CANADIAN("fr-CA");
  @ID
  private String code;
  @Column(name="DESCRIPTION")
  private String description;

  Language(String code) {
    this.code = code;
  }

  public String getCode() {
    return code;
  }

  public String getDescription() {
    return description;
  }
}



Links