Как получить DiscriminatorValue во время выполнения

У нас есть следующие классы

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) // optional annotation as this is default
@DiscriminatorColumn(name = "apType", discriminatorType = DiscriminatorType.STRING, length = 255)
@DiscriminatorValue("AP")
public class ApplicationProcess {
}

И это

@Entity
@DiscriminatorValue("APS")
public class ApplicationProcessScheme extends ApplicationProcess {
}

Теперь мне нужно знать во время выполнения, является ли ApplicationProcess DiscriminatorValue AP or APS. Поскольку это автоматически обрабатывается jpa, у меня нет возможности получить это значение.

Мы вызываем метод, который принимает ApplicationProcess в качестве параметра, и я хочу избежать использования instanceof для проверки его типа. Было бы круче, если бы я мог сделать что-то вроде

applicationProcess.getApType().equals("AP");

person Shervin Asgari    schedule 09.06.2010    source источник


Ответы (6)


Вы можете сопоставить свой дискриминатор как свойство только для чтения:

public class ApplicationProcess { 

    ...

    @Column(name = "apType", insertable = false, updatable = false)
    private String apType;

}
person axtavt    schedule 09.06.2010
comment
@Shervin: Вы можете, но это не очень хорошая идея, ваша логика не должна полагаться на это. - person Pascal Thivent; 09.06.2010
comment
@Pascal: я думаю, что должен. На стороне xhtml у меня нет возможности в выражении EL сказать instanceof (я думаю). Так что мне это может понадобиться. - person Shervin Asgari; 09.06.2010
comment
@Shervin: мне жаль это говорить, но кажется, что у вас есть серьезный недостаток в вашем дизайне, представление НИКОГДА не должно полагаться на значение дискриминатора (это верно для бизнес-кода, но даже больше для вида). - person Pascal Thivent; 09.06.2010
comment
Если вы не хотите отображать apType, вы можете установить его в @Transient и установить его значение в конструкторе: apType = getClass().getAnnotation(DiscriminatorValue.class).value() - person gamliela; 26.01.2016
comment
Но если вы используете переходный процесс, вы не сможете использовать его в своих запросах JPA. - person Thermech; 28.01.2017
comment
@PascalThivent, просто чтобы добавить к комментарию ... Я понимаю и согласен с тем, что это плохой дизайн, но в настоящее время я отражаю существующую схему данных в Hibernate, поэтому мне нужно, чтобы это были общедоступные данные. Спасибо за решение! - person Jeff Fairley; 16.05.2017
comment
у этого подхода есть один большой недостаток: он не работает должным образом, если вы создаете объект, сохраняете его и пытаетесь использовать только что сохраненный объект. В этом случае apType==null. И, конечно же, ручное обновление apType для этого конкретного случая еще хуже. Решение, предоставленное @Wirus, намного лучше. - person Oleksandr_DJ; 31.10.2018

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

@Transient
public String getDiscriminatorValue(){
    DiscriminatorValue val = this.getClass().getAnnotation( DiscriminatorValue.class );

    return val == null ? null : val.value();
}
person Wirus    schedule 26.11.2012
comment
Примером использования может быть: некоторая общесистемная таблица перевода с типами (страна, язык, пол, ...) и несколькими ключами для каждого типа (pl, uk, us, ...) и значениями (Польша, Великобритания, .. .). Некоторые типы могут использоваться в отношениях @OneToMany, например, страна может использоваться в отношениях @Entity Address, но другие типы могут использоваться только при запросе к базе данных. Итак: базовый класс будет обрабатывать большинство типов, но несколько конкретных будут иметь свой собственный производный класс. - person Arjan; 12.02.2013
comment
для меня проблема с принятым ответом заключалась в том, что объект только что создан и еще не сохранен, дискриминатор отсутствует. В тестовых случаях, когда данные действительно не сохраняются в БД, у меня были проблемы с этим. Это сработало отлично. Спасибо - person Rajani Karuturi; 06.03.2013
comment
Мне это очень нравится. Однако производительность важна в моем текущем случае использования, поэтому мне придется использовать более быстрое решение. Для других подумайте о производительности перед реализацией этого. - person Sam Berry; 03.10.2016
comment
Отличная идея, но при таком использовании я не смогу использовать значение дискриминатора в своем запросе jpa. Например: findByDiscriminatorIn() не будет работать. - person Thermech; 28.01.2017
comment
Это хорошее решение, если вы не используете его для лениво загруженного/прокси-объекта - в этом случае класс будет прокси-классом и не будет содержать аннотацию @DiscriminatorValue. - person jlb; 04.04.2017

Мы вызываем метод, который принимает ApplicationProcess в качестве параметра, и я хочу избежать использования instanceof для проверки его типа. Было бы круче, если бы я мог сделать что-то вроде (...)

Я не думаю, что это было бы круче, мне это кажется хуже, чем вызов instanceOf: если по какой-то причине вы измените значение дискриминатора, это сломает ваш код.

Если вам нужно проверить тип, используйте instanceOf. Уловка с использованием дискриминатора не улучшит ситуацию, она просто сделает ваш код менее надежным.

person Pascal Thivent    schedule 09.06.2010
comment
+1 Полностью согласен. Возможно, применим этот рефакторинг: c2.com/cgi/wiki?ReplaceConditionalWithPolymorphism - person chickeninabiscuit; 09.06.2010
comment
Но тогда мне пришлось бы сделать что-то вроде этого: псевдокод: if(not applicationProcess instanceof ApplicationProcessScheme), потому что я не могу проверить ApplicationProcess, поскольку ApplicationProcessScheme также будет соответствовать этому. Таким образом, код будет if(obj instanceof ApplicationProcessScheme) {//because there is no !instanceof} else { //We have a child!} - person Shervin Asgari; 09.06.2010
comment
@Shervin: Да, тебе придется это сделать. Я не говорю, что это идеально, я говорю, что полагаться на метаданные было бы хуже. На самом деле, я бы раскопал предложение @Ash. - person Pascal Thivent; 09.06.2010
comment
Для тех, кто интересуется ReplaceConditionalWithPolymorphism, вот лучшее объяснение: sourcemaking.com/refactoring/ - person chickeninabiscuit; 09.06.2010

Я только что наткнулся на этот вопрос и должен был опубликовать ответ. IMO, это очевидный случай использования API отражения Java.

DiscriminatorValue annotation = ApplicationProcess.class.getAnnotation(DiscriminatorValue.class);
annotation.getValue().equals("AP");
person Jan Hruby    schedule 26.02.2019
comment
это, не заметил, что один - person Jan Hruby; 11.03.2019

Вы можете использовать аннотацию формулы. Если вы используете Hibernate, вы можете использовать приведенный ниже код в соответствии с эта ссылка:

private String theApType;

@Formula("apType")
String getTheApType() {
    return theApType;
}

Конечно, вы могли бы использовать его в своих запросах.

person user2786531    schedule 09.08.2017

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

SessionFactoryImpl sessionFactory = entityManager.getEntityManagerFactory().unwrap(SessionFactoryImpl.class);
EntityPersister entityPersister = sessionFactory.getEntityPersister( Task.class.getPackage().getName()+"."+param.getValue().get(0) );
int clazz_ = 0;
if(UnionSubclassEntityPersister.class.isInstance(entityPersister)) {
    clazz_ = (Integer) ((UnionSubclassEntityPersister) entityPersister).getDiscriminatorValue();
} else if(JoinedSubclassEntityPersister.class.isInstance(entityPersister)) {
    clazz_ = (Integer) ((JoinedSubclassEntityPersister) entityPersister).getDiscriminatorValue();
}

Вы должны изменить тип clazz_ в соответствии с вашими аннотациями discriminatorType (целое значение по умолчанию для стратегий объединения и соединения). Это решение можно использовать и для SINGLE_TABLE, если вы добавите такой случай; или вы можете использовать другие решения, упомянутые здесь.

person AngelVel    schedule 11.07.2018