У меня есть база данных системы проката фильмов. У каждого фильма есть атрибут рейтинга. В 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)--Exception [TOPLINK-116] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.DescriptorException Exception Description: No conversion value provided for the value [NC-17] in field [FILM.RATING]. Mapping: oracle.toplink.essentials.mappings.DirectToFieldMapping[rating-->FILM.RATING] Descriptor: RelationalDescriptor(de.fhw.nsdb.entities.Film --> [DatabaseTable(FILM)])
ваше здоровье
тимо
Вам понадобится статическая хэш-карта ваших перечислений внутри перечисления и статический метод getByRating.




Я не знаю внутреннего устройства 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 выдает рейтинг, "удобочитаемый". обратите внимание, что символ «-» не допускается в идентификаторе перечисления.
конечно, вам нужно будет сохранить значения в БД как NC_17.
Как насчет этого
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 как в своей БД, так и в сущности, но используйте перечисление для всего остального.
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.
Похоже, вам нужно добавить поддержку специального типа:
Расширение OracleAS TopLink для поддержки преобразования пользовательских типов
У вас есть проблема, и это ограниченные возможности JPA, когда дело доходит до обработки перечислений. С перечислениями у вас есть два варианта:
Enum.ordinal(), что является ужасной идеей (imho); или жеEnum.name(). Примечание:, а не toString(), как вы могли ожидать, тем более что поведение по умолчанию для Enum.toString() - возвращать name().Лично я считаю, что лучший вариант - (2).
Теперь у вас есть проблема в том, что вы определяете значения, которые не представляют действительные имена экземпляров в Java (а именно, используя дефис). Итак, ваш выбор:
Я бы сделал их в таком порядке (от первого до последнего) в порядке предпочтения.
Кто-то предложил преобразователь Oracle TopLink, но вы, вероятно, используете Toplink Essentials, являющуюся эталонной реализацией JPA 1.0, которая является подмножеством коммерческого продукта Oracle Toplink.
В качестве еще одного предложения я настоятельно рекомендую перейти на EclipseLink. Это гораздо более полная реализация, чем Toplink Essentials, и Eclipselink будет эталонной реализацией JPA 2.0 после выпуска (ожидается JavaOne в середине следующего года).
вы пытались сохранить порядковый номер. Сохранение строкового значения отлично работает, если у вас нет связанной строки со значением:
@Enumerated(EnumType.ORDINAL)
Я не могу поверить, что у этого нет больше голосов, учитывая, что @Enumerated - это часть JPA1. Лично я думаю, что EnumType.STRING был бы лучше по причинам, указанным Клетусом.
@electrotype Да, технически это работает, но это далеко не лучшая практика. Хранить перечисление по его числовому значению - УЖАСНАЯ идея. Очень простые изменения, которые не изменяют функциональность вашего кода, могут легко сделать все ваши постоянные данные недействительными. Например, изменение порядка значений перечисления или добавление нового значения в начало списка.
@ spaaarky21 Я согласен с этим в большинстве случаев, однако, сохраняя значения, которые никогда не изменятся, например, будние дни, вы также можете сохранить порядковый номер (и сохранить его как TINYINT (1) в случае будних дней).
@JelleBlaauw Я бы делал это везде, как на конвенциях. Даже день недели может быть проблематичным. В неделе будет только семь дней, но разные библиотеки относятся к ним по-разному. Основные различия заключаются в том, начинается ли неделя с воскресенья (например, Java Calendar) или понедельника (например, Java 8 DayOfWeek), и основаны ли значения на 0 (например, порядковые номера перечисления) или на основе 1 (например, константы Joda). Представьте, что вы меняете свой проект с одной библиотеки на другую, а разработчик из лучших побуждений меняет порядок значений перечисления DayOfWeek, чтобы они соответствовали друг другу.
используйте эту аннотацию
@Column(columnDefinition = "ENUM('User', 'Admin')")
Я почти уверен, что это не сработает. Сообщение JPA о том, как определяется столбец, не даст указание искать значение для базы данных другим способом. В Hibernate определение столбца помогает устранить несоответствия типов - например, если Hibernate считает, что для столбца следует использовать один тип BLOB-объекта, а в базе данных - другой.
Решено !!! Где нашел ответ: http://programming.itags.org/development-tools/65254/
Вкратце, преобразование ищет имя перечисления, а не значение атрибута "рейтинг". В вашем случае: если у вас есть значения db «NC-17», вам необходимо иметь в своем перечислении:
enum Рейтинг {
(...)
NC-17 ("NC-17");
(...)
Голосовать против, потому что - не разрешено в именах перечислений, как указано в OP.
Проблема, я думаю, в том, что JPA никогда не создавалась с мыслью о том, что у нас уже может быть сложная уже существующая схема.
Я думаю, что это связано с двумя основными недостатками, характерными для Enum:
Помогите моему делу и проголосуйте за JPA_SPEC-47
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
открытый класс RatingComparator реализует Comparator {
public int compare(final ParentalControlLevelsEnum o1, final ParentalControlLevelsEnum o2) {
if (o1.ordinal() < o2.ordinal()) {
return -1;
} else {
return 1;
}
}
}
Используя ваш существующий enum Rating. Вы можете использовать AttributeCoverters.
@Converter(autoApply = true)
public class RatingConverter implements AttributeConverter<Rating, String> {
@Override
public String convertToDatabaseColumn(Rating rating) {
if (rating == null) {
return null;
}
return rating.toString();
}
@Override
public Rating convertToEntityAttribute(String code) {
if (code == null) {
return null;
}
return Stream.of(Rating.values())
.filter(c -> c.toString().equals(code))
.findFirst()
.orElseThrow(IllegalArgumentException::new);
}
}
вы намеренно пропустили атрибут @Column для поля? Без него было бы трудно что-либо выдержать ...