Можно ли использовать перечисление в качестве значения дискриминатора при использовании стратегии наследования SINGLE_TABLE?
Стратегия наследования SINGLE_TABLE с использованием перечислений в качестве значения дискриминатора
Ответы (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";
}
}
Не очень элегантно, но лучше, чем хранить значения в нескольких местах.
Я просто хотел улучшить отличный ответ @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 {
…
}
Все еще не идеально, но немного лучше, я думаю.
Нет, к сожалению, нельзя.
Если вы попытаетесь использовать перечисление в качестве значения дискриминатора, вы получите исключение 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
}
Я бы предложил инвертировать отношение: определить значение дискриминатора как константу в сущности, а затем обернуть его в перечисление:
@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;
}
Помимо устранения дублирования, этот код также менее тесно связан: классы сущностей не зависят от значений перечисления и могут быть добавлены без изменения другого кода; в то время как перечисление может перечислять разные классы из разных источников - в зависимости от контекста, в котором оно используется.
Насколько мне известно, это невозможно с аннотациями:
- значение дискриминатора должно быть типа
String
- значение дискриминатора должно быть константой времени компиляции, т. е. возвращаемые значения из методов в перечислениях не допускаются.
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
жаль, что я не видел это вчера, но хорошо. Так оно и есть
DiscriminatorType
относится к типу DB, а не к типу Java. DiscriminatorValue
должен по-прежнему иметь число в кавычках в виде строки.
- person TWiStErRob; 08.10.2013
Вы можете использовать DiscriminatorType.INTEGER
и сопоставлять каждый подкласс с @DiscriminatorValue("X")
, где X
должно быть порядковым значением перечисления (0,1,2,3...).
Это должно быть значением в виде постоянной строки. Вы не можете использовать YourEnum.SOME_VALUE.ordinal()
, потому что значения атрибутов аннотаций должны быть константами. Да, это утомительно. Да, это подвержено ошибкам. Но это работает.
enum
, который используется как DiscriminatorValue
! Также то же самое работает с именами перечислений (с DiscriminatorType.String
, которое может быть более стабильным, чем порядковый номер.
- person TWiStErRob; 08.10.2013