Почему использование исключения @NamedEntityGraph вызывает исключение?

#java #spring-boot #spring-data-jpa #jpql #entitygraph

Вопрос:

У меня есть Spring Boot приложение. В приложении существует сущность:

 @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;
}
 

Особенность заключается в том, что Paragraph сущность имеет рефлексивную однонаправленную связь с другими абзацами. Я сделал JpaRepository так, чтобы получить подграфы абзаца:

 @Transactional(readOnly = true)
public interface ParagraphDao extends JpaRepository<Paragraph, Integer> {

    @EntityGraph(value = "paragraph-graph-questions")
    @Query("SELECT sp FROM Paragraph p JOIN p.subParagraphs sp WHERE p.id=:id")
    Set<Paragraph> getAllSubParagraphs(int id);
}
 

Но если я использую @EntityGraph этот метод, вызов этого метода вызывает исключение, метод else работает нормально. Это трассировка исключения стека, которую я получаю при использовании @EntityGraph :

 org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=sp,role=ru.zhenyria.deeptest.model.task.Paragraph.subParagraphs,tableName=paragraphs,tableAlias=paragraph2_,origin=paragraphs paragraph0_,columns={paragraph0_.id,className=ru.zhenyria.deeptest.model.task.Paragraph}}]
 

Как я могу это исправить? Спасибо.

Ответ №1:

Похоже, использование @EntityGraph этого способа не поддерживается.

Предполагая, что вы хотели бы получить все SubParagraphs части абзаца, включая все их вопросы, в одном операторе выбора, вы могли бы использовать диаграмму сущностей, подобную следующей:

 @NamedEntityGraph(name = "subParagraphsWithQuestions", attributeNodes = {
  @NamedAttributeNode(value = "subParagraphs", subgraph = "subgraph") }, subgraphs = {
    @NamedSubgraph(name = "subgraph", attributeNodes = { @NamedAttributeNode(value = "questions") }) })
 

Его можно использовать с запросом репозитория, подобным этому:

 @EntityGraph(value = "subParagraphsWithQuestions")
@Query("SELECT p FROM Paragraph p WHERE p.id=:id")
Paragraph customFindWithSubParagraphQuestions(@Param("id") long id);
 

При выполнении этого запроса все связанные подпункты с вопросами извлекаются напрямую, так что доступ к этим вопросам не вызывает дополнительных проблем.
(Подробности см. в этом примере интеграционного теста.)

Помогает ли это?


Другим вариантом было бы добавить обратное сопоставление, чтобы иметь возможность запрашивать Paragraphs «parentParagraphId» — таким образом, вы сможете повторно использовать свой ток @EntityGraph в запросе spring-данных, подобном этому:

 @EntityGraph(value = "paragraph-graph-questions")
Set<Paragraph> findByParentParagraphId(int id);