Стратегия наследования SINGLE_TABLE с использованием перечислений в качестве значения дискриминатора

Можно ли использовать перечисление в качестве значения дискриминатора при использовании стратегии наследования SINGLE_TABLE?


person Bauna    schedule 03.09.2010    source источник


Ответы (7)


Если вы пытаетесь не дублировать значения дискриминатора, существует простой обходной путь.

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="FREQUENCY",
    discriminatorType=DiscriminatorType.STRING
)    
public abstract class Event  {
}

@Entity
@DiscriminatorValue(value=Frequency.Values.WEEKLY)
public class WeeklyEvent extends Event {
    …
}

public enum Frequency {
    DAILY(Values.DAILY),
    WEEKLY(Values.WEEKLY),
    MONTHLY(Values.MONTHLY);

    private String value;

    …

    public static class Values {
        public static final String DAILY = "D";
        public static final String WEEKLY = "W";
        public static final String MONTHLY = "M";
    }   
}

Не очень элегантно, но лучше, чем хранить значения в нескольких местах.

person Asa    schedule 24.04.2013

Я просто хотел улучшить отличный ответ @asa об обходном пути. Обычно нам нравится использовать столбец дискриминатора в качестве атрибута абстрактного класса и, конечно же, отображать с помощью enum. Мы все еще можем использовать решение, упомянутое выше, и обеспечить некоторую согласованность между именами enum (используемыми для отображения столбца) и значениями String (используемыми в качестве значений дискриминатора). Вот мое предложение:

public enum ELanguage {
  JAVA(Values.JAVA), GROOVY(Values.GROOVY);

  private ELanguage (String val) {
     // force equality between name of enum instance, and value of constant
     if (!this.name().equals(val))
        throw new IllegalArgumentException("Incorrect use of ELanguage");
  }

  public static class Values {
     public static final String JAVA= "JAVA";
     public static final String GROOVY= "GROOVY";
  }
}

А для сущностей вот код:

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="LANGUAGE_TYPE", discriminatorType=DiscriminatorType.STRING)    
public abstract class Snippet {
   // update/insert is managed by discriminator mechanics
   @Column(name = "LANGUAGE_TYPE", nullable = false, insertable = false, updatable = false) 
   @Enumerated(EnumType.STRING)
   public ELanguage languageType
}

@Entity
@DiscriminatorValue(value=ELanguage.Values.JAVA)
public class JavaSnippet extends Snippet {
    …
}

Все еще не идеально, но немного лучше, я думаю.

person Genu    schedule 03.11.2014

Нет, к сожалению, нельзя.

Если вы попытаетесь использовать перечисление в качестве значения дискриминатора, вы получите исключение Type Mismatch («невозможно преобразовать MyEnum в String»), поскольку разрешены только типы дискриминатора String, Char и Integer. Затем я попытался использовать имя и порядковый номер перечисления в сочетании с DiscriminatorType.STRING и DiscriminatorType.INTEGER соответственно. Но и это не сработало, так как аннотация @DiscriminatorValue (как и любая другая) требует постоянного выражения:

Это не работает:

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="FREQUENCY",
    discriminatorType=DiscriminatorType.STRING
)    
public abstract class Event  {}

@Entity
@DiscriminatorValue(value=Frequency.WEEKLY.name())
public class WeeklyEvent extends Event {
    // Exception: The value for annotation attribute DiscriminatorValue.value must be a constant expression
}

Тоже не работает:

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="FREQUENCY",
    discriminatorType=DiscriminatorType.INTEGER
) 
public abstract class Event  {}

@Entity
@DiscriminatorValue(value=Frequency.WEEKLY.ordinal())
public class WeeklyEvent extends Event {
    // Exception: The value for annotation attribute DiscriminatorValue.value must be a constant expression
}
person marciowerner    schedule 17.10.2012

Я бы предложил инвертировать отношение: определить значение дискриминатора как константу в сущности, а затем обернуть его в перечисление:

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
    name = "FIELD_TYPE",
    discriminatorType = DiscriminatorType.STRING
)
public class Shape {}

@Entity
@DiscriminatorValue(Square.DISCRIMINATOR_VALUE)
public class Square extends Shape {
    public static final String DISCRIMINATOR_VALUE = "SQUARE";
}

@Entity
@DiscriminatorValue(Circle.DISCRIMINATOR_VALUE)
public class Circle extends Shape {
    public static final String DISCRIMINATOR_VALUE = "CIRCLE";
}

@AllArgsConstructor
public enum FieldType {
    SHAPE(Shape.DISCRIMINATOR_VALUE),
    CIRCLE(Circle.DISCRIMINATOR_VALUE);

    @Getter
    private final String discriminatorValue;
}

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

person Albert Gevorgyan    schedule 21.06.2019

Насколько мне известно, это невозможно с аннотациями:

  • значение дискриминатора должно быть типа String
  • значение дискриминатора должно быть константой времени компиляции, т. е. возвращаемые значения из методов в перечислениях не допускаются.
person Pascal Thivent    schedule 03.09.2010
comment
JPA 2.1 не изменил это? - person Adriano Machado; 08.08.2013
comment
Это не JPA, язык Java не позволяет вызывать методы для значений аннотаций, см.: docs.oracle.com/javase/specs/jls/se7/html/jls-9.html#jls-9.7 в 9.7.1, прочитайте список: Тип V совместим по присваиванию (§5.2) с T, и, кроме того:. Таким образом, MyEnum.ENUM_CONST.x() недействителен, MyEnum.ENUM_CONST был бы действителен, но T здесь String. - person TWiStErRob; 08.10.2013

да, когда вы определяете дискриминатор, опция аннотации - это имя и тип дискриминации.

@DiscriminatorColumn (name="MYDISCRIMINATOR", discriminatorType= DiscriminatorType.INTEGER)

из которых DiscriminatorType может быть только:

DiscriminatorType.STRING
DiscriminatorType.CHAR
DiscriminatorType.INTEGER

жаль, что я не видел это вчера, но хорошо. Так оно и есть

person Luis Ramirez-Monterosa    schedule 31.05.2012
comment
И обратите внимание, что DiscriminatorType относится к типу DB, а не к типу Java. DiscriminatorValue должен по-прежнему иметь число в кавычках в виде строки. - person TWiStErRob; 08.10.2013

Вы можете использовать DiscriminatorType.INTEGER и сопоставлять каждый подкласс с @DiscriminatorValue("X"), где X должно быть порядковым значением перечисления (0,1,2,3...).

Это должно быть значением в виде постоянной строки. Вы не можете использовать YourEnum.SOME_VALUE.ordinal(), потому что значения атрибутов аннотаций должны быть константами. Да, это утомительно. Да, это подвержено ошибкам. Но это работает.

person tetsuo    schedule 26.03.2013
comment
Это хакерский обходной путь, однако он работает, я бы поместил огромные комментарии в enum, который используется как DiscriminatorValue! Также то же самое работает с именами перечислений (с DiscriminatorType.String, которое может быть более стабильным, чем порядковый номер. - person TWiStErRob; 08.10.2013