Как использовать 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 не работает. Я получаю следующее исключение:
- @Basic (необязательно = false) vs @Column (nullable = false) в JPA
- Поставщик сущностей для EntityManager не указан
- Как настроить JPA для тестирования в Maven
- Данные LOAD и CACHE с областью приложения с @Singleton и @Stateless
- В чем разница между интерфейсами CrudRepository и JpaRepository в Spring Data JPA?
[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
- Как настроить имя схемы по умолчанию в конфигурации JPA?
- Hibernate: MyInterceptor # onFlushDirty никогда не вызывается
- Spring-Data-Jpa Repository - Подчеркивание имени столбца объекта
- setMaxResults для annotations Spring-Data-JPA?
- java.lang.NoSuchMethodError: javax.persistence.spi.PersistenceUnitInfo.getValidationMode () Ljavax / persistence / ValidationMode;
- Недействительность сессии JPA EntityManager
- Как ввести ограничение нескольких столбцов с помощью аннотаций JPA?
- Указывает ли спецификация JPA ссылки на столбцы не первичного ключа?
Похоже, вам нужно добавить поддержку для настраиваемого типа:
Расширение OracleAS TopLink для поддержки конверсий пользовательского типа
вы пытались сохранить порядковое значение. Хранить строковое значение отлично, если у вас нет привязанного String к значению:
@Enumerated(EnumType.ORDINAL)
У вас здесь проблема, и это ограниченные возможности JPA, когда дело касается обработки перечислений. С enumsми у вас есть два варианта:
- Храните их как число, равное
Enum.ordinal()
, что является ужасной идеей (imho); или - Сохраните их как строку, равную
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:
- Ограничение использования имени () и порядкового (). Почему бы просто не отметить геттер с помощью @Id, как мы это делаем с @Entity?
- Обычно у 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»);
(…)