es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Java lambda conserva el valor de parámetro exterior de la primera llamada incluso en la segunda llamada.

El título puede que no sea muy descriptivo, pero trataré de resumir mi problema.

Estoy intentando crear consultas dinámicas utilizando Java Specifications. En mi caso, un DTO con filtros llega desde un punto final REST y para cada filtro intento crear una especificación como esta:

   private Specification <trafficdata> buildSpecificationQuery(TrafficDataRequestDTO requestDTO) {
       List <specification></specification><trafficdata>> specificationList = new ArrayList <> ();
       requestDTO.getFilters (). for Each (filter -> {
           specificationList.add(createSpecification(filter));
       });
       Especificación <tráfico de="" datos=""> especificación = Especificación. donde(specificationList. eliminar(0));

       specificationList.forEach(specification :: y);

       volver especificación;
   }

   private Specification <trafficdata> createSpecification(final TrafficDataFilterDTO input) {
       switch (input.getOperator ()) {
           caso iguales:
               devuelve (raíz, consulta, criteriosConstructor) -> criteriosConstructor.equals(root.get(input.getFieldName()), input.getValue());
           case NOT_EQUALS:
               devuelve (raíz, consulta, constructorCriterios) ->
               criteriaBuilder.notEqual(root.get(input.getFieldName()), input.getValue());
           caso comienza con:
               devuelve (raíz, consulta, constructorCriterios) ->
               criteriaBuilder.like(root.get(input.getFieldName()), input.getValue()+"%");
           case termina con:
               devuelve (raíz, consulta, constructorCriterios) ->
               criteriaBuilder.like(root.get(input.getFieldName()), "%"+input.getValue());
           caso contiene:
               devuelve (raíz, consulta, constructorCriterios) ->
               criteriaBuilder.like(root.get(input.getFieldName()), "%"+input.getValue()+"%");

           defecto:
               lanzar una nueva RuntimeException("Operación aún no compatible.");
       }
   }

El DTO de filtro se parece a esto:

@Data
public class TrafficDataFilterDTO {
    private String fieldName;
    private TrafficDataFilterOperator operator;
    private String value;
}

Lo extraño es que cuando llamo a este código para dos objetos de filtro diferentes, parece que las lambdas usan solo un valor de input y me devuelve una consulta solo para el primer criterio. Probablemente sea un problema relacionado con los argumentos lambda de Java, pero no puedo decir exactamente qué es.

Entonces, un escenario real:

Cuando tengo dos objetos de filtro diferentes:

{"fieldName": "version", "operator": "EQUALS", "value": "EN_UK"}, {"fieldName": "isManaged", "operator": "EQUALS", "value": true}

El método createSpecification(final TrafficDataFilterDTO input) se llama dos veces, con los valores de entrada correctos (para cada filtro), pero la línea que contiene la lambda criteriaBuilder.equals(root.get(input.getFieldName()), input.getValue()); se llama dos veces con el primer objeto de filtro

Esperando una solución y una explicación de por qué esto sucede.</tráfico>

Tags:  , ,

Answer

  1. Avatar for davy.ai

    El problema que estás enfrentando está relacionado con la expresión lambda y el cierre que crea. Cuando utilizas una expresión lambda, las variables utilizadas dentro de ella son esencialmente capturadas y almacenadas en un cierre. En tu caso, estás usando la variable “input” dentro de la expresión lambda, que está siendo capturada y almacenada en un cierre.

    Ahora bien, cuando llamas al método “createSpecification” dos veces con dos objetos diferentes de “TrafficDataFilterDTO”, estás creando esencialmente dos expresiones lambda diferentes, cada una con su propio cierre. Sin embargo, como la expresión lambda está utilizando la variable “input”, que está siendo almacenada en el cierre, ambas expresiones lambda terminan utilizando el mismo valor de “input”, que es el último valor pasado al método.

    Para solucionar este problema, puedes modificar tu método “createSpecification” para utilizar variables locales en lugar del parámetro del método. Aquí tienes una implementación actualizada:

    private Specification<trafficdata> createSpecification(final TrafficDataFilterDTO input) {
        final String fieldName = input.getFieldName();
        final String value = input.getValue();
    
        switch (input.getOperator()) {
            case EQUALS:
                return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get(fieldName), value);
            case NOT_EQUALS:
                return (root, query, criteriaBuilder) ->
                    criteriaBuilder.notEqual(root.get(fieldName), value);
            case STARTS_WITH:
                return (root, query, criteriaBuilder) ->
                    criteriaBuilder.like(root.get(fieldName), value + "%");
            case ENDS_WITH:
                return (root, query, criteriaBuilder) ->
                    criteriaBuilder.like(root.get(fieldName), "%" + value);
            case CONTAINS:
                return (root, query, criteriaBuilder) ->
                    criteriaBuilder.like(root.get(fieldName), "%" + value + "%");
    
            default:
                throw new RuntimeException("Operation not supported yet");
        }
    }
    

    En esta implementación actualizada, estamos almacenando los valores de “fieldName” y “value” en variables locales, que luego son utilizadas dentro de la expresión lambda. De esta manera, cada expresión lambda tiene su propio conjunto de variables locales, y no están afectadas por el cierre creado por la expresión lambda anterior.

    Con esta modificación, tu método “buildSpecificationQuery” debería poder crear consultas dinámicas correctamente para múltiples objetos de filtro.

Comments are closed.