En una aplicación web que utiliza Spring Data JPA con Hibernate, utilizamos la funcionalidad web pagination para proporcionar funciones de paginación y clasificación en varias listas de entidades.Spring Data JPA no válido page.sort Parámetros
@Controller
public class MyEntityController {
@RequestMapping(method = RequestMethod.GET)
public ModelAndView list(Pageable pageable) { ... }
}
@Configuration
public class MyWebMvcConfig extends WebMvcConfigurationSupport {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
super.addArgumentResolvers(argumentResolvers);
argumentResolvers.add(new PageableArgumentResolver());
}
}
public interface MyEntityRepository extends PagingAndSortingRepository<MyEntity, String> {
Page<MyEntity> findByPropertyX(String propertyX, Pageable pagable);
}
Esto permite propiedades de la entidad que se definirán en el HTML representado como una especie especial request parameters, donde el valor page.sort
realidad coincide con una propiedad en la entidad en el que se ordenan.
<table>
<thead>
<tr>
<th><a href="?page.sort=propertyX&page.sort.dir=asc">Property X</a></th>
<th><a href="?page.sort=propertyY&page.sort.dir=asc">Property Y</a></th>
</tr>
</thead>
<tbody>...</tbody>
</table>
Esto produce una URL que se obtiene como:
http://host/context-root/entities/?page.sort=propertyX&page.sort.dir=asc
El es que los usuarios pueden modificar la dirección URL a usar inválida page.sort
propiedades problema de que los nombres de cualquiera de referencia inexistentes columna/propiedad, o peor aún, que utilizan caracteres de consulta JPA no válidos que dan como resultado una sintaxis no válida.
Por ejemplo, si la URL es modificado para ordenar en "noSuchProperty":
http://host/context-root/entities/?page.sort=noSuchProperty&page.sort.dir=asc
Pero esta propiedad no existe, se lanzará la siguiente excepción:
java.lang.IllegalArgumentException: No property noSuchProperty found for type class com.my.company.MyEntity
at org.springframework.data.repository.query.parser.Property.<init>(Property.java:76)
. . .
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:86)
. . .
at $Proxy68.findByPropertyX(Unknown Source)
at com.my.company.MyEntityRepository.findByPropertyX(MyEntityRepository.java:17
Del mismo modo, si la URL se modifica a un carácter de sintaxis de consulta no válida, como "" ":
http://host/context-root/entities/?page.sort=%22&page.sort.dir=asc
Lo que sigue o se producirá:
java.lang.StackOverflowError
java.util.regex.Pattern$GroupTail.match(Pattern.java:4227)
. . .
org.springframework.data.repository.query.parser.Property.create(Property.java:326)
org.springframework.data.repository.query.parser.Property.create(Property.java:326)
org.springframework.data.repository.query.parser.Property.create(Property.java:326)
org.springframework.data.repository.query.parser.Property.create(Property.java:326)
(Hay también un tercer sabor de excepciones que resulta en una org.hibernate.QueryException
cuando el @Query
se define explícitamente en el método de repositorio.)
primavera de datos JPA abstrae los detalles de la clasificación, paginación y manejo de estos parámetros; sin embargo, no parece manejar con elegancia estos escenarios (es decir, cuando se especifica un parámetro de clasificación no válido).
Podríamos agregar algo de lógica personalizada adicional para validar que la propiedad de ordenamiento realmente existe en la entidad; sin embargo, me pregunto si existe un enfoque más centralizado y más limpio para hacer esto, de modo que no perdamos los beneficios y la simplicidad de las abstracciones de Spring Data JPA. Usamos esta capacidad de clasificación en toda nuestra aplicación con muchas entidades diferentes, por lo que, idealmente, desearíamos un enfoque más genérico, en lugar de tener que definir explícitamente o verificar las propiedades de clasificación para cada página de entidad solicitada.
Específicamente, ampliamos el PageableArgumentResolver
para aceptar un valor predeterminado de orden anotado que se proporciona en nuestro controlador (no ilustrado en los ejemplos del código por simplicidad), por lo que nos gustaría recurrir a este orden de clasificación predeterminado, o solo el orden de clasificación predeterminado para la entidad, en lugar de lanzar una excepción.
Algunas ideas e intentos .. Podría usar un QueryCreationListener
para interceptar la creación de la consulta y obtener el parámetro de ordenamiento; sin embargo, no puedo modificar la consulta en ese momento. O bien, puedo extender y usar un PageableArgumentResolver
personalizado (ya lo estamos haciendo) para obtener los parámetros de clasificación; sin embargo, no tengo acceso a la entidad en ese punto, ni la capacidad de determinar si la entidad tiene realmente una propiedad con ese nombre.Podríamos declarar explícitamente las propiedades compatibles; sin embargo, una vez más, esto frustra la idea de manejar de forma central y automática este escenario sin requerir un conocimiento específico o declarado de las entidades.
¿Hay algún otro tipo de interceptor o construcción similar que pueda utilizar para validar centralmente los parámetros de clasificación paginable y modificarlos si es necesario antes de invocar la consulta? ¿O hay algún tipo de configuración o forma en que Spring pueda manejar automáticamente este escenario de manera que maneje con mayor gracia los parámetros de clasificación no válidos?