在使用Spring Data JPA和Hibernate的Web应用程序中,我们利用web pagination功能在各种实体列表中提供分页和排序功能 .
@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中将实体属性定义为特殊排序request parameters,其中 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)
(还有第三种异常,当在Repository方法中明确定义 @Query
时会产生 org.hibernate.QueryException
. )
Spring Data JPA抽象出这些参数的排序,分页和处理的细节;但是,它似乎没有优雅地处理这些场景(即指定了无效的排序参数) .
我们可以添加一些额外的自定义逻辑来验证实体上实际存在sort属性;但是,我想知道是否有更清晰,更集中的方法来做到这一点,这样我们就不会失去Spring Data JPA抽象的好处和简单性 . 我们在整个应用程序中使用这种排序功能与许多不同的实体,所以理想情况下,我们需要更多的通用方法,而不是必须明确定义或检查所请求的每个实体页面的排序属性 .
具体来说,我们实际上扩展 PageableArgumentResolver
以接受我们的控制器中提供的带注释的排序默认值(为简单起见未在代码示例中说明),因此我们只想回退到此默认排序顺序,或者只是默认排序实体的订单,而不是抛出异常 .
一些想法和尝试..我可以使用 QueryCreationListener
来拦截查询创建并获取sort参数;但是,我当时无法修改查询 . 或者,我可以扩展并使用自定义 PageableArgumentResolver
(我们已经这样做)来获取排序参数;但是,我当时无权访问实体,也无法确定实体是否实际拥有该名称的属性 . 我们可以明确声明支持的属性;然而,这又失败了集中和自动处理这种情况的想法,而不需要具体或声明的实体知识 .
是否有任何其他类型的拦截器或类似构造可用于集中验证可分页排序参数,并在调用查询之前根据需要进行修改?或者是否有任何类型的配置或方式,Spring可以自动处理这种情况,以便它更优雅地处理无效的排序参数?
2 回答
我正在看一下代码,我认为更多的堆栈跟踪会有所帮助 . 但是从我所看到的情况来看,如果您想重写一些Spring代码,我认为有两个地方可能需要解决 .
这里有两种情况,在第一种情况下,您传递的是对象/表中不存在的排序字段 . 你真正想要的是,这个坏参数一直被忽略,而不仅仅是在传递1
PageableArgumentResolver
] 1时 . 我认为它应该是AbstractQueryCreator上的一个选项(因此,JpaQueryCreator)可以忽略排序中的错误参数 .应该解决的第二部分可能是PageableArgumentResolver . 如果你传递空字符串或像
%20
那样没有意义的东西,那么它应该忽略该参数而不将它发送到PageRequest
.快乐的黑客和好运 . 阅读你的帖子让我意识到我的网站容易受到同样的问题,我真的没有好的解决方案 .
我认为改进
PageableArgumentResolver
可以优雅地处理这些场景 . 它可以尝试从String
创建PropertyPath
实例作为Sort
,从而确保它是有效的 . 我有点不知道是否有必要简单地删除默认的无效String
,这将返回结果根本没有排序 . 这可能是最无缝的体验,但也可能导致尝试找出结果未排序的繁琐尝试 .但是,它 . 如果您可以针对Spring Data Commons筹集JIRA票,并且只是在这里链接此票,那将会很酷 . 如果你已经充实了可行的实现,请随意打开拉取请求 . 谢谢你把这个带到了 table 上!