Извлекать только суперкласс из иерархии классов
У меня есть сценарий как следующее:
@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
и те, которые Dog
s.
Я хочу их всех, но в случае 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();