Можно ли использовать перечисление как значение дискриминатора при использовании стратегии наследования SINGLE_TABLE?
Стратегия наследования SINGLE_TABLE с использованием перечислений в качестве значения дискриминатора
Ответ 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 может "перечислять" разные классы из разных источников - в зависимости от контекста, в котором он используется.