Извлекать только суперкласс из иерархии классов

У меня есть сценарий как следующее:

@Entity
@Table(name = "ANIMAL")
@Inheritance(strategy = InheritanceType.JOINED)

public class Animal implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "S_ANIMAL")
    @SequenceGenerator(name = "S_ANIMAL", sequenceName = "S_ANIMAL", allocationSize = 1)
    public int getNumero() {
        return numero;
    }

    public void setNumero(int numero) {
        this.numero = numero;
    }
        .
        .
        .
}

и как подкласс:

@Entity
@Table(name = "DOG")
public class Dog extends Animal {

    private static final long serialVersionUID = -7341592543130659641L;
        .
        .
        .
}

У меня есть заявление JPA Select, как это:

SELECT a FROM Animal a;

Я использую Hibernate 3.3.1

Как я вижу, каркас извлекает экземпляры Animal а также из Dog используя левое внешнее соединение.

Есть ли способ выбрать только "часть" Animal? Я имею в виду, предыдущий Select получит все Animalс, те, которые только Animalс но не Dogи те, которые Dogs.

Я хочу их всех, но в случае DogЯ хочу получить только "звериную часть" из них.

Я нашел @org.hibernate.annotations.Entity(polymorphism = PolymorphismType.EXPLICIT)но, как я мог видеть, это работает, только если Animal не @Entity,

Большое спасибо.

3 ответа

Решение

Краткий ответ: описанное вами поведение соответствует стандарту JPA. Не существует стандартного способа JPA ограничивать JPA-провайдера для получения только суперкласса.

Поставщик может выбрать логику реализации запроса для достижения функциональности, согласованности и производительности. Пока он уважает аннотации вашей сущности и возвращает запрашиваемую информацию в запросе, все хорошо. Думайте о внешнем соединении с Dog как о частной реализации, которая не должна касаться вас. Поставщик закодировал внешнее объединение для повышения производительности и согласованности.

Рассматривать:

  • JPA определен для работы с объектами Java, а не с таблицами.
  • Корень иерархии сущностей, Animal, не является абстрактным, поэтому вы можете создать экземпляр и сохранить его.
  • У вас есть значение по умолчанию для Animal @DescriminatorColumn и @DescriminatorType - поэтому в таблице Animal будет добавлен столбец дискриминатора с именем "DTYPE" и типом "некоторый тип String". У вас есть значение по умолчанию для @DescriminatorValue - будет равно имени сущности: Animal. Так Animal.DTYPE column = "Animal" при создании этой сущности.
  • У Entity Dog есть суперкласс Animal. Значение по умолчанию также используется для его @DescriminatorValue - Dog. Так что Animal.DTYPE column = "Dog" при создании этой сущности.
  • Ограничения класса Java гарантируют, что всякий раз, когда существует объект Dog, также существует соответствующий объект суперкласса Animal.
  • Очень часто, когда у вас есть Entity Animal, загруженный в контекст сохраняемости JPA, с @DescriminatorValue (значение хранится в столбце DTYPE) = "Dog", тогда очень полезно, чтобы объект Dog был загружен в ПК для согласованности. Даже если это не требуется стандартом JPA.
  • Нельзя указывать отношение наследования как EAGER или LAZY (как поле базового типа или поле отношения сущности). Если бы вам нужно было прочитать / обновить свойства собаки, а класс Dog не был загружен, что бы вы сделали? Запустить отдельный запрос, чтобы перезагрузить его? Это сильно повредит согласованности и производительности.
  • Основной проблемой производительности является общее количество отдельных команд SQL, отправленных в БД.
    С точки зрения времени: запрос с таблицей животных выполняется лишь немного быстрее (10 секунд микросекунд???), чем запрос с внешним животным, присоединенным к собаке, но намного быстрее, чем два отдельных запроса (один для животного и один для собаки)

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

@Query(value = "SELECT * FROM animal", nativeQuery = true)
List<Resource> findAllAnimal();

Флаг nativeQuery как true позволяет запускать собственный SQL для базы данных.

Если вы используете Entity Manager, проверьте это: https://www.thoughts-on-java.org/jpa-native-queries/

Hibernate использует свойство «clazz_» для определения подклассов.

Пример:

      select 
  animal0_.animal_id as animal_i1_0_,..., 
  case 
    when animal0_1_.animal_id is not null then 1 
    when animal0_2_.animal_id is not null then 2 
    when animal0_.animal_id is not null then 0 
  end as clazz_
from animal animal0_ 
left outer join dog animal0_1_ on animal0_.animal_id=animal0_1_.animal_id
left outer join cat animal0_2_ on animal0_.animal_id=animal0_2_.animal_id

Вы должны указать SQL с " 0 AS clazz_ "

      @Query(value = "SELECT *, 0 AS clazz_ FROM animal", nativeQuery = true)
List<Animal> findAllAnimal();
Другие вопросы по тегам