title: Spring Query DSL Giving Null Pointer Exception
body: <p>I'm using Spring Boot and a Hibernate file database for this prototype. </p>
  
  <p>here's a snippet out of application.properties (user/pass omitted. obviously) NOTE I have set fave as a String rather than a boolean so I can use a String in the Query DSL Bindings:</p>
  
  <pre><code>spring.jpa.generate-ddl=true
  spring.datasource.url=jdbc:h2:file:~/db
  spring.datasource.driver-class-name=org.h2.Driver
  hibernate.hbm2ddl.auto=update
  hibernate.dialect=org.hibernate.dialect.H2Dialect
  hibernate.show_sql=true
  </code></pre>
  
  <p>here's the model w/ City class omitted as it's quite simple:</p>
  
  <pre><code>@Data
  @AllArgsConstructor
  @Entity
  @Table(name = "matches")
  @EqualsAndHashCode(of = "id")
  public class FilterMatch {
  
      @SequenceGenerator(name = "MATCHES_SEQ_GENERATOR", sequenceName = "MATCHES_SEQ",
                         initialValue = 1, allocationSize = 1)
  
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO, generator = "MATCHES_SEQ_GENERATOR")
      @Column
      private Long id;
  
      @Column
      private String fave;
  
      @Column
      private int age;
  
      @Column
      private int cmHeight;
  
      @Column
      private int contactsExchanged;
  
      @Column
      private float compatScore;
  
      @Column
      private String displayName;
  
      @Column
      private String jobTitle;
  
      @Column
      private String mainPhoto;
  
      @Column
      private String religion;
  
      @OneToOne(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
      private City city;
  
      public FilterMatch() {} 
  
      // getters/setters omitted
  }
  </code></pre>
  
  <p>here's the repository which extends JPARepository as is necessary:</p>
  
  <pre><code>public interface FilterMatchRepository extends JpaRepository&lt;FilterMatch, Long&gt;,
      QueryDslPredicateExecutor&lt;FilterMatch&gt;,QuerydslBinderCustomizer&lt;QFilterMatch&gt; {
  
  
      @Override
      default public void customize(QuerydslBindings bindings, QFilterMatch root) {
          bindings.bind(String.class)
                  .first((SingleValueBinding&lt;StringPath, String&gt;) StringExpression::containsIgnoreCase);
          bindings.excluding(root.religion,root.jobTitle);
      }
  
  }
  </code></pre>
  
  <p>my SearchCriteria class:</p>
  
  <pre><code>public class SearchCriteria {
  
      private static final Logger logger = LogManager.getLogger(SearchCriteria.class);
  
      private String key;
      private String operation;
      private Object value;
  
      public SearchCriteria(String key, String operation, Object value)  {
          this.key = key;
          this.operation = operation;
          this.value = value;
      }
      //getters &amp; setters omitted.
  }
  </code></pre>
  
  <p>my MatchBuilderPredicates class and my MatchPredicates class:</p>
  
  <pre><code>public class MatchPredicatesBuilder {
  
      private static final Logger logger = LoggerFactory.getLogger(MatchPredicatesBuilder.class);
  
      private List&lt;SearchCriteria&gt; parameters;
  
      public MatchPredicatesBuilder() {
          parameters = new ArrayList&lt;&gt;();
      }
  
      public MatchPredicatesBuilder with(String key, String operation, Object value) {
  
          parameters.add(new SearchCriteria(key, operation, value));
          return this;
      }
  
      public BooleanExpression build() {
  
          if (parameters.size() == 0) {
              return null;
          }
  
          List&lt;BooleanExpression&gt; predicates = new ArrayList&lt;&gt;();
          MatchPredicate predicate;
  
          for (SearchCriteria param : parameters) {
  
              predicate = new MatchPredicate(param);
              BooleanExpression expression = predicate.getPredicate();
  
              if (Objects.nonNull(expression)) {
                  predicates.add(expression);
              }
          }
  
          BooleanExpression result = predicates.get(0);
  
          for (int i = 1; i &lt; predicates.size(); i++) {
  
              result = result.and(predicates.get(i));
          }
          return result;
      }   
  
  }
  
  public class MatchPredicate {
  
      private static final long serialVersionUID = 8621295752447527269L;
      private static final Logger logger = LoggerFactory.getLogger(MatchPredicate.class);
  
      private SearchCriteria criteria;
  
      public MatchPredicate(SearchCriteria criteria) {
          this.criteria = criteria;
      }
  
      public BooleanExpression getPredicate() {
  
          PathBuilder&lt;FilterMatch&gt; entityPath = new PathBuilder&lt;&gt;(FilterMatch.class, "FilterMatch");
  
          if (FilterUtils.isNumeric(criteria.getValue().toString())) {
  
              NumberPath&lt;Double&gt; path = entityPath.getNumber(criteria.getKey(), Double.class);
              double value = Double.parseDouble(criteria.getValue().toString());
  
              switch (criteria.getOperation()) {
                  case ":":
                      return path.eq(value);
                  case "&gt;":
                      return path.goe(value);
                  case "&lt;":
                      return path.loe(value);
              }
          } else {
  
              StringPath path = entityPath.getString(criteria.getKey());
  
              if (criteria.getOperation().equalsIgnoreCase(":")) {
                  return path.containsIgnoreCase(criteria.getValue().toString());
              }
          }
          return null;
      }   
  }
  </code></pre>
  
  <p>so this integration test throws a NPE:</p>
  
  <pre><code>@RunWith(SpringJUnit4ClassRunner.class)
  @SpringBootTest
  public class JpaQueryDslIntegrationTest {
  
      private static final Logger logger = LoggerFactory.getLogger(JpaQueryDslIntegrationTest.class);
  
      @Autowired
      private FilterMatchRepository matchRepository;
  
      private FilterMatch match1;
      private FilterMatch match2;
  
      /**
       * @throws java.lang.Exception
       */
      @Before
      public void setUp() throws Exception {
  
          match1 = new FilterMatch();
          match1.setDisplayName("Emma");
          match1.setAge(40);
          match1.setJobTitle("Banker");
          match1.setCmHeight(150);
  
          City leeds = new City();
          leeds.setName("Leeds");
          leeds.setLon(-1.548567);
          leeds.setLat(53.801277);
  
          match1.setCity(leeds);
          match1.setMainPhoto("http://thecatapi.com/api/images/get?format=src&amp;type=gif");
          match1.setCompatScore(0.73f);
          match1.setContactsExchanged(0);
          match1.setFave(Constants.FALSE.getValue());
          match1.setReligion("Christian");
          matchRepository.save(match1);
  
          match2 = new FilterMatch();
          match2.setDisplayName("Diana");
          match2.setAge(44);
          match2.setJobTitle("Consultant");
          match2.setCmHeight(153);
  
          City london = new City();
          london.setName("London");
          london.setLat(51.509865);
          london.setLon(-0.118092);
  
          match2.setCity(london);
          match2.setMainPhoto("http://thecatapi.com/api/images/get?format=src&amp;type=gif");
          match2.setCompatScore(0.50f);
          match2.setContactsExchanged(0);
          match2.setFave(Constants.TRUE.getValue());
          match2.setReligion("Atheist");
          matchRepository.save(match2);
      }
  
      @Test
      public final void testMultipleMatch() {
  
          MatchPredicatesBuilder builder = new MatchPredicatesBuilder().with("displayName", ":", "Emma");
  
          Iterable&lt;FilterMatch&gt; results = matchRepository.findAll();
          results = matchRepository.findAll(builder.build()); // This is the line that throws the NPE. i've tested and builder.build() isn't null  
          assertThat(results, Matchers.containsInAnyOrder(match1, match2));
      }
  }
  </code></pre>
  
  <p>i have the contents of builder.build() printing out &amp; it's:</p>
  
  <pre><code>containsIc(FilterMatch.displayName,Emma)
  </code></pre>
  
  <p>here's the stack trace cut down a bit. :</p>
  
  <pre><code>java.lang.NullPointerException
      at java.lang.String$CaseInsensitiveComparator.compare(String.java:1192)
      at java.lang.String$CaseInsensitiveComparator.compare(String.java:1186)
      at java.util.TreeMap.getEntryUsingComparator(TreeMap.java:376)
      at java.util.TreeMap.getEntry(TreeMap.java:345)
      at java.util.TreeMap.get(TreeMap.java:278)
      at org.hibernate.dialect.function.SQLFunctionRegistry.findSQLFunction(SQLFunctionRegistry.java:45)
      at org.hibernate.hql.internal.ast.util.SessionFactoryHelper.findSQLFunction(SessionFactoryHelper.java:369)
      at org.hibernate.hql.internal.ast.tree.IdentNode.getDataType(IdentNode.java:374)
      at org.hibernate.hql.internal.ast.HqlSqlWalker.lookupProperty(HqlSqlWalker.java:654)
      at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.addrExpr(HqlSqlBaseWalker.java:5003)
      at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.expr(HqlSqlBaseWalker.java:1286)
      at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.exprOrSubquery(HqlSqlBaseWalker.java:4707)
      at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.functionCall(HqlSqlBaseWalker.java:2733)
      at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.expr(HqlSqlBaseWalker.java:1365)
      at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.exprOrSubquery(HqlSqlBaseWalker.java:4707)
      at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.comparisonExpr(HqlSqlBaseWalker.java:4319)
      at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2138)
      at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.whereClause(HqlSqlBaseWalker.java:815)
      at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:609)
      at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:313)
      at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:261)
      at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:266)
      at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:189)
      at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:141)
      at org.hibernate.engine.query.spi.HQLQueryPlan.&lt;init&gt;(HQLQueryPlan.java:115)
      at org.hibernate.engine.query.spi.HQLQueryPlan.&lt;init&gt;(HQLQueryPlan.java:77)
      at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:153)
      at org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:553)
      at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:662)
      at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:23)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:498)
      at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)
      at com.sun.proxy.$Proxy58.createQuery(Unknown Source)
      at com.querydsl.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:101)
      at com.querydsl.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:94)
      at com.querydsl.jpa.impl.AbstractJPAQuery.fetch(AbstractJPAQuery.java:201)
      at org.springframework.data.jpa.repository.support.QueryDslJpaRepository.findAll(QueryDslJpaRepository.java:105)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:498)
      at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:520)
      at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:505)
      at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:477)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
      at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:56)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
      at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
      at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
      at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
      at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
      at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
      at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
      at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
  </code></pre>
  
  <p>I have a couple of questions:</p>
  
  <p>1) I have my MatchPredicatesBuilder returning a BooleanExpression as the tutorial I'm following says I should do so but I don't know why the compiler doesn't choke on this. I've been in the java docs &amp; it's still a mystery.</p>
  
  <p>2) Why is it throwing a NPE? builder.build() isn't null. It's hard to tell from the stack trace. </p>