37 votes

Comment utiliser les énumérations avec JPA

J'ai une base de données existante d'un système de location de films. Chaque film a un a un attribut évaluation. Dans SQL, ils ont utilisé une contrainte de limiter les valeurs possibles de cet attribut.

CONSTRAINT film_rating_check CHECK 
    ((((((((rating)::text = ''::text) OR 
          ((rating)::text = 'G'::text)) OR 
          ((rating)::text = 'PG'::text)) OR 
          ((rating)::text = 'PG-13'::text)) OR 
          ((rating)::text = 'R'::text)) OR 
          ((rating)::text = 'NC-17'::text)))

Je pense qu'il serait bien d'utiliser un enum de Java à la carte de la contrainte dans le monde objet. Mais il n'est pas possible de simplement prendre les valeurs autorisées en raison de la char spécial dans "PG-13" et "NC-17". J'ai donc mis en application les enum:

public enum Rating {

    UNRATED ( "" ),
    G ( "G" ), 
    PG ( "PG" ),
    PG13 ( "PG-13" ),
    R ( "R" ),
    NC17 ( "NC-17" );

    private String rating;

    private Rating(String rating) {
        this.rating = rating;
    }

    @Override
    public String toString() {
        return rating;
    }
}

@Entity
public class Film {
    ..
    @Enumerated(EnumType.STRING)
    private Rating rating;
    ..

Avec la méthode toString() de la direction enum -> Chaîne fonctionne correctement, mais la Chaîne -> enum ne fonctionne pas. J'obtiens l'exception suivante:

[TopLink Avertissement]: 2008.12.09 01:30:57.434--ServerSession(4729123)--Exception [TOPLINK-116] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.des exceptions.DescriptorException Description de l'Exception: Pas de conversion valeur fournie pour la valeur [NC-17] dans le champ [FILM.CLASSEMENT]. Cartographie: oracle.toplink.essentials.les mappages.DirectToFieldMapping[cote-->FILM.NOTATION] Descripteur: RelationalDescriptor(de.fhw.nsdb.les entités.Film --> [DatabaseTable(FILM)])

cheers

timo

33voto

aledbf Points 517

avez-vous essayé de stocker la valeur ordinale. Stocker la valeur de chaîne fonctionne correctement si vous n'avez pas associé de chaîne à la valeur:

 @Enumerated(EnumType.ORDINAL)
 

27voto

cletus Points 276888

Vous avez un problème ici, et c'est les capacités limitées de la JPA quand il s'agit de la manipulation des enums. Avec les énumérations, vous avez deux choix:

  1. Les stocker dans un certain nombre égalant Enum.ordinal(), ce qui est une idée terrible (à mon humble avis); ou
  2. Les stocker dans une chaîne égalant Enum.name(). Remarque: non toString() comme vous vous en doutez, surtout depuis que le défaut behaviourfor Enum.toString() est de retour name().

Personnellement, je pense que la meilleure option est de (2).

Maintenant, vous avez un problème que vous êtes à la définition des valeurs qui ne représentent vailid les noms d'instance en Java (à savoir à l'aide d'un trait d'union). Si vos choix sont:

  • La modification de vos données;
  • Persistent les champs de type Chaîne et, implicitement, de les convertir ou de les énumérations de vos objets; ou
  • Utiliser les extensions non standard comme TypeConverters.

Je voudrais les faire dans l'ordre (la première à la dernière), comme un ordre de préférence.

Quelqu'un a suggéré d'Oracle TopLink du convertisseur, mais vous êtes probablement à l'aide de Toplink Essentials, étant la référence JPA 1.0 mise en œuvre, qui est un sous-ensemble de l'offre commerciale d'Oracle Toplink produit.

Comme une autre suggestion, je recommanderais fortement de commutation à EclipseLink. C'est une mesure plus complète de la mise en œuvre de Toplink Essentials et Eclipselink sera la référence pour l'implémentation de JPA 2.0 lors de leur sortie (prévue pour la JavaOne milieu de l'année prochaine).

8voto

Dan Vinton Points 11975

On dirait que vous devez ajouter un support pour un type personnalisé:

Extension d'OracleAS TopLink pour prendre en charge les conversions de types personnalisés

5voto

JeeBee Points 11882
 public enum Rating {

    UNRATED ( "" ),
    G ( "G" ), 
    PG ( "PG" ),
    PG13 ( "PG-13" ),
    R ( "R" ),
    NC17 ( "NC-17" );

    private String rating;

    private static Map<String, Rating> ratings = new HashMap<String, Rating>();
    static {
        for (Rating r : EnumSet.allOf(Rating.class)) {
            ratings.put(r.toString(), r);
        }
    }

    private static Rating getRating(String rating) {
        return ratings.get(rating);
    }

    private Rating(String rating) {
        this.rating = rating;
    }

    @Override
    public String toString() {
        return rating;
    }
}
 

Cependant, je ne sais pas comment faire les correspondances dans le côté annoté de TopLink.

2voto

Andreas Petersson Points 8096

je ne sais pas entrailles de toplink, mais mon hypothèse est la suivante: il utilise la Notation.valueOf(String s) à la carte dans l'autre sens. il n'est pas possible de remplacer valueOf(), de sorte que vous devez vous en tenir à la convention de nommage de java, afin de permettre une bonne méthode valueOf.

public enum Rating {

    UNRATED,
    G, 
    PG,
    PG_13 ,
    R ,
    NC_17 ;

    public String getRating() {
        return name().replace("_","-");;
    }
}

getRating produit la "lisible" de notation. notez que le "-" chanracter n'est pas autorisé dans l'enum identificateur.

bien sûr, vous aurez à stocker les valeurs dans la base de données comme NC_17.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X