Search code examples
jpanullspring-data-jpaspecification-pattern

JPA Specification and null parameter in .where clause


I wrote two Specifications which return null if their parameter is null.

public static Specification<Prodotto> getProdottoByLineaSpec (String linea) {

        if (linea != null) {
            return (root, query, criteriaBuilder) -> {
                return criteriaBuilder.like((root.join("linea")).get("nome"), "%"+linea+"%");
            };
        }
        else return null;
    }

public static Specification<Prodotto> getProdottoByIngSpec (String ing) {

        if (ing != null) {
            return (root, query, criteriaBuilder) -> {
                return criteriaBuilder.like(((root.join("listaQuoteIng")).join("ing")).get("nome"), "%"+ing+"%");
            };
        }
        else return null;
    }

Then I created a third one that combines the previous ones with an and operator inside a where clause:

public static Specification<Prodotto> getProdottoByMainTraits (String linea, String ing) {

        return Specification.where(getProdottoByLineaSpec(linea).and(getProdottoByIngSpec(ing)));
    }

Now, that's the funny part:

  • If ByLinea returns null, i get a nullPointerException from checkPackageAccess when resolving the where clause.
  • If ByIng returns null, it just gets ignored (like it should be) and the query matches just the other predicate.
  • If I switch the two predicates, putting ByIng as the first one and then ByLinea inside the where clause, everything works in every combination.

Solution

  • It is a good practice to avoid returning null from methods.

    You can use criteriaBuilder.conjunction() to ignore null parameter Specification. It generates always true Predicate. There is an opposite method criteriaBuilder.disjunction()

    public static Specification<Prodotto> getProdottoByLineaSpec (String linea) {
                            
        return (root, query, criteriaBuilder) -> {
            if (linea == null) {                 
                return criteriaBuilder.conjunction();
            }
    
            return criteriaBuilder.like(
                     (root.join("linea")).get("nome"), 
                     "%" + linea + "%"
            );            
        }
    }
    

    P.S. You get NullPointerException if first Specification is null trying to access a method and. To be clear it looks like this

    Specification.where(null.and(getProdottoByIngSpec(ing)));
    

    But if only second Specification is null this one works

    Specification.where(getProdottoByLineaSpec(linea).and(null));
    

    because and method parameter can be null