Pregunta

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, utilizaron 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 de Java para asignar la restricción al mundo de objetos. Pero no es posible simplemente tomar los valores permitidos debido al carácter especial en & Quot; PG-13 & Quot; 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 - & Gt; la enumeración no funciona. Recibo la siguiente excepción:

  

[Advertencia de enlace superior]: 2008.12.09   01: 30: 57.434 - ServerSession (4729123) - Excepción [TOPLINK-116] (Oracle   TopLink Essentials - 2.0.1 (compilación b09d-fcs (06/12/2007)):   oracle.toplink.essentials.exceptions.DescriptorException Exception   Descripción: no se proporciona ningún valor de conversión para el valor [NC-17] en   campo [CLASIFICACIÓN DE PELÍCULA]. Cartografía:   oracle.toplink.essentials.mappings.DirectToFieldMapping [rating - > FILM.RATING]   Descriptor: Descriptor relacional (de.fhw.nsdb.entities.Film - & Gt;   [DatabaseTable (FILM)])

aplausos

timo

¿Fue útil?

Solución

Parece que necesita agregar soporte para un tipo personalizado:

Ampliación de OracleAS TopLink para admitir conversiones de tipo personalizado

Otros consejos

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

@Enumerated(EnumType.ORDINAL)

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

  1. Almacénelos como un número igual a Enum.ordinal(), lo cual es una idea terrible (en mi humilde opinión); o
  2. Almacénelos 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 tiene el problema de que está definiendo valores que no representan nombres de instancia no válidos en Java (es decir, utilizando un guión). Entonces sus opciones son:

  • Cambie sus datos;
  • Persiga los campos de cadena e implícitamente conviértalos ao desde enumeraciones en sus objetos; o
  • Use extensiones no estándar como TypeConverters.

Los haría en ese orden (primero al último) como orden de preferencia.

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

Como otra sugerencia, le recomiendo 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 (esperada por JavaOne a mediados del próximo año).

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

Sin embargo, no sé cómo hacer las asignaciones en el lado anotado de TopLink de las cosas.

No conozco aspectos internos de toplink, pero mi conjetura es la siguiente: utiliza el método Rating.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 el & "; legible por humanos &"; clasificación. tenga en cuenta que " - " el carácter no está permitido en el identificador de enumeración.

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

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

Creo que hay dos defectos principales que resultan de esto, específicos de Enum:

  1. La limitación de usar name () y ordinal (). ¿Por qué no simplemente marcar un getter con @Id, como lo hacemos con @Entity?
  2. Los Enum suelen tener representación en la base de datos para permitir la asociación con todo tipo de metadatos, incluido 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 .

Ayude a mi causa y vote sobre JPA_SPEC-47

¿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, usa las calificaciones como String tanto en su base de datos como en su entidad, pero usa la enumeración para todo lo demás.

usa esta anotación

@Column(columnDefinition="ENUM('User', 'Admin')")

Enum enumeración pública ParentalControlLevelsEnum {     U (& Quot; U & Quot;), PG (& Quot; PG & Quot;), _12 (& Quot; 12 & Quot;), _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 - > Enum

public class RatingComparator implementa Comparator {

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

}

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 los valores de db & Quot; NC-17 & Quot ;, necesita tener en su enumeración:

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top