Reputation: 169
Context: I have two tables: Questionnaire and Question Section. A Questionnaire can have many Question Sections. Questionnaires and Question Sections both have Start and End Dates to determine if they are active records.
Here are my entities as written:
@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;
}
Here is my Spring Data Repository with a single declared method:
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);
}
The above derived query generates two queries shown below:
-- 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
Then Spring Data or Hibernate is combining those two above queries into one data object representative of the questionnaire object and returning that.
My problem with this is that I would have expected One query to run with a Join between the two tables, not two and then combine the results in memory. I'm pretty experienced with Spring Data and ORMs in general and have not been able to find any documentation as to why this is happening. Honestly I wouldn't care except that my original intention was to query at the parent entity and 'filter' out children that have end dates (not active). This derived query (commented out above) exhibited the same behavior which ultimately resulted in the data set that was returned containing the end dated question sections. I know there's 100 other ways I could solve this problem (which is fine) so this is more of an educational interest for me at this point if anyone has any insight into this behavior. I could be missing something really simple.
Upvotes: 3
Views: 1250
Reputation: 23226
You should be able to do this using the Entity Graph feature introduced in JPA 2.1.
https://www.baeldung.com/jpa-entity-graph
Spring Data offers support for Entity Graphs via the @NamedEntityGraph
and @EntityGraph
annotations:
https://www.baeldung.com/spring-data-jpa-named-entity-graphs
So in your code:
Entity:
@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);
}
Upvotes: 3
Reputation: 3423
public interface QuestionnaireRepository extends JpaRepository<Questionnaire, UUID> {
@EntityGraph(attributePaths = { "questionSections" })
Questionnaire findByNameAndEndDateIsNull(String name);
}
Upvotes: 0