Search code examples
javamysqljpaormeclipselink

Create advanced search in JPA through user defined dynamic where clause conditions


I'd like to create a search where the user defines the where clause conditions. If the user enters a parameter in the search form, then that parameter is included in the WHERE clause. If the parameter is left blank, it is omitted from the where clause. If the entire form is blank,the query should returns a SELECT ALL and the entire table. In this way the query becomes successively more precise the more parameters entered by the user.

I am trying the below code but this seems to require the parameters to be input and returns results only if the actual data matches with ALL of the Predicates specified. It looks like the Predicates are being included in the array even if they are NULL and empty? Either that or I'm missing something very obvious in how I've written this.

My questions are:

1) If the search form is left blank, would the Predicates be excluded from the array?

2) If yes, wouldn't the query default to a SELECT ALL? [query.from(Contacts.class)]?

I saw a similar question but didn't really tell me how to achieve the above since it appears that the logic of my code is correct.

JPA where clause any

Thanks in advance for any guidance!

public ListDataModel<Contacts> qSearchContacts(String firstName, String surName, Integer countryid, Integer companyId) {

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Contacts> query = cb.createQuery(Contacts.class);
    Root<Contacts> cont = query.from(Contacts.class);
    query.select(cont);

    List<Predicate> predicateList = new ArrayList<Predicate>();
    Predicate firstnamePredicate, surnamePredicate, countryPredicate, companyPredicate;

    if ((firstName != null) && (!(firstName.isEmpty()))) {
        firstnamePredicate = cb.like(cb.upper(cont.<String>get("firstName")), "%" + firstName.toUpperCase() + "%");
        predicateList.add(firstnamePredicate);
    }


    if ((surName != null) && (!(surName.isEmpty()))) {
        surnamePredicate = cb.like(cb.upper(cont.<String>get("surName")), "%" + surName.toUpperCase() + "%");
        predicateList.add(surnamePredicate);

     }

    if (countryid != null) {
        Join<Contacts, Country> country = cont.join("country");
        countryPredicate = cb.equal(country.<Integer>get("countryid"), countryid);
        predicateList.add(countryPredicate);
    } 
    }

    if (companyId != null) {
        Join<Contacts, Company> company = cont.join("company");
        companyPredicate = cb.equal(company.<Integer>get("companyId"), companyId);
        predicateList.add(companyPredicate);
    }

    Predicate[] predicateArray = new Predicate[predicateList.size()];
    predicateList.toArray(predicateArray);
    query.where(predicateArray);
    ListDataModel<Contacts> contactResultList = new ListDataModel<Contacts>(em.createQuery(query).getResultList());

    return contactResultList;
}

Solution

  • I solved this. The above code seems to create the predicates in all cases and there are no explicit conditions for excluding the predicates from the array list or executing a SELECT ALL in the event that no predicates exist. I rewrote the code explicitly each parameter using a TypedQuery object. This is now resulting in the desired behavior. No predicates result in a SELECT ALL. Each predicate is only included as a WHERE clause condition if it is not null and empty. Hope this helps.

    public ListDataModel<Contacts> qSearchContacts(String firstName, String surName, Integer countryid, Integer companyId) {
    
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Contacts> query = cb.createQuery(Contacts.class);
        Root<Contacts> cont = query.from(Contacts.class);
        query.select(cont);
    
        // Join<Contacts, Country> country = cont.join("country", JoinType.LEFT);
        List<Predicate> predicateList = new ArrayList<Predicate>();
    
        if ((firstName != null) && (!(firstName.isEmpty()))) {
            ParameterExpression<String> p
                    = cb.parameter(String.class, "firstName");
            predicateList.add(cb.like(cb.upper(cont.<String>get("firstName")), "%" + firstName.toUpperCase() + "%"));
    
        }
    
        if ((surName != null) && (!(surName.isEmpty()))) {
            ParameterExpression<String> p
                    = cb.parameter(String.class, "surName");
            predicateList.add(cb.like(cb.upper(cont.<String>get("surName")), "%" + surName.toUpperCase() + "%"));
    
        }
    
        if ((countryid != null) && (countryid != 0)) {
            Join<Contacts, Country> country = cont.join("country");
            ParameterExpression<Integer> ci
                    = cb.parameter(Integer.class, "countryid");
            predicateList.add(cb.equal(country.get("countryid"), ci));
        }
    
        if ((companyId != null) && (companyId != 0)) {
            Join<Contacts, Company> company = cont.join("company");
            ParameterExpression<Integer> co
                    = cb.parameter(Integer.class, "companyId");
            predicateList.add(cb.equal(company.get("companyId"), co));
        }
    
        if (predicateList.size() == 0) {
            query.select(cont);
        } else {
            if (predicateList.size() == 1) {
                query.where(predicateList.get(0));
            } else {
                query.where(cb.and(predicateList.toArray(new Predicate[0])));
            }
        }
    
        TypedQuery<Contacts> tq = em.createQuery(query);
        if (firstName != null) {
            tq.setParameter("firstName", firstName);
        }
        if (surName != null) {
            tq.setParameter("surName", surName);
        }
    
        if ((countryid != null) && (countryid != 0)) {
            tq.setParameter("countryid", countryid);
        }
    
        if((companyId != null) && (companyId != 0)) {
            tq.setParameter("companyId", companyId);
        }
    
        ListDataModel<Contacts> contactsResultList = new ListDataModel<Contacts>(tq.getResultList());
        return contactsResultList;
    
    }
    }