Как использовать enums с JPA

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

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 () направление enum -> String отлично работает, но String -> enum не работает. Я получаю следующее исключение:

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

ура

Timo

Похоже, вам нужно добавить поддержку для настраиваемого типа:

Расширение OracleAS TopLink для поддержки конверсий пользовательского типа

вы пытались сохранить порядковое значение. Хранить строковое значение отлично, если у вас нет привязанного String к значению:

 @Enumerated(EnumType.ORDINAL) 

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

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

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

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

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

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

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

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

 public enum Rating { UNRATED ( "" ), G ( "G" ), PG ( "PG" ), PG13 ( "PG-13" ), R ( "R" ), NC17 ( "NC-17" ); private String rating; private static Map ratings = new HashMap(); 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.

я не знаю внутренних компонентов 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("_","-");; } } в public enum Rating { UNRATED, G, PG, PG_13 , R , NC_17 ; public String getRating() { return name().replace("_","-");; } } 

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

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

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

Я думаю, что из-за этого возникают два основных недостатка, характерных для Enum:

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

Помогите моему делу и проголосуйте за JPA_SPEC-47

Как насчет этого

 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 как на вашей БД, так и на сущности, но используйте перечисление для всего остального.

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

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

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

}

сравнить -> Enum

public class RatingComparator реализует Comparator {

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

}

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

Вкратце, конверсия ищет имя enums, а не значение атрибута «рейтинг». В вашем случае: если у вас есть значения db «NC-17», вы должны иметь в своем перечислении:

enum Рейтинг {
(…)
NC-17 («NC-17»);
(…)

  • Поддержка JPA для Java 8 новых API даты и времени
  • Установка значений по умолчанию для столбцов в JPA
  • Java - JPA - Генераторы - @SequenceGenerator
  • Как хранить дату Java в Mysql datetime ...?
  • Нарушение спящего режима с помощью orphanRemoval
  • JPA Несколько встроенных полей
  • JPA @ManyToOne с CascadeType.ALL
  • Может javax.persistence.Query.getResultList () вернуть null?
  • JPA: ПРИСОЕДИНЯЙТЕСЬ в JPQL
  • В чем разница между JPA и Hibernate?
  • Включение зависимостей Spring в объект EntityListener
  • Давайте будем гением компьютера.