Spring Data / Hibernate Создает два запроса вместо JOIN - PullRequest
2 голосов
/ 29 июня 2019

Контекст: у меня есть две таблицы: анкета и раздел вопросов. Анкета может иметь много разделов вопросов. Анкеты и разделы вопросов имеют начальную и конечную даты, чтобы определить, являются ли они активными записями.

Вот мои сущности, как написано:

@Entity
@Data
public class Questionnaire {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
private String name;
private Date startDate;
private Date endDate;
private String description;

@OneToMany(cascade = CascadeType.All,
          fetch = FetchType.LAZY,
          mappedBy = "questionnaire")
@JsonManagedReference
private List<QuestionSection> questionSections = new ArrayList<QuestionSection>();
}

@Entity
@Data
public class QuestionSection {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
private String name;
private String description;
private int sectionLevel;
private Date startDate;
private Date endDate;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "QUESTIONNAIRE_ID", nullable = false)
@JsonBackReference
private Questionnaire questionnaire;
}

Вот мое хранилище данных Spring с одним объявленным методом:

public interface QuestionnaireRepository extends JpaRepository<Questionnaire, UUID> {

  Questionnaire findByNameAndEndDateIsNull(String name);

  // Previous goal query, but worked all the way back to the above simple query
  // Questionnaire findByIdAndQuestionSectionsEndDateIsNull(UUID id);
  }

Приведенный выше производный запрос генерирует два запроса, показанных ниже:

-- For brevity

select questionnaire.id as id
  questionnaire.description as description
  questionnaire.end_date as end_date
  questionnaire.start_date as start_date
  from questionnaire 
  where questionnaire.name='Foo' and (questionnaire.end_date is null)

select questionsection.questionnaire_id as questionnaire id
    ...rest of fields here...
    from question_section
    where questionsection.questionnaire_id = id from above query

Затем Spring Data или Hibernate объединяет эти два вышеупомянутых запроса в один объект данных, представляющий объект вопросника, и возвращает его.

Моя проблема с этим заключается в том, что я ожидал, что Один запрос будет выполнен с соединением между двумя таблицами, а не двумя, и затем объединит результаты в памяти. У меня довольно большой опыт работы со Spring Data и ORM в целом, и я не смог найти никакой документации о том, почему это происходит. Честно говоря, мне было бы все равно, за исключением того, что мое первоначальное намерение состояло в том, чтобы запрашивать у родительской сущности и «отфильтровывать» дочерние элементы, у которых есть даты окончания (неактивные). Этот производный запрос (закомментированный выше) демонстрировал то же поведение, что в итоге привело к тому, что был возвращен набор данных, содержащий разделы с конечным датированным вопросом. Я знаю, что есть 100 других способов, которыми я мог бы решить эту проблему (и это хорошо), так что на данный момент это больше интересует меня с точки зрения образования, если у кого-то есть понимание этого поведения. Я мог упустить что-то действительно простое.

Ответы [ 2 ]

0 голосов
/ 01 июля 2019

Вы должны быть в состоянии сделать это с помощью функции Entity Graph, представленной в JPA 2.1.

https://www.baeldung.com/jpa-entity-graph

Spring Data предлагает поддержку Entity Graphs с помощью аннотаций @NamedEntityGraph и @EntityGraph:

https://www.baeldung.com/spring-data-jpa-named-entity-graphs

Так в вашем коде:

Организация:

@Entity
@NamedEntityGraph(name = "Questionnaire.questionSections",
    attributeNodes = @NamedAttributeNode("questionSections ")
)
public class Questionnaire{
    //...
}

Repository:

public interface QuestionnaireRepository extends JpaRepository<Questionnaire, UUID> {

  @NamedEntityGraph("Questionnaire.questionSections")
  Questionnaire findByNameAndEndDateIsNull(String name);
}
0 голосов
/ 01 июля 2019
public interface QuestionnaireRepository extends JpaRepository<Questionnaire, UUID> {
  @EntityGraph(attributePaths = { "questionSections" })
  Questionnaire findByNameAndEndDateIsNull(String name);
}
...