Я ищу различные способы отображения перечисления с использованием JPA. Я особенно хочу установить целочисленное значение каждой записи enum и сохранить только целочисленное значение.
@Entity
@Table(name = "AUTHORITY_")
public class Authority implements Serializable {
public enum Right {
READ(100), WRITE(200), EDITOR (300);
private int value;
Right(int value) { this.value = value; }
public int getValue() { return value; }
};
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "AUTHORITY_ID")
private Long id;
// the enum to map :
private Right right;
}
Простым решением является использование аннотации Enumerated с EnumType.ORDINAL:
@Column(name = "RIGHT")
@Enumerated(EnumType.ORDINAL)
private Right right;
Но в этом случае JPA отображает индекс перечисления (0,1,2), а не значение, которое я хочу (100,200,300).
Че два решения, которые я нашел, не кажутся простыми ...
Первое решение
Предлагаемое здесь решение использует @PrePersist и @PostLoad, чтобы преобразовать перечисление в другое поле и пометить поле перечисления как переходное:
@Basic
private int intValueForAnEnum;
@PrePersist
void populateDBFields() {
intValueForAnEnum = right.getValue();
}
@PostLoad
void populateTransientFields() {
right = Right.valueOf(intValueForAnEnum);
}
Второе решение
Второе решение, предложенное здесь, предложило универсальный объект преобразования, но все еще кажется тяжелым и ориентированным на спящий режим (похоже, @Type не существует в Java EE):
@Type(
type = "org.appfuse.tutorial.commons.hibernate.GenericEnumUserType",
parameters = {
@Parameter(
name = "enumClass",
value = "Authority$Right"),
@Parameter(
name = "identifierMethod",
value = "toInt"),
@Parameter(
name = "valueOfMethod",
value = "fromInt")
}
)
Есть ли другие решения?
У меня есть несколько идей, но я не знаю, существуют ли они в JPA:
- использовать методы setter и getter правого члена класса Authority при загрузке и сохранении объекта Authority
- эквивалентной идеей было бы рассказать JPA, каковы методы Right enum для преобразования enum в int и int в enum
- Поскольку я использую Spring, есть ли способ сказать JPA использовать определенный конвертер (RightEditor)?
Ответы:
Для версий более ранних, чем JPA 2.1, JPA предоставляет только два способа работы с перечислениями: по их
name
или по ихordinal
. И стандартный JPA не поддерживает пользовательские типы. Так:UserType
, EclipseLinkConverter
и т. Д.). (второе решение). ~ или ~int
значение ~ или ~Я проиллюстрирую последний вариант (это базовая реализация, настройте его по мере необходимости):
источник
Теперь это возможно с JPA 2.1:
Дальнейшие подробности:
источник
@Converter
, ноenum
должны быть обработаны более элегантно из коробки!AttributeConverter
НО, цитируется некоторый код, который ничего не делает и не отвечает на ОП.AttributeConverter
@Convert(converter = Converter.class) @Enumerated(EnumType.STRING)
Из JPA 2.1 вы можете использовать AttributeConverter .
Создайте перечислимый класс следующим образом:
И создайте конвертер вот так:
На сущности вам просто нужно:
@Converter(autoApply = true)
Ваша удача может варьироваться в зависимости от контейнера, но проверено на работу с Wildfly 8.1.0. Если это не работает, вы можете добавить@Convert(converter = NodeTypeConverter.class)
столбец класса сущности.источник
Наилучшим подходом будет сопоставление уникального идентификатора с каждым типом перечисления, что позволит избежать ошибок ORDINAL и STRING. Посмотрите этот пост, в котором изложены 5 способов сопоставления перечисления.
Взяты из ссылки выше:
1 & 2. Использование @Enumerated
В настоящее время существует 2 способа отображения перечислений внутри объектов JPA с помощью аннотации @Enumerated. К сожалению, и EnumType.STRING, и EnumType.ORDINAL имеют свои ограничения.
Если вы используете EnumType.String, то переименование одного из типов перечислений приведет к тому, что значение перечисления будет не синхронизировано со значениями, сохраненными в базе данных. Если вы используете EnumType.ORDINAL, то удаление или изменение порядка типов в вашем перечислении приведет к тому, что значения, сохраненные в базе данных, будут сопоставлены с неправильными типами перечислений.
Оба эти варианта хрупки. Если перечисление будет изменено без выполнения миграции базы данных, вы можете поставить под угрозу целостность ваших данных.
3. Обратные вызовы жизненного цикла
Возможное решение - использовать аннотации обратного вызова жизненного цикла JPA, @PrePersist и @PostLoad. Это кажется довольно уродливым, поскольку теперь у вас будет две переменные в вашей сущности. Один отображает значение, хранящееся в базе данных, а другой - фактическое перечисление.
4. Назначение уникального идентификатора каждому типу перечисления
Предпочтительным решением является сопоставление вашего перечисления с фиксированным значением или идентификатором, определенным в перечислении. Отображение на предопределенное фиксированное значение делает ваш код более надежным. Любые изменения порядка типов перечислений или рефакторинга имен не будут вызывать каких-либо неблагоприятных последствий.
5. Использование Java EE7 @Convert
Если вы используете JPA 2.1, у вас есть возможность использовать новую аннотацию @Convert. Это требует создания класса конвертера с аннотацией @Converter, внутри которого вы будете определять, какие значения сохраняются в базе данных для каждого типа перечисления. Внутри вашей сущности вы можете аннотировать свой enum с помощью @Convert.
Мои предпочтения: (номер 4)
Причина, по которой я предпочитаю определять свои идентификаторы в перечислении, а не использовать конвертер, заключается в хорошей инкапсуляции. Только тип enum должен знать его идентификатор, и только объект должен знать, как он отображает enum в базу данных.
Смотрите оригинальный пост для примера кода.
источник
Проблема, я думаю, в том, что JPA никогда не задумывался с мыслью о том, что у нас уже может быть сложная существующая схема.
Я думаю, что это связано с двумя основными недостатками, характерными для Enum:
Помоги моему делу и проголосуй за JPA_SPEC-47
Разве это не было бы более элегантно, чем использование @Converter для решения проблемы?
источник
Возможно закрыть связанный код Паскаля
источник
Я бы сделал следующее:
Объявите отдельно перечисление в собственном файле:
Объявить новый объект JPA с именем Right
Вам также понадобится конвертер для получения этих значений (только JPA 2.1, и есть проблема, которую я не буду обсуждать здесь с этими enum'ами, которые будут напрямую сохраняться с помощью конвертера, так что это будет только односторонняя дорога)
Орган управления:
Поступая таким образом, вы не можете установить непосредственно поле enum. Однако вы можете установить поле Right в Authority, используя
И если вам нужно сравнить, вы можете использовать:
Это довольно круто, я думаю. Это не совсем правильно, поскольку конвертер не предназначен для использования с enum. На самом деле, в документации сказано, что никогда не используйте ее для этой цели, вместо этого вы должны использовать аннотацию @Enumerated. Проблема в том, что существует только два типа enum: ORDINAL или STRING, но ORDINAL хитрый и небезопасный.
Однако, если это не удовлетворяет вас, вы можете сделать что-то более хакерское и более простое (или нет).
Посмотрим.
The RightEnum:
и орган власти
В этой второй идее, это не идеальная ситуация, так как мы взломали порядковый атрибут, но это гораздо меньшая кодировка.
Я думаю, что спецификация JPA должна включать EnumType.ID, где поле значения enum должно быть аннотировано с помощью своего рода аннотации @EnumId.
источник
Мое собственное решение для решения этого вида отображения Enum JPA заключается в следующем.
Шаг 1 - Напишите следующий интерфейс, который мы будем использовать для всех перечислений, которые мы хотим отобразить в столбец БД:
Шаг 2 - Реализуйте пользовательский универсальный конвертер JPA следующим образом:
Этот класс преобразует значение enum
E
в поле базы данных типаT
(напримерString
), используяgetDbVal()
on enumE
, и наоборот.Шаг 3 - Пусть оригинальный enum реализует интерфейс, который мы определили на шаге 1:
Шаг 4 - Расширьте конвертер шага 2 для
Right
перечисления шага 3:Шаг 5 - Последний шаг - аннотировать поле в сущности следующим образом:
Вывод
ИМХО, это самое чистое и элегантное решение, если у вас есть много перечислений для отображения, и вы хотите использовать определенное поле самого перечисления в качестве значения сопоставления.
Для всех остальных перечислений в вашем проекте, которые требуют аналогичной логики сопоставления, вам нужно только повторить шаги с 3 по 5, то есть:
IDbValue
в вашем перечислении;EnumDbValueConverter
всего тремя строками кода (вы также можете делать это внутри вашей сущности, чтобы избежать создания отдельного класса);@Convert
изjavax.persistence
пакета.Надеюсь это поможет.
источник
первый случай (
EnumType.ORDINAL
)второй случай (
EnumType.STRING
)источник