[java] Carte enum dans JPA avec des valeurs fixes?



Answers

C'est maintenant possible avec JPA 2.1:

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

Plus de détails:

Question

Je cherche les différentes façons de mapper une énumération en utilisant JPA. Je veux particulièrement définir la valeur entière de chaque entrée enum et enregistrer seulement la valeur entière.

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

Une solution simple consiste à utiliser l'annotation Enumerated avec EnumType.ORDINAL:

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

Mais dans ce cas, JPA mappe l'index enum (0,1,2) et non la valeur que je veux (100,200,300).

Les deux solutions que j'ai trouvées ne me paraissent pas simples ...

Première solution

Une solution, proposée ici , utilise @PrePersist et @PostLoad pour convertir l'énumération en un autre champ et marquer le champ enum comme transitoire:

@Basic
private int intValueForAnEnum;

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

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

Deuxième solution

La deuxième solution proposée ici propose un objet de conversion générique, mais semble toujours lourd et orienté hibernate (@Type ne semble pas exister 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")
            }
)

Y a-t-il d'autres solutions?

J'ai plusieurs idées en tête mais je ne sais pas si elles existent dans JPA:

  • utiliser les méthodes setter et getter du membre right de la classe Authority lors du chargement et de la sauvegarde de l'objet Authority
  • une idée équivalente serait de dire à JPA quelles sont les méthodes de Enum à droite pour convertir enum en int et int enum
  • Parce que j'utilise Spring, est-il possible de dire à JPA d'utiliser un convertisseur spécifique (RightEditor)?



Peut-être fermer le code associé 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();
    }

}



Le problème est, je pense, que JPA n'a jamais été créée avec l'idée que nous pourrions avoir un schéma préexistant complexe déjà en place.

Je pense qu'il y a deux défauts principaux qui en résultent, spécifiques à Enum:

  1. La limitation de l'utilisation de name () et ordininal (). Pourquoi ne pas simplement marquer un getter avec @Id, comme nous le faisons avec @Entity?
  2. Enum ont généralement une représentation dans la base de données pour permettre l'association avec toutes sortes de métadonnées, y compris un nom propre, un nom descriptif, peut-être quelque chose avec localisation etc. Nous avons besoin de la facilité d'utilisation combinée à la flexibilité d'une Entité.

Aidez ma cause et votez pour JPA_SPEC-47

Cela ne serait-il pas plus élégant que d'utiliser un @Converter pour résoudre le problème?

// 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