2008-12-09 5 views
34

У меня есть существующая база данных системы проката фильмов. Каждый фильм имеет атрибут рейтинга. В SQL они использовали ограничение для ограничения допустимых значений этого атрибута.Как использовать перечисления с JPA

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

Я думаю, было бы неплохо использовать перечисление Java для сопоставления ограничения в мир объектов. Но нельзя просто принимать разрешенные значения из-за специального символа в «PG-13» и «NC-17». Таким образом, я осуществил следующее перечисление:

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

С помощью метода ToString() направление перечисления -> Строка работает отлично, но String -> перечисление не работает. Я получаю следующее исключение:

[TopLink Warning]: 2008.12.09 01: 30: 57.434 - ServerSession (4729123) - Исключение [TOPLINK-116] (Oracle TOPLINK Основы - 2.0.1 (Построить b09d-fcs (12/06/2007)): ​​ oracle.toplink.essentials.exceptions.DescriptorException Exception Описание: Значение преобразования [NC-17] в поле отсутствует. [FILM.RATING]. Отображение: oracle.toplink.essentials.mappings.DirectToFieldMapping [рейтинг -> FILM.RATING] Descriptor: RelationalDescriptor (de.fhw.nsdb.entities.Film -> [DatabaseTable (ФИЛЬМ)])

веселит

Timo

+0

вы опустить атрибут @Column для поля намеренно? Без него было бы трудно сохранить что-либо ... – Yuval 2008-12-09 12:53:12

+0

Вам нужен статический хэш-файл из ваших перечислений в вашем перечислении, а также статический метод getByRating – JeeBee 2008-12-09 14:06:07

ответ

2

я не знаю внутренностей TopLink, но моя догадка состоит в следующем: он использует метод Rating.valueOf (String s) для отображения в другом направлении. невозможно переопределить valueOf(), поэтому вы должны придерживаться соглашения об именах java, чтобы разрешить правильный метод valueOf.

public enum Rating { 

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

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

getRating производит «удобочитаемый» рейтинг. обратите внимание, что chanracter «-» не разрешен в идентификаторе перечисления.

Конечно, вам нужно будет сохранить значения в DB как NC_17.

0

Что об этом

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

с этим использовать рейтинги как String, как на вашей БД и лица, но использовать перечисление для всего остального.

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

Я не знаю, как выполнять сопоставления в аннотированной стороне TopLink.

23

У вас есть проблема здесь, и это ограниченные возможности JPA, когда речь идет об обработке перечислений. С перечислениями у вас есть два варианта:

  1. Храните их как число, равное Enum.ordinal(), что является ужасной идеей (imho); или
  2. Сохраните их как строку, равную Enum.name().Примечание: не toString(), как и следовало ожидать, тем более, что по умолчанию для Enum.toString() должно быть возвращено name().

Лично я считаю, что лучшим вариантом является (2).

Теперь у вас есть проблема в том, что вы определяете значения, которые не представляют имена экземпляров vailid в Java (а именно, используя дефис). Таким образом, ваш выбор:

  • Изменение ваших данных;
  • Персистировать строковые поля и неявно преобразовывать их в или из перечислений в ваших объектах; или
  • Используйте нестандартные расширения, такие как TypeConverters.

Я бы сделал их в таком порядке (от первого до последнего) в порядке предпочтения.

Кто-то предложил конвертер Oracle TopLink, но вы, вероятно, используете Toplink Essentials, являющийся ссылочной версией JPA 1.0, которая является подмножеством коммерческого продукта Oracle Toplink.

В качестве другого предложения я настоятельно рекомендую перейти на EclipseLink. Это гораздо более полная реализация, чем Toplink Essentials, а Eclipselink станет эталонной реализацией JPA 2.0 при выпуске (ожидается в середине следующего года в JavaOne).

28

Вы пытались сохранить порядковое значение. Сохранение строковое значение отлично работает, если вы не имеете ассоциированную строку со значением:

@Enumerated(EnumType.ORDINAL) 
+3

Я не могу поверить, что у него больше не болит, учитывая, что @ Перечислено [часть JPA1] (http://docs.oracle.com/javaee/5/api/javax/persistence/Enumerated.html). Лично я думаю, что `EnumType.STRING` было бы лучше по причинам, изложенным cletus. – Powerlord 2012-05-15 19:39:16

+0

Отлично работает для меня, спасибо! – electrotype 2012-06-29 23:50:26

0

используют эту аннотацию

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

Решено !!! Где я нашел ответ: http://programming.itags.org/development-tools/65254/

Вкратце, конверсия ищет имя перечисления, а не значение атрибута 'рейтинг'. В вашем случае: Если у вас есть в значение БД "NC-17", вы должны иметь в своем перечислении:

перечисления Рейтинга {
(...)
NC-17 («NC -17 ");
(...)

1

Проблема заключается в том, что проблема JPA никогда не была воспринята с учетом того, что у нас уже есть сложная существовавшая ранее схема.

Я считаю, что есть два основных недостатка, возникающие в результате этого, специфичные для Enum:

  1. Ограничение используя имя() и порядковый номер(). Почему бы просто не отметить геттер с помощью @Id, как мы это делаем с @Entity?
  2. Enum's обычно имеет представление в базе данных, чтобы разрешить ассоциацию со всеми видами метаданных, включая собственное имя, описательное имя, возможно, что-то с локализацией и т. Д. Нам нужно просто использовать Enum в сочетании с гибкостью Entity ,

Помощь моя причина и голосовать JPA_SPEC-47

0

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

}

сравнить -> Enum

общественного класса RatingComparator реализует компаратор {

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

}