Spring Data Rest - 按嵌套属性排序

Posted

技术标签:

【中文标题】Spring Data Rest - 按嵌套属性排序【英文标题】:Spring Data Rest - sort by nested property 【发布时间】:2017-07-04 21:48:42 【问题描述】:

我有一个使用 Spring Boot 1.5.1 和 Spring Data Rest 的数据库服务。我将我的实体存储在 mysql 数据库中,并使用 Spring 的 PagingAndSortingRepository 通过 REST 访问它们。我发现this 声明支持按嵌套参数排序,但我找不到按嵌套字段排序的方法。

我有这些课程:

@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 请求是错误的,还是缺少一些配置?

谢谢!

【问题讨论】:

name.lastName 将是要使用的属性。在 Hopper 版本中按嵌套属性排序对我来说很好,但我在 Ingalls 版本的 RC 版本中确实遇到了以下错误。这被报告为已修复,但我还没有尝试过。 jira.spring.io/browse/… @AlanHay 你是Man,在降级到Hopper 版本后与我合作&lt;spring.data.jpa.version&gt;1.10.10.RELEASE&lt;/spring.data.jpa.version &lt;spring.data.rest.webmvc.version&gt;2.5.10.RELEASE&lt;/spring.data.rest.webmvc.version&gt; @AlanHay 顺便说一句,我试过v3.0.0.M3 报告说已修复但无法与我合作。 有人能解决这个问题吗?它不适用于 SDR 3.0.2.RELEASE 和 Spring Boot 1.5.8。我正在使用 sort=property_subproperty。谢谢 我们遇到了同样的问题(使用 Spring Boot 1.5.9 和 Spring Data REST 2.6.9)。我们尝试用于排序的嵌套属性被包含@JsonProperty(access = READ_ONLY) 的 Jackson Mixin 覆盖。删除此注释会导致此嵌套属性的正确排序行为。 【参考方案1】:

我发现的解决方法是创建一个额外的只读属性,仅用于排序目的。基于上面的示例:

@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,但显然这会破坏排序。

【讨论】:

【参考方案2】:

我通过它进行了调试,它看起来像 Alan 提到的问题。

我找到了可能有帮助的解决方法:

创建自己的控制器,注入您的 repo 和可选的投影工厂(如果您需要投影)。实现 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;这个问题似乎存在于所有版本中。

【讨论】:

【参考方案3】:

来自 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

我尝试在 annotations 的帮助下通过可链接关联激活排序,但没有成功,因为我们总是需要覆盖 JacksonMappingAwareSortTranslator.SortTranslatormappPropertyPath 方法来检测注释:

            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 团队考虑将其包含在下一个版本中。

【讨论】:

【参考方案4】:

当我们想要按链接实体排序时,请参阅https://***.com/a/66135148/6673169 了解可能的解决方法/hack。

【讨论】:

以上是关于Spring Data Rest - 按嵌套属性排序的主要内容,如果未能解决你的问题,请参考以下文章

Spring Data Rest - 按多个属性排序

Spring Data Rest with Spring Security - 按当前用户查找所有内容

如何按角色限制对 Spring Data REST 投影的访问?

如何让Spring的Data Rest Repository按名称而不是id来检索数据

Spring Data REST 的 QueryDSL 集成,用于查询实体中集合映射的子属性

使用 Spring Data REST,为啥 @Version 属性成为 ETag 并且不包含在表示中?