Home > Software design >  Java lambda keeps outer param value of first call even on second call
Java lambda keeps outer param value of first call even on second call

Time:11-30

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);
  • Related