在将 Spring Data JPA 与 Hibernate 结合使用的 Web 应用程序中,我们利用Web 分页功能在各种实体列表中提供分页和排序功能。
@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);
}
这允许在呈现的 html 中将实体属性定义为特殊的排序请求参数,其中page.sort
值实际上与要排序的实体中的属性匹配。
<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>
这会产生一个结果 URL,例如:
http://host/context-root/entities/?page.sort=propertyX&page.sort.dir=asc
问题是用户可能会修改 URL 以使用page.sort
引用不存在的列/属性名称的无效属性,或者更糟的是,使用导致无效语法的无效 JPA 查询字符。
例如,如果 URL 被修改为按“noSuchProperty”排序:
http://host/context-root/entities/?page.sort=noSuchProperty&page.sort.dir=asc
但是这个属性不存在,会抛出如下异常:
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
同样,如果 URL 被修改为无效的查询语法字符,例如“””:
http://host/context-root/entities/?page.sort=%22&page.sort.dir=asc
会出现以下错误:
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)
(还有第三种类型的异常会导致在org.hibernate.QueryException
Repository@Query
方法上显式定义。)
Spring Data JPA 抽象出这些参数的排序、分页和处理的细节;但是,它似乎不能优雅地处理这些场景(即指定了无效的排序参数)。
我们可以添加一些额外的自定义逻辑来验证实体上确实存在排序属性;但是,我想知道是否有一种更清洁、更集中的方法来做到这一点,这样我们就不会失去 Spring Data JPA 抽象的好处和简单性。我们在整个应用程序中使用这种排序功能与许多不同的实体,所以理想情况下,我们需要更多的通用方法,而不是必须显式定义或检查请求的每个实体页面的排序属性。
具体来说,我们实际上将 扩展PageableArgumentResolver
为接受控制器中提供的带注释的排序默认值(为简单起见,代码示例中未说明),因此我们只想回退到这个默认排序顺序,或者只是默认排序顺序对于实体,而不是抛出异常。
一些想法和尝试..我可以使用 aQueryCreationListener
来拦截查询创建并获取排序参数;但是,此时我实际上无法修改查询。或者,我可以扩展并使用自定义PageableArgumentResolver
(我们已经在这样做)来获取排序参数;但是,我当时无权访问该实体,也无法确定该实体是否实际上具有该名称的属性。我们可以显式声明支持的属性;然而,这再次破坏了集中和自动处理这种情况而不需要特定或声明的实体知识的想法。
是否有任何其他类型的拦截器或类似构造可用于集中验证可分页排序参数并在调用查询之前根据需要进行修改?或者 Spring 是否有任何类型的配置或方式可以自动处理这种情况,以便更优雅地处理无效的排序参数?