Подтвердить что ты не робот

Отображение перечислений типов JPA. Лучший подход

Как обычно, типы переименования Java имеют соответствующие коды и описание имени. И классы Java, содержащие такие поля, содержат их как Enum:

public enum MyEnum{
    SOMEINSTANCE(1, "test1"),
    SOMEINSTANCE(2, "test2");

    private final int code;
    private final String name;
    private MyEnum(int code, String name){
        this.code = code;
        this.name = name;
    }
    ... helper getter for code and name
}

@Entity
puclic class EnumHolder{
    private MyEnum myEnum;
}

Я новичок в JPA, но хочу, чтобы таблица "myEnums" выглядела так:

code int not null, name varchar(50) not null)

И в моей таблице enumHolder я хочу иметь поле myEnumCode, которое указывает на таблицу myEnums.

Использование currenlty поддерживается как EnumType.ORDINAL, так и EnumType.STRING. Я полагаю, что это не очень хорошая идея.

И еще вопрос. Как заполнить таблицу myEnums с использованием данных класса Java MyEnum? Как бы вы это сделали? Наилучший подход.

PS: вот решения, которые я могу предложить:

Предположим, что существует таблица MyEnum с code и имя fields. Java MyEnum перечисление, которое описано в вопросе. Таблица enumHolder должна иметь ссылку myEnumCode в поле myEnum.code. Пожалуйста, прокомментируйте решение, если вы не согласны.

@Entity
@Access(AccessType.FIELD)
public class EnumHolder {
    @Id private int id;
    @Transient private MyEnum myEnum;
    …
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public MyEnum getMyEnum() { return MyEnum; }
    public void setMyEnum(MyEnum myEnum) { this.myEnum = myEnum; }

    @Access(AccessType.PROPERTY) @Column(name="myEnumCode")
    protected int getMyEnumForDb() {
        return myEnum.getCode();
    }

    protected void setMyEnumForDb(int enumCode) {
        myEnum = MyEnum.getByCode( enumCode);
    }
…
}

Конечно, здесь есть недостатки. Но на данный момент я не вижу лучшего подхода. Альтернативы с EnumType.ORDINAL и EnumType.STRING, пожалуйста, не предлагайте. Я не хочу писать здесь все проблемы, которые могут существовать с его использованием (в "Эффективной Java" описано использование ординалов). Использование EnumType.STRING Мне не нравится ни одна из причин, почему он не позволяет указывать в базе данных и запрашивать его с db.

Что касается базы данных для заполнения. Я думаю, что нелегко написать script, который очищает таблицу MyEnum, а затем для каждого перечисления Java istance делает вставку в таблицу. И всегда делайте это на этапе развертывания.

4b9b3361

Ответ 1

Лучшим подходом было бы сопоставление уникального идентификатора каждому типу перечисления, что позволило бы избежать ошибок 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, внутри которого вы определяете, какие значения сохраняются в базе данных для каждого типа перечисления. Внутри вашей сущности вы затем аннотировали свое перечисление с помощью @Convert.

Мои предпочтения: (Номер 4)

Причина, по которой я предпочитаю определять свой идентификатор в enum как против использования конвертера, является хорошей инкапсуляцией. Только тип перечисления должен знать свой идентификатор, и только сущность должна знать, как он отображает перечисление в базу данных.

Для примера кода см. оригинал .

Ответ 2

Вы можете добавить перечислимое поле к своей сущности, используя @Enumerated. Кикер здесь состоит в том, что вы хотите иметь свой торт и съесть его тоже - для обеспечения последовательности вам лучше выбрать либо EnumType.ORDINAL, либо EnumType.STRING.

Оба имеют свои плюсы и минусы, которые перечислены на этом сайте. Большая разница, похоже, связана с упорядочением - если у вас установлен EnumType.ORDINAL, при обновлении перечисления возникнут проблемы.

Я бы посоветовал вам переосмыслить, как вы хотите разработать таблицу. Строка и целое по существу хранят одну и ту же информацию - то, что перечисленное значение - и вы можете получить это обратно из базы данных без излишней суеты. Было бы лучше иметь id в качестве первичного ключа в таблице, а затем ваш нумерованный тип либо как строку, либо порядковое значение, принимая во внимание предостережения из ссылки выше.

Ответ 3

@Column(name = "MY_TYPE")
@Enumerated(EnumType.STRING)
private MyType type;

enum MyType {
type_1
type_2
}

затем создайте столбец следующим образом:

TYPE_ID  VARCHAR(20) NOT NULL DEFAULT 'TYPE_1' CHECK (TYPE_ID IN ('TYPE_1', 'TYPE_2')))

это решило мою проблему.