Почему использование @EntityGraph вместе с JOIN приводит к ошибке в одном случае, но работает нормально в другом?
При загрузке сущностей из базы данных я использую @NamedEntityGraph. Я пытался использовать граф вот в таком запросе:
@EntityGraph(value = "paragraph-graph-questions")
@Query("SELECT sp FROM Paragraph p JOIN p.subParagraphs sp WHERE p.id=:id")
Set<Paragraph> getAllSubParagraphs(int id);
Это вызывает следующую ошибку:
org.hibernate.QueryException: query specified join fetching,
but the owner of the fetched association was not present in the select list ...
Следует обратить внимание, что запрос в данном случае рефлексивен, потому что сущность Paragraph ссылается на другие сущности Paragraph отношением OneToMany:
@NamedEntityGraph(
name = "paragraph-graph-questions",
attributeNodes = {
@NamedAttributeNode(value = "questions")
})
@Entity
@Table(name = "paragraphs")
public class Paragraph {
@Id
private Integer id
@OneToMany(mappedBy = "paragraph")
private Set<Question> questions;
@OneToMany
@JoinTable(
name = "sub_paragraphs",
joinColumns = @JoinColumn(name = "paragraph_id"),
inverseJoinColumns = @JoinColumn(name = "sub_paragraph_id")
)
private Set<Paragraph> subParagraphs;
}
Я решил, что нельзя использовать JOIN и EntityGraph одновременно, так как это приводит к ошибке. Но, видимо, всё не так однозначано, так как вот такой запрос корректно работает с графом сущностей:
@EntityGraph(value = "report-graph-low")
@Query("""
SELECT r FROM Report r
LEFT JOIN r.themeDetails td ON td.theme.id=:themeId
LEFT JOIN r.paragraphDetails pd ON pd.paragraph.theme.id=:themeId
LEFT JOIN r.offlineThemeDetails od ON od.theme.id=:themeId
WHERE r.student.id=:studentId
""")
List<Report> getAllByStudentIdAndThemeId(int studentId, int themeId, Pageable pagination);
В случае сущности Report рефлексивные отношения отсутствуют.
Почему в одном случае происходит ошибка, а в другом нет? Каким образом я могу получить множество subParagraphs с использованием графа сущностей?
P.S.: Я знаю, что могу сделать в Paragraph поле Paragraph parent, и так действительно будет проще, но я хотел бы обойтись иным способом.
Ответы (1 шт):
Причина ошибки по-видимому заключалась вовсе не в использовании JOIN, а в том, что в выборке брался не владелец отношения, а связанные с ним сущности, в то время как @EntityGraph пытается загрузить граф именно владельца отношения.
В методе getAllSubParagraphs я пробовал разные вариации запросов, но все они давали ошибку:
org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list
Я решил зайти с другой стороны и попробовал сделать такой запрос, в котором выбираться будет именно владелец ассоциации, а не его дочерние сущности:
@EntityGraph(value = "paragraph-graph-questions")
@Query("""
SELECT p FROM Paragraph p
WHERE p.id IN
(SELECT sp.id FROM Paragraph p JOIN p.subParagraphs sp WHERE p.id=:id)
""")
Set<Paragraph> getAllSubParagraphs(int id);
Это действительно сработало. Hibernate формирует такой запрос:
select
paragraph0_.id as id1_5_0_,
questions4_.id as id1_7_1_,
paragraph0_.name as name2_5_0_,
paragraph0_.grade as grade3_5_0_,
paragraph0_.is_active as is_activ4_5_0_,
paragraph0_.num_of_questions_for_paragraph_test as num_of_q5_5_0_,
paragraph0_.theme_id as theme_id6_5_0_,
questions4_.answer as answer2_7_1_,
questions4_.bottom_text as bottom_t3_7_1_,
questions4_.displaying_setting as displayi4_7_1_,
questions4_.image as image5_7_1_,
questions4_.paragraph_id as paragrap8_7_1_,
questions4_.top_text as top_text6_7_1_,
questions4_.type as type7_7_1_,
questions4_.paragraph_id as paragrap8_7_0__,
questions4_.id as id1_7_0__
from
paragraphs paragraph0_
left outer join
questions questions4_
on paragraph0_.id=questions4_.paragraph_id
where
paragraph0_.id in (
select
paragraph3_.id
from
paragraphs paragraph1_
inner join
sub_paragraphs subparagra2_
on paragraph1_.id=subparagra2_.paragraph_id
inner join
paragraphs paragraph3_
on subparagra2_.sub_paragraph_id=paragraph3_.id
where
paragraph1_.id=?
)
И в результате загружается именно такой граф, какой я и хотел.
Вероятно, такой запрос не является оптимальным. Здесь делается лишний SELECT. Лучше всего было бы сделать в сущности Paragraph поле Paragraph parent, тогда запрос был бы проще:
SELECT p FROM Paragraph p WHERE p.parent.id=:id
Так что, если вы собираетесь загружать сущность графом, выгружайте владельца ассоциации, потому что загрузка графа будет именно у него.