Cómo usar enumeraciones con JPA


Tengo una base de datos existente de un sistema de alquiler de películas. Cada película tiene un atributo de calificación. En SQL usaron una restricción para limitar los valores permitidos de este atributo.

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

Creo que sería bueno usar una enumeración Java para mapear la restricción en el mundo de objetos. Pero no es posible simplemente tomar los valores permitidos debido al carácter especial en "PG-13" y "NC-17". Así que implementé la siguiente enumeración:

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;
    ..

Con el método toString () la dirección enum - > String funciona bien, pero String - > enum no funciona. Obtengo la siguiente excepción:

[Advertencia TopLink]: 2008.12.09 01:30: 57.434 Ser ServerSession(4729123) Exception Excepción [TOPLINK-116] (Oracle TopLink Essentials-2.0.1 (Build b09d-fcs (12/06/2007))): oráculo.toplink.esencial.salvedad.DescriptorException Excepción Descripción: No se proporciona valor de conversión para el valor [NC-17] en campo [PELÍCULA.CLASIFICACIÓN]. Asignación: oráculo.toplink.esencial.asignación.DirectToFieldMapping [valoración> > PELÍCULA.CLASIFICACIÓN] Descriptor: RelationalDescriptor(de.fhw.nsdb.entidad.Cine --> [DatabaseTable (FILM)])

Salud

Timo

Author: Buhake Sindi, 2008-12-09

10 answers

Parece que necesita agregar soporte para un tipo personalizado:

Extensión de OracleAS TopLink para Admitir Conversiones de Tipos Personalizados

 7
Author: Dan Vinton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-16 00:42:00

¿Ha intentado almacenar el valor ordinal? Almacenar el valor de la cadena funciona bien si no tiene una cadena asociada al valor:

@Enumerated(EnumType.ORDINAL)
 29
Author: aledbf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-08 20:44:12

Tiene un problema aquí y es que las capacidades limitadas de JPA cuando se trata de manejar enumeraciones. Con enums tienes dos opciones:

  1. Almacenarlos como un número igual a Enum.ordinal(), que es una idea terrible (en mi humilde opinión); o
  2. Almacenarlos como una cadena igual a Enum.name(). Nota: no toString() como es de esperar, especialmente porque el comportamiento predeterminado para Enum.toString() es devolver name().

Personalmente creo que la mejor opción es (2).

Ahora tienes un problema en que está definiendo valores que no representan nombres de instancia de vailid en Java (es decir, utilizando un guion). Así que sus opciones son:

  • Cambie sus datos;
  • Persisten los campos de cadena y los convierten implícitamente a o desde enumeraciones en sus objetos; o
  • Use extensiones no estándar como TypeConverters.

Las haría en ese orden (primero a último) como un orden de preferencia.

Alguien sugirió el convertidor de Oracle TopLink, pero probablemente esté utilizando Toplink Essentials, siendo la implementación de referencia JPA 1.0, que es un subconjunto del producto comercial Oracle Toplink.

Como otra sugerencia, recomiendo encarecidamente cambiar a EclipseLink. Es una implementación mucho más completa que Toplink Essentials y Eclipselink será la implementación de referencia de JPA 2.0 cuando se lance (esperado por JavaOne a mediados del próximo año).

 23
Author: cletus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-23 06:30:15
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;
    }
}

No se como hacer las asignaciones en el lado TopLink anotado de las cosas sin embargo.

 5
Author: JeeBee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-09 14:29:11

No conozco los aspectos internos de toplink, pero mi conjetura educada es la siguiente: utiliza la calificación.Método valueOf (String s) para mapear en la otra dirección. no es posible anular valueOf (), por lo que debe atenerse a la convención de nomenclatura de java, para permitir un método valueOf correcto.

public enum Rating {

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

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

GetRating produce la calificación "legible por humanos". tenga en cuenta que el chanracter "-" no está permitido en el identificador de enumeración.

Por supuesto, tendrá que almacenar los valores en la base de datos como NC_17.

 2
Author: Andreas Petersson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-09 13:18:43

El problema es, creo, que JPA nunca se inició con la idea en mente de que podríamos tener un Esquema preexistente complejo ya en su lugar.

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

  1. La limitación de usar name() y ordinal(). ¿Por qué no marcar un getter con @Id, como lo hacemos con @Entity?
  2. Las enumeraciones suelen tener representación en la base de datos para permitir la asociación con todo tipo de metadatos, incluyendo nombre, un nombre descriptivo, tal vez algo con localización, etc. Necesitamos la facilidad de uso de una Enumeración combinada con la flexibilidad de una Entidad.

Ayuda a mi causa y vota sobre JPA_SPEC-47

 1
Author: YoYo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-23 18:30:41

¿Qué pasa con esto

public String getRating{  
   return rating.toString();
}

pubic void setRating(String rating){  
   //parse rating string to rating enum
   //JPA will use this getter to set the values when getting data from DB   
}  

@Transient  
public Rating getRatingValue(){  
   return rating;
}

@Transient  
public Rating setRatingValue(Rating rating){  
   this.rating = rating;
}

Con esto se usan las clasificaciones como Cadena tanto en la base de datos como en la entidad, pero se usa la enumeración para todo lo demás.

 0
Author: Mg.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-09 14:05:14

Use esta anotación

@Column(columnDefinition="ENUM('User', 'Admin')")
 0
Author: Selvaraj Muthiah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-04 08:29:25

Enumeración public enum ParentalControlLevelsEnum { U("U"), PG("PG"), _12("12"), _15("15"), _18("18");

private final String value;

ParentalControlLevelsEnum(final String value) {
    this.value = value;
}

public String getValue() {
    return value;
}

public static ParentalControlLevelsEnum fromString(final String value) {
    for (ParentalControlLevelsEnum level : ParentalControlLevelsEnum.values()) {
        if (level.getValue().equalsIgnoreCase(value)) {
            return level;
        }
    }
    return null;
}

}

Comparar - > Enumeración

Public class RatingComparator implementa Comparator {

public int compare(final ParentalControlLevelsEnum o1, final ParentalControlLevelsEnum o2) {
    if (o1.ordinal() < o2.ordinal()) {
        return -1;
    } else {
        return 1;
    }
}

}

 0
Author: Jayen Chondigara,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-14 16:53:01

Resuelto!!! Donde encontré la respuesta: http://programming.itags.org/development-tools/65254 /

Brevemente, la conversión busca el nombre de enum, no el valor del atributo 'rating'. En su caso: Si tiene en la base de datos valores "NC-17", necesita tener en su enumeración:

Clasificación enum {
(...)
NC-17 ( "NC-17" );
(...)

 -1
Author: pingpong,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-07 14:32:05