The title might not be very descriptive, but I will try to resume my problem.
So, I am trying to create dynamic queries using Java Specifications. In my case a DTO with filters comes from REST endpoint and for each filter I am trying to create a specification like this:
private Specification<TrafficData> buildSpecificationQuery(TrafficDataRequestDTO requestDTO) {
List<Specification<TrafficData>> specificationList = new ArrayList<>();
requestDTO.getFilters().forEach(filter -> {
specificationList.add(createSpecification(filter));
});
Specification<TrafficData> specification = Specification.where(specificationList.remove(0));
specificationList.forEach(specification::and);
return specification;
}
private Specification<TrafficData> createSpecification(final TrafficDataFilterDTO input) {
switch (input.getOperator()) {
case EQUALS:
return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get(input.getFieldName()), input.getValue());
case NOT_EQUALS:
return (root, query, criteriaBuilder) ->
criteriaBuilder.notEqual(root.get(input.getFieldName()), input.getValue());
case STARTS_WITH:
return (root, query, criteriaBuilder) ->
criteriaBuilder.like(root.get(input.getFieldName()), input.getValue() "%");
case ENDS_WITH:
return (root, query, criteriaBuilder) ->
criteriaBuilder.like(root.get(input.getFieldName()), "%" input.getValue());
case CONTAINS:
return (root, query, criteriaBuilder) ->
criteriaBuilder.like(root.get(input.getFieldName()), "%" input.getValue() "%");
default:
throw new RuntimeException("Operation not supported yet");
}
}
The filter DTO looks like this:
@Data
public class TrafficDataFilterDTO {
private String fieldName;
private TrafficDataFilterOperator operator;
private String value;
}
The strange thing is when I call this code for two different filter objects, it seems that the lambdas uses just one input
value and it returns me a query just for the first criteria. It is must probably a problem related to java lambda arguments, but I can't tell exactly what it is.
So a real scenario:
When I have two different filter objects:
{ "fieldName": "version", "operator": "EQUALS", "value": "EN_UK" }, { "fieldName": "isManaged", "operator": "EQUALS", "value": true }
the method createSpecification(final TrafficDataFilterDTO input) is called twice, with the correct input values (for each filter), but the line containing the lambda
criteriaBuilder.equal(root.get(input.getFieldName()), input.getValue());
is called twice with the first filter object
Waiting for a solution and an explanation why this happens.
CodePudding user response:
Specificiation.and()
returns a new Specification
and therefore your code for joining the specifications doesn't work.
You need to join the specifications like this:
Specification<TrafficData> specification = Specification.where(specificationList.remove(0));
return specificationList.stream().reduce(specification, Specification::and);