JPA @Enumerated vs. @Entity

En muchas ocasiones nos hemos visto en la necesidad de incorporar valores que se corresponden con un enumerado, como por ejemplo, diferenciación de tipos, estados,…​ Lo que se suele hacer es crear un enumerado en Java y posteriormente referenciarlo en la clase que lo necesite incorporando la anotación @Enumerated.

Esta anotación permite que se almacene como valor en la base de datos bien el orden (EnumType.ORDINAL) o bien el nombre (EnumType.String) del elemento dentro del enumerado. Ejemplo:

public enum EnumeradoEjemplo {
  VALOR1, VALOR2, VALOR3;
}

Si en la entidad que haga uso de este enumerado ponemos EnumType.ORDINAL y el valor que toma es VALOR2, en la base de datos se grabará un entero con el número 1, puesto que se establecen los valores según el orden de definición y empezando por 0, 1, 2,…​ Si por el contrario ponemos EnumType.STRING, el valor que tomaría sería el texto “VALOR2”.

Desde mi punto de vista, con estas opciones, tenemos las siguientes problemáticas:

  • Si usamos ORDINAL y dejamos que sea Java quien ponga los valores en función del orden en que definamos el enumerado:

    • Si posteriormente modificamos el enumerado y añadimos un nuevo valor entre alguno de los existentes, todo lo que tengamos en la base de datos pierde coherencia.

    • Necesitamos facilitar al resto de departamentos que pudieran leer la información de la base de datos las equivalencias entre los valores numéricos y los valores del enumerado.

  • Si usamos STRING, perderíamos normalización en la base de datos aunque éste no sería el mayor problema: Si posteriormente modificamos el enumerado y renombramos uno de los valores, tendríamos que actualizar toda la base de datos estableciendo donde aplique el nuevo valor de tipo texto.

Por mi parte, la recomendación es utilizar directamente un Integer a almacenar en la base de datos, es decir, en la entidad, pondríamos algo como:

@Column(name = "tipo_enum", precision = 2, scale = 0)
private Integer tipo;

public EnumeradoEjemplo getTipo() {
  return EnumeradoEjemplo.valueOf(this.tipo);
}

public void setTipo(EnumeradoEjemplo tipo) {
  this.position = (tipo == null) ? null : tipo.getId();
}

Y el enumerado cambiaría a:

public enum EnumeradoEjemplo {
  VALOR1(1), VALOR2(2), VALOR3(3);

  private Integer id;

  private EnumeradoEjemplo(Integer id) {
    this.id = id;
  }

  public Integer getId() {
    return id;
  }

  public static EnumeradoEjemplo valueOf(Integer id) {
    if (id == null)
      return null;

    for (EnumeradoEjemplo ejemplo : EnumeradoEjemplo.values()) {
      if (id.equals(ejemplo.getId()))
        return ejemplo;
    }

    throw new IllegalArgumentException("No existe enumerado con valor: " + id);
  }

}

Por último, no nos olvidemos de crear una tabla de correspondencia de los valores numéricos a un texto descriptivo, de forma que otros departamentos puedan hacer un join en la consulta y saber qué están recuperando :wink: