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

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

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

4b9b3361

Ответ 1

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

@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";
    }   
}

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

Ответ 2

Я просто хотел улучшить отличный ответ @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 {
    …
}

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

Ответ 3

Нет, к сожалению, вы не можете.

Если вы попытаетесь использовать перечисление как значение дискриминатора, вы получите исключение типа 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
}

Ответ 4

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

  • значение дискриминатора должно быть типа String
  • значение дискриминатора должно быть константой времени компиляции, то есть возвращаемые значения из методов перечисления не допускаются.

Ответ 5

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

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

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

DiscriminatorType.STRING
DiscriminatorType.CHAR
DiscriminatorType.INTEGER

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

Ответ 6

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

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

Ответ 7

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

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

@Entity
@DiscriminatorValue(Square.DISCRIMINATOR_VALUE)
public class Square extends Parent  {

    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;
}

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