0

I have a simplified Entity class like this:

@Entity
public class Student {
    String name;
    Date birthday;
    Integer term;
}

A third party dependency (DataTables) is sending a search request in form of a Map<String,String> where the key is the name of the entity's attribut and the value the search String. A map can look like this:

key      |   value
------------------
name     |   Alice
birthday |    2000
term     |       3

I currently use this implementation, which works fine for String attributes like name:

public List<Student> getStudents(Map<String,String> filters) throws Exception {
    //get builder
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery cq = cb.createQuery();
    Root<Student> student = cq.from(Student.class);

    //validate if attribut exists
    try {
        for(String key : filters.keySet()) {
            student.get(key); //check if path exists
        }
    } catch (IllegalStateException | IllegalArgumentException ex ) {
        throw new Exception("Invalid filter parameters!");
    }

    // select
    cq.select(student);
    //where
    Predicate wherePredicate = cb.and(); // init with true;
    for (Entry<String,String> filter : filters.entrySet()) {
        wherePredicate = cb.and(wherePredicate,
                                cb.like(student.get(filter.getKey()), "%"+filter.getValue()+"%"));
    }           
    cq.where(wherePredicate);

    return entityManager.createQuery(cq).getResultList();

}

Of course, the like criteria does not work for Integer or Date. How can I extent my code to define criterias based on the attributes data type?

I tried this, what unfortuantly is not possible in Java:

switch (student.get(filter.getKey()).getClass()) {
    case Path<String>.class:
         return cb.like(student.get(filter.getKey()), "%"+filter.getValue()+"%");
    case Path<Integer>.class:
         return cb.equal(student.get(filter.getKey()), filter.getValue())
}
Thanthla
  • 526
  • 1
  • 8
  • 29

1 Answers1

0

It's not the best approach, but you can use just simple name of the class:

String className= student.get(filter.getKey()).getClass().getSimpleName();
switch (className) {
   case "Integer":
       return cb.equal(student.get(filter.getKey()), filter.getValue())
   case "String":
       return cb.like(student.get(filter.getKey()), "%"+filter.getValue()+"%");
}
Thanthla
  • 526
  • 1
  • 8
  • 29
Naya
  • 850
  • 6
  • 19
  • `getSimpleName()` returns **SingularAttributePath** and not the generic value `` – Thanthla Jan 25 '19 at 14:27
  • @Thanthla What returns pdat.get(filter.getKey())? For String, Integer and Date filter? – Naya Jan 25 '19 at 14:29
  • It returns an Object of type Path, where T is the data type of the student's attribut. – Thanthla Jan 25 '19 at 14:34
  • @Thanthla could you, please send the implementation of the method student.get()? Is this method used somewhere else? – Naya Jan 25 '19 at 14:39
  • `Root student = cq.from(Student.class);` is a class from the CriteriaBuilder. `student.get(...)` returns the path to the attribut. See: https://docs.oracle.com/javaee/6/api/javax/persistence/criteria/Path.html#get(javax.persistence.metamodel.MapAttribute) – Thanthla Jan 25 '19 at 14:43