#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);