Spring Data Rest - 按嵌套属性排序

17

我有一个使用Spring Boot 1.5.1和Spring Data Rest的数据库服务。我将实体存储在MySQL数据库中,并使用Spring的PagingAndSortingRepository通过REST访问它们。我找到了这篇文章,其中指出支持按嵌套参数进行排序,但我找不到按嵌套字段排序的方法。

我有以下类:

@Entity(name = "Person")
@Table(name = "PERSON")
public class Person {
    @ManyToOne
    protected Address address;

    @ManyToOne(targetEntity = Name.class, cascade = {
        CascadeType.ALL
    })
    @JoinColumn(name = "NAME_PERSON_ID")
    protected Name name;

    @Id
    protected Long id;

    // Setter, getters, etc.
}

@Entity(name = "Name")
@Table(name = "NAME")
public class Name{

    protected String firstName;

    protected String lastName;

    @Id
    protected Long id;

    // Setter, getters, etc.
}
例如,当使用该方法时:
Page<Person> findByAddress_Id(@Param("id") String id, Pageable pageable);

调用URI http://localhost:8080/people/search/findByAddress_Id?id=1&sort=name_lastName,desc 时,Spring完全忽略了排序参数。

参数 sort=name.lastNamesort=nameLastName 也都不起作用。

我是不是在错误地构建REST请求,或者缺少某些配置?

谢谢!


3
name.lastName是要使用的属性。在Hopper版本中,按嵌套属性排序对我来说效果很好,但是在Ingalls版本的RC版本中,我遇到了以下错误。据报告,该问题已经得到修复,但我还没有尝试过。https://jira.spring.io/browse/DATAREST-976?jql=text%20~%20%22sort%20nested%22%20ORDER%20BY%20created%20DESC - Alan Hay
@AlanHay,你是个厉害的家伙,在我们降级到Hopper版本<spring.data.jpa.version>1.10.10.RELEASE</spring.data.jpa.version><spring.data.rest.webmvc.version>2.5.10.RELEASE</spring.data.rest.webmvc.version>后和我一起工作。 - Khaled Lela
@AlanHay 顺便说一句,我尝试了v3.0.0.M3,报告说已经修复了,但是在我的机器上运行不起来。 - Khaled Lela
有人能解决这个问题吗?它在SDR 3.0.2.RELEASE和Spring Boot 1.5.8上无法工作。我正在使用sort=property_subproperty。谢谢。 - drenda
我们遇到了相同的问题(使用Spring Boot 1.5.9和Spring Data REST 2.6.9)。我们尝试用于排序的嵌套属性被包含在一个Jackson Mixin中,其中包含@JsonProperty(access = READ_ONLY)。删除此注释可导致该嵌套属性的正确排序行为。 - jensfischerhh
4个回答

5
我找到的解决方法是创建一个额外的只读属性,仅用于排序目的。在上面的示例基础上构建:
@Entity(name = "Person")
@Table(name = "PERSON")
public class Person {

    // read only, for sorting purposes only
    // @JsonIgnore // we can hide it from the clients, if needed
    @RestResource(exported=false) // read only so we can map 2 fields to the same database column
    @ManyToOne
    @JoinColumn(name = "address_id", insertable = false, updatable = false) 
    private Address address;

    // We still want the linkable association created to work as before so we manually override the relation and path
    @RestResource(exported=true, rel="address", path="address")
    @ManyToOne
    private Address addressLink;

    ...
}

提出的解决方法的缺点是,我们现在必须明确地复制所有要支持嵌套排序的属性。另一个缺点是,我们无法将嵌套属性隐藏在客户端中。在我的原始答案中,我建议添加@JsonIgnore,但显然这会破坏排序。

1
我已经调试过了,看起来问题和Alan提到的一样。我找到了一个解决方法:
创建自己的控制器,注入你的仓库,可选地注入投影工厂(如果你需要投影)。实现get方法来委托调用你的仓库。
 @RestController
 @RequestMapping("/people")
 public class PeopleController {

    @Autowired
    PersonRepository repository;

    //@Autowired
    //PagedResourcesAssembler<MyDTO> resourceAssembler;

    @GetMapping("/by-address/{addressId}")
    public Page<Person> getByAddress(@PathVariable("addressId") Long addressId, Pageable page)  {

        // spring doesn't spoil your sort here ... 
        Page<Person> page = repository.findByAddress_Id(addressId, page)

        // optionally, apply projection
        //  to return DTO/specifically loaded Entity objects ...
        //  return type would be then PagedResources<Resource<MyDTO>>
        // return resourceAssembler.toResource(page.map(...))

        return page;
    }

}

这对我来说在2.6.8.RELEASE版本中有效;问题似乎存在于所有版本中。

0

0

来自Spring Data REST文档:

不支持通过可链接的关联(即指向顶级资源的链接)进行排序。

https://docs.spring.io/spring-data/rest/docs/current/reference/html/#paging-and-sorting.sorting

我发现的另一种选择是使用@ResResource(exported=false)。这不是有效的(特别是对于旧版Spring Data REST项目),因为它避免了资源/实体被加载HTTP链接:

JacksonBinder
BeanDeserializerBuilder updateBuilder throws
 com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of ' com...' no String-argument constructor/factory method to deserialize from String value

我尝试使用注释激活可链接关联的排序,但没有成功,因为我们总是需要覆盖JacksonMappingAwareSortTranslator.SortTranslator类中的 mappPropertyPath方法来检测注释:

            if (associations.isLinkableAssociation(persistentProperty)) {
                if(!persistentProperty.isAnnotationPresent(SortByLinkableAssociation.class)) {
                    return Collections.emptyList();
                }
            }

注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SortByLinkableAssociation {
}

在项目标记关联时,请使用@SortByLinkableAssociation
@ManyToOne
@SortByLinkableAssociation
private Name name;

实际上,我并没有找到一个明确且成功的解决方案来解决这个问题,但我决定将其公开以便让大家思考,甚至Spring团队可以考虑在下一次发布中加入。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接