Spring @PathVariable es la anotación que nos sirve dentro de Spring framework para configurar variables dentro de los propios segmentos de la URL algo que a nivel de Arquitecturas REST es imprescindible. Vamos a ver un par de ejemplos de como construir unas urls REST que se apoyen en este tipo de anotación. Para ello vamos a construir la clase Facturas y la clase LineaFactura que tienen una relación de 1 a n y nos ayude a entender mejor este tipo de variables.
package com.arquitecturajjava.web; import java.util.ArrayList; import java.util.List; import com.fasterxml.jackson.annotation.JsonIgnore; public class Factura { private int numero; private String concepto; @JsonIgnore private List<LineaFactura> lineas= new ArrayList<LineaFactura>(); public Factura(int numero, String concepto) { super(); this.numero = numero; this.concepto = concepto; } public int getNumero() { return numero; } public void setNumero(int numero) { this.numero = numero; } public String getConcepto() { return concepto; } public void setConcepto(String concepto) { this.concepto = concepto; } public List<LineaFactura> getLineas() { return lineas; } public void setLineas(List<LineaFactura> linea) { this.lineas = linea; } public void addLinea(LineaFactura linea) { this.lineas.add(linea); } }
package com.arquitecturajjava.web; public class LineaFactura { private int numero; private String concepto; private double importe; public LineaFactura(int numero, String concepto, double importe) { super(); this.numero = numero; this.concepto = concepto; this.importe = importe; } public int getNumero() { return numero; } public void setNumero(int numero) { this.numero = numero; } public String getConcepto() { return concepto; } public void setConcepto(String concepto) { this.concepto = concepto; } public double getImporte() { return importe; } public void setImporte(double importe) { this.importe = importe; } }
Tenemos dos clases relacionadas . Podemos usar un clásico despliegue de Spring Boot para una aplicación web y generar un Servicio REST que incluya el acceso a una lista de Facturas y Lineas como variables estáticas:
package com.arquitecturajjava.web; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/facturas") public class FacturaREST { static List<Factura> lista= new ArrayList<Factura>(); static { Factura f= new Factura(1,"informatica"); f.addLinea(new LineaFactura(1,"auricular",200)); f.addLinea(new LineaFactura(2,"telefono",300)); Factura f2= new Factura(2,"alimentacion"); f2.addLinea(new LineaFactura(1,"galletas",2)); f2.addLinea(new LineaFactura(2,"leche",1)); Factura f3= new Factura(1,"limpieza"); f3.addLinea(new LineaFactura(1,"gel",2)); f3.addLinea(new LineaFactura(2,"jabon",4)); lista.add(f); lista.add(f2); lista.add(f3); } @RequestMapping public List<Factura> buscarTodas() { return lista; }
En un primer momento solo tenemos la url de Facturas.
Con la cual accedemos al listado por completo :
Es momento de añadir una url que nos devuelve una única Factura y para ello usaremos @PathVariable que nos permite añadir una variable en nuestros métodos para filtrar la lista y quedarnos con un único elemento:
@RequestMapping("{numero}") public Optional<Factura> buscarUna( @PathVariable int numero) { return lista.stream().filter((f)->f.getNumero()==numero).findFirst(); }
La estructura es :
En este caso la url de filtrado será /facturas/1
Hemos añadido una variable a la url de acceso :
Spring @PathVariable
El uso de @PathVariable es muy común ya que en muchas casuísticas tenemos la estructura en forma de agregado , es decir. ¿Cómo podemos acceder a la linea de una factura concreta? . Es relativamente sencilla basta con añadir un @PathVariable adicional y generar la estructura deseada.
@RequestMapping("{numero}/lineas/{numerolinea}") public Optional<LineaFactura> buscarUna( @PathVariable int numero,@PathVariable int numerolinea) { Optional<Factura> factura=lista.stream().filter((f)->f.getNumero()==numero).findFirst(); if (factura.isPresent()) { return factura.get() .getLineas().stream().filter((f)->f.getNumero()==numerolinea).findFirst(); } return Optional.empty(); }
Aquí podemos ver como cada segmento incluye {} para identificar a ambas variables con diferente nombre {numero} y {numerolinea} .
Podemos verlo en el explorador :