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