title: Using JPA CriteriaBuilder to generate query where attribute is either in a list or is empty
body: <p>I am trying to use the JPA CriteriaBuilder to generate a query for an entity called "TestContact" that has a many-to-many join with another entity called "SystemGroup" where the attribute for this join called "groups". The objective of the query is to retrieve records from the "TestContact" entity where the "groups" attribute is either in a list or is empty.</p>
  
  <p>The code I'm using is as follows</p>
  
  <pre><code>public List&lt;TestContact&gt; findWithCriteriaQuery(List&lt;SystemGroup&gt; groups) {
  
      CriteriaBuilder cb = em.getCriteriaBuilder();
      CriteriaQuery&lt;TestContact&gt; cq = cb.createQuery(TestContact.class);
      Root&lt;TestContact&gt; testContact = cq.from(TestContact.class);
      cq.select(testContact);
  
      Path&lt;List&lt;SystemGroup&gt;&gt; groupPath = testContact.get("groups");
  
      // cq.where(groupPath.in(groups));
      // cq.where(cb.isEmpty(groupPath));
      cq.where(cb.or(cb.isEmpty(groupPath), groupPath.in(groups)));
  
      TypedQuery&lt;TestContact&gt; tq = em.createQuery(cq);
  
      return tq.getResultList();
  }
  </code></pre>
  
  <p>The problem is this query only returns results where group is in the list "groups" but for some reason isn't also returning the results where group is empty (i.e. there is no entry in the join table)</p>
  
  <p>If I change the where clause to <code>cq.where(cb.isEmpty(groupPath));</code> then the query correctly returns the results where group is empty.</p>
  
  <p>If I change the where clause to <code>cq.where(groupPath.in(groups));</code> then the query correctly returns the results where the group is in the list "groups".</p>
  
  <p>What I don't understand is why when I try to combine these two predicates using the CriteriaBuilder or method the results don't include the records where the group is either in the list or is empty.</p>
  
  <p>The groups attribute in the "TestContact" entity is declared as follows</p>
  
  <pre><code>@ManyToMany(fetch=FetchType.EAGER)
  @JoinTable(name = "TEST_CONTACT_GROUPS", joinColumns = { @JoinColumn(name = "CONTACT_ID", referencedColumnName = "CONTACT_ID") }, inverseJoinColumns = { @JoinColumn(name = "GROUP_ID", referencedColumnName = "GROUP_ID") })
  private List&lt;SystemGroup&gt; groups;
  </code></pre>
  
  <p>The JPA provider is EclipseLink 2.5.0, the Java EE application server is GlassFish 4 and the database is Oracle 11gR2.</p>
  
  <p>Can anyone please point out where I'm going wrong?</p>
  
  <p><strong>Update</strong></p>
  
  <p>I've tried the suggestion from @Chris but Eclipse is returning the following error on <code>Join&lt;List&lt;SystemGroup&gt;&gt; groupPath = testContact.join("groups", JoinType.LEFT)</code> </p>
  
  <blockquote>
    <p>Incorrect number of arguments for type Join; it cannot be
    parameterized with arguments ></p>
  </blockquote>
  
  <p>Looking at the JavaDoc for <code>Join</code> it says the type parameters are... </p>
  
  <blockquote>
    <p>Z - the source type of the join, X - the target type of the join</p>
  </blockquote>
  
  <p>I've tried <code>Join&lt;TestContact, SystemGroup&gt; groupPath = testContact.join("groups", JoinType.LEFT);</code> which then causes Eclipse to return the following error on <code>cb.isEmpty</code> </p>
  
  <blockquote>
    <p>Bound mismatch: The generic method isEmpty(Expression) of type
    CriteriaBuilder is not applicable for the arguments
    (Join). The inferred type SystemGroup is not
    a valid substitute for the bounded parameter ></p>
  </blockquote>