9

在将 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&amp;page.sort.dir=asc">Property X</a></th>
            <th><a href="?page.sort=propertyY&amp;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.QueryExceptionRepository@Query方法上显式定义。)

Spring Data JPA 抽象出这些参数的排序、分页和处理的细节;但是,它似乎不能优雅地处理这些场景(即指定了无效的排序参数)。

我们可以添加一些额外的自定义逻辑来验证实体上确实存在排序属性;但是,我想知道是否有一种更清洁、更集中的方法来做到这一点,这样我们就不会失去 Spring Data JPA 抽象的好处和简单性。我们在整个应用程序中使用这种排序功能与许多不同的实体,所以理想情况下,我们需要更多的通用方法,而不是必须显式定义或检查请求的每个实体页面的排序属性。

具体来说,我们实际上将 扩展PageableArgumentResolver为接受控制器中提供的带注释的排序默认值(为简单起见,代码示例中未说明),因此我们只想回退到这个默认排序顺序,或者只是默认排序顺序对于实体,而不是抛出异常。

一些想法和尝试..我可以使用 aQueryCreationListener来拦截查询创建并获取排序参数;但是,此时我实际上无法修改查询。或者,我可以扩展并使用自定义PageableArgumentResolver(我们已经在这样做)来获取排序参数;但是,我当时无权访问该实体,也无法确定该实体是否实际上具有该名称的属性。我们可以显式声明支持的属性;然而,这再次破坏了集中和自动处理这种情况而不需要特定或声明的实体知识的想法。

是否有任何其他类型的拦截器或类似构造可用于集中验证可分页排序参数并在调用查询之前根据需要进行修改?或者 Spring 是否有任何类型的配置或方式可以自动处理这种情况,以便更优雅地处理无效的排序参数?

4

2 回答 2

3

我正在查看代码,我认为更多的堆栈跟踪会有所帮助。但据我所知,如果您有心情重写一些 Spring 代码,我认为您可能需要解决两个问题。

这里有两种情况,第一种情况是您传递对象/表中不存在的排序字段。您真正想要的是始终默默地忽略该错误参数,而不仅仅是在传入 1 PageableArgumentResolver] 1时。我认为它应该是AbstractQueryCreator(因此,JpaQueryCreator)上的一个选项来忽略排序中的错误参数。

应该解决的第二部分可能是PageableArgumentResolver. 如果您传递空字符串或没有意义的东西,%20那么它应该忽略该参数并且不将其发送到PageRequest.

快乐的黑客和好运。阅读您的帖子让我意识到我的网站容易受到同样问题的影响,我真的没有好的解决方案。

于 2012-10-16T16:06:21.807 回答
1

我认为改进PageableArgumentResolver以优雅地处理这些场景。它可以尝试从提交的 as中创建一个PropertyPath实例,从而确保它是一个有效的实例。我对在默认情况下简单地删除无效是否有意义感到有些不安,这将返回根本没有排序的结果。这可能是最无缝的体验,但也可能导致繁琐的尝试找出结果未排序的原因。StringSortString

然而,它。如果您可以针对 Spring Data Commons 提出 JIRA 票并在此处链接此票,那就太酷了。如果您已经充实了一个可行的实现,请随意打开一个拉取请求。谢谢你已经把它带到了餐桌上!

于 2012-06-28T17:19:37.560 回答