如何使用Spring Data Sort动态排序自定义JPQL返回的嵌套实体?
问题描述
我在Spring Data Repository中定义了如下JPQL查询:
public interface CarRepository extends Repository<Car, Integer> { @Query("select distinct c.model from Car c where c.id in :ids") Set<Model> findDistinctModelByIdIn(@Param("ids") Set<Integer> ids, Sort sort); }
客户端通过Spring Data REST调用该查询:
http://localhost:8080/api/cars/search/findDistinctModelByIdIn?ids=1,33,55,43&sort=model.name,desc
但返回结果未排序。开启org.springframework.web的trace日志后,发现我的请求中Sort被解析为UNSORTED,而相同依赖版本、代码及配置的其他项目中,Sort能被正常解析为model.name: DESC。
域模型如下:
@Entity @Data public class Car { @Id private Long id; @ManyToOne private Model model; } @Entity @Data public class Model { @Id private Long id; private String name; }
问题分析
核心原因是Spring Data REST默认基于Repository管理的**主实体(Car)**校验并解析sort参数,但你的请求中sort=model.name是针对关联实体Model的属性,不属于Car的直接属性,导致参数解析校验失败,最终Sort被置为UNSORTED。
解决方案
方案1:在JPQL中通过SpEL直接引用Sort参数
修改Repository的JPQL查询,使用Spring Data的SpEL表达式?#{#sort}将传入的Sort参数直接转换为JPQL的order by子句:
public interface CarRepository extends Repository<Car, Integer> { @Query("select distinct c.model from Car c where c.id in :ids order by ?#{#sort}") Set<Model> findDistinctModelByIdIn(@Param("ids") Set<Integer> ids, Sort sort); }
此时客户端请求需调整sort参数为Model的直接属性,无需加model.前缀:
http://localhost:8080/api/cars/search/findDistinctModelByIdIn?ids=1,33,55,43&sort=name,desc
方案2:自定义Sort参数解析器
如果需要保留客户端请求的model.name前缀,可自定义参数解析逻辑去掉前缀:
- 实现自定义Sort解析器:
@Component public class CustomSortResolver extends SortHandlerMethodArgumentResolver { @Override protected Sort parseParameterIntoSort(String[] source, SortDefaults defaults) { String[] adjustedSource = Arrays.stream(source) .map(s -> s.replace("model.", "")) .toArray(String[]::new); return super.parseParameterIntoSort(adjustedSource, defaults); } }
- 配置Spring Data REST使用自定义解析器:
@Configuration public class RestConfig implements RepositoryRestConfigurer { @Autowired private CustomSortResolver customSortResolver; @Override public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) { configurer.addCustomResolver(customSortResolver); } }
配置完成后,客户端原请求sort=model.name,desc即可正常生效。
方案3:使用Projection定义返回结构
定义Model的Projection,让Spring Data REST识别返回类型的属性,从而正常解析sort参数:
public interface ModelProjection { String getName(); // 按需添加其他属性 }
修改Repository方法的返回类型:
@Query("select distinct c.model from Car c where c.id in :ids") Set<ModelProjection> findDistinctModelByIdIn(@Param("ids") Set<Integer> ids, Sort sort);
客户端请求使用sort=name,desc即可实现排序。
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

