title: JPA Entity Graph eager loading affected by where clause
body: <p>I'm having some trouble using the JPA Entity Graph feature to eager load data in the way that I expect, using Spring Data JPA and Hibernate (with a Postgres database). To illustrate, I've come up with a simple example. I have a Spring Boot application containing <em>Articles</em> and <em>Tags</em>, with a many-to-many relationship between them:</p>
  
  <pre class="lang-java prettyprint-override"><code>@Entity
  @NoArgsConstructor
  @AllArgsConstructor
  @Data
  public class Article {
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      private Integer id;
  
      private String title;
  
      @ManyToMany
      private Set&lt;Tag&gt; tags;
  }
  </code></pre>
  
  <pre class="lang-java prettyprint-override"><code>@Entity
  @AllArgsConstructor
  @NoArgsConstructor
  @Data
  public class Tag {
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      private Integer id;
  
      private String name;
  }
  </code></pre>
  
  <p>I also have a simple <code>TagRepository</code> as well as an <code>ArticleRepository</code> to assist with loading articles, using the <code>@EntityGraph</code> annotation to specify which properties I want to eager load when I use each repository method, in this case the <code>tags</code> property:</p>
  
  <pre class="lang-java prettyprint-override"><code>@Repository
  public interface ArticleRepository extends JpaRepository&lt;Article, Integer&gt; {
      @EntityGraph(attributePaths = {"tags"})
      Set&lt;Article&gt; findAllByTagsIn(Collection&lt;Tag&gt; tags);
  
      @Override
      @EntityGraph(attributePaths = {"tags"})
      List&lt;Article&gt; findAll();
  }
  </code></pre>
  
  <pre class="lang-java prettyprint-override"><code>@Repository
  public interface TagRepository extends JpaRepository&lt;Tag, Integer&gt; {
  }
  </code></pre>
  
  <p>Finally, to illustrate the problem, I execute some code on application startup which:</p>
  
  <ul>
  <li>Creates 3 <em>tags</em>, <code>Tag A</code>, <code>Tag B</code> and <code>Tag C</code></li>
  <li>Creates 3 <em>articles</em>:
  
  <ul>
  <li><code>Article A</code>, which is assigned the tags <em>A</em> and <em>B</em></li>
  <li><code>Article B</code>, which is assigned the tags <em>A</em>, <em>B</em> and <em>C</em></li>
  <li><code>Article C</code>, which is assigned just tag <em>C</em></li>
  </ul></li>
  <li>Executes <code>ArticleRepository.findAllByTagsIn</code> to <em>get all articles that are assigned <code>Tag A</code></em>, eager loading tags and then prints each article's title and names of its assigned tags.</li>
  <li>Repeats the previous step, using <code>ArticleRepository.findAll</code> instead.</li>
  </ul>
  
  <pre class="lang-java prettyprint-override"><code>@Component
  public class StartupListener implements ApplicationListener&lt;ContextRefreshedEvent&gt; {
      private final ArticleRepository articleRepository;
      private final TagRepository tagRepository;
  
      @Autowired
      public StartupListener(ArticleRepository articleRepository, TagRepository tagRepository) {
          this.articleRepository = articleRepository;
          this.tagRepository = tagRepository;
      }
  
      @Override
      public void onApplicationEvent(ContextRefreshedEvent event) {
          Tag tagA = tagRepository.save(new Tag(null, "Tag A"));
          Tag tagB = tagRepository.save(new Tag(null, "Tag B"));
          Tag tagC = tagRepository.save(new Tag(null, "Tag C"));
  
          articleRepository.save(new Article(null, "Article A", Set.of(tagA, tagB)));
          articleRepository.save(new Article(null, "Article B", Set.of(tagA, tagB, tagC)));
          articleRepository.save(new Article(null, "Article C", Set.of(tagC)));
  
          printArticles("--- Find all by tags in ---", articleRepository.findAllByTagsIn(Set.of(tagA)));
          printArticles("--- Find all ---", articleRepository.findAll());
      }
  
      private void printArticles(String title, Collection&lt;Article&gt; articles) {
          System.out.println(title);
          articles.forEach(article -&gt; {
              String tags = article.getTags().stream().map(Tag::getName).collect(Collectors.joining(", "));
              System.out.println(String.format("%s: [%s]", article.getTitle(), tags));
          });
      }
  }
  </code></pre>
  
  <h2>Expectation</h2>
  
  <p>I expected that in the first query, only articles A and B would be loaded but <em>all</em> of their assigned tags would be displayed. In the second query, I expected <em>all</em> articles to be loaded, with <em>all</em> of their associated tags. I.e. I expected the output to look like this:</p>
  
  <pre><code>--- Find all by tags in ---
  Article A: [Tag A, Tag B]
  Article B: [Tag A, Tag B, Tag C]
  --- Find all ---
  Article A: [Tag A, Tag B]
  Article B: [Tag A, Tag B, Tag C]
  Article C: [Tag C]
  </code></pre>
  
  <h2>Result</h2>
  
  <p>To my surprise, with the additional <code>where</code> clause added to the first query, only the tags that were in the set provided as an argument to the repository method were actually eager loaded. In this instance, <code>Tag B</code> and <code>Tag C</code> were omitted entirely because a set containing only <code>Tag A</code> was passed to the repository method and the eventual query that was executed. The second query worked as I had hoped, because there were no <code>where</code> clauses surrounding categories added to the resulting query:</p>
  
  <pre><code>--- Find all by tags in ---
  Article A: [Tag A]
  Article B: [Tag A]
  --- Find all ---
  Article A: [Tag A, Tag B]
  Article B: [Tag A, Tag B, Tag C]
  Article C: [Tag C]
  </code></pre>
  
  <p>The queries that are ultimately executed are:</p>
  
  <pre><code>select
      article0_.id as id1_0_0_,
      tag2_.id as id1_2_1_,
      article0_.title as title2_0_0_,
      tag2_.name as name2_2_1_,
      tags1_.article_id as article_1_1_0__,
      tags1_.tags_id as tags_id2_1_0__
  from
      article article0_
  left outer join
      article_tags tags1_
      on article0_.id=tags1_.article_id
  left outer join
      tag tag2_
      on tags1_.tags_id=tag2_.id
  where
      tag2_.id in (?)
  </code></pre>
  
  <pre><code>select
      article0_.id as id1_0_0_,
      tag2_.id as id1_2_1_,
      article0_.title as title2_0_0_,
      tag2_.name as name2_2_1_,
      tags1_.article_id as article_1_1_0__,
      tags1_.tags_id as tags_id2_1_0__
  from
      article article0_
  left outer join
      article_tags tags1_
      on article0_.id=tags1_.article_id
  left outer join
      tag tag2_
      on tags1_.tags_id=tag2_.id
  </code></pre>
  
  <p>I'm a little surprised that Hibernate attempts to do all of this with a single query. I'd have expected it to first do the initial query to get all of the matching articles, joining in the tags and applying the where condition to filter out those that do not match the criteria; <em>then</em> execute another query using the IDs of the loaded articles to load <em>all</em> of their related tags.</p>
  
  <h2>Questions</h2>
  
  <ul>
  <li>Is this the expected, default behaviour of JPA with Entity Graphs? To factor in any <code>where</code> clauses when eager loading entities?</li>
  <li>How can I achieve the result I expected, in that only articles that were assigned <code>Tag A</code> are displayed (Articles A and B), but <em>all</em> of their associated tags are eagerly loaded?</li>
  </ul>