Search code examples
javajpqlquerydsl

querydsl-jpa dynamic query


Let's say I have a generated Entity like this:

public class QCandidate extends EntityPathBase<Candidate> {

public final com.avisto.candisearch.persistance.model.enums.QAvailabilityEnum availability;

public final DatePath<java.util.Date> birthDate = createDate("birthDate", java.util.Date.class);

public final NumberPath<Long> cvId= createNumber("cvId", Long.class);

public final StringPath city = createString("city");

}

My input values are the fields names ("availability","birthDate","cvId"...) and a string value that I should use to perform a 'like' with all the fields.

I want to build a query starting from the field names that:

  • casts Dates and Numbers to strings and lowercases them
  • if the field is an EntityPathBase (like availability) extracts the id and then again casts to lowercased string

Something like:

lower(cast(C.cvId as varchar(255))) like 'value'

for each field.

I can do this usign querydsl-sql module, but I want to achieve it using only the jpa module.

I'm not interested in the mechanism of creating the FULL 'where' clause (I know I have to use the BooleanBuilder, or at least, this is what I do in the sql version).

What I want to know is how to create the individual 'where' conditions basing on the field type.

I'm trying to use a PathBuilder but it seems that to use methods like "getString or getBoolean" you already have to know the type of the field that you are trying to extract. In my case, since I start just from the field name, I can't use these methods and I don't know how to identify the type of each field starting from the field name, so I'm stuck.


Solution

  • May be a bit ugly, but workable suggestion.

    Note, that the number of field types that PathBuilder accepts is quite limited.

    You definitely can find the field class from field name (using reflection or by maintaining a member map updated with each field).

    Just implement handling for each specific type.

    This can be ugly bunch of if..else or, for more elegant solution, create Map of type handlers [class->handler], each handler implements interface method to handle specific type.

    Pseudocode:

    //building query
    for each field
      Class fieldClass = findFieldClas(.., field) //use reflection or map
      PathHandler handler = handlers.get(fieldClass)
      handler.process( ...)
    
    //type handler interface
    public interface Handler{
      public xx process(? extends DataPathBase);
    }
    
    //specific type handler implementation
    public class BooleanHandler implements Handler{
      public xx process(? extends DataPathBase path){
        BooleanPath bPath = (BooleanPath)path;
        ...
    }
    
    //intitialize handlers map singleton or a factory in advance    
    handlers.put(BooleanPath.class, new BooleanHandler());
    ...
    

    Note this is a generic solution if you have many classes. If you have only one specific class, you can just create a permanent map of fieldName->Handler and avoid lookup for the field class.

    Again, this is by no means a pretty solution, but should work.