Default Sort on a Spring Data JPA Repository Method with Custom Query and Pageable Parameter

北城以北 提交于 2020-01-03 19:20:21

问题


I have the following repository method that works exactly the way I need it to iff the user provides a sort column in the page parameter:

public interface IdentityRepository extends JpaRepository<Identity, String> {

    @Query("select distinct ident from Identity ident left outer join ident.authorities authority "
        + "where ("
            + "(:src is null or ident.source = :src) and "
            + "(:org is null or ident.organization = :org) and "
            + "(:auth is null or authority.authority = :auth) and "
            + "(:authSrc is null or authority.authoritySource = :authSrc))")
    @RestResource(path="filter")
    public Page<Identity> findWithFilter(
        @Param("src") String source, 
        @Param("org") String org, 
        @Param("auth") Authority auth, 
        @Param("authSrc") AuthoritySource authSrc, 
        Pageable page);
...
}

If the caller provides a page count, but not a sort column, they will get back the correct number of results when retrieving all the pages. However, many of the entities will be duplicated, so even though the result count is correct, many expected entities are missing and others are duplicated (or triplicated).

What I'm wondering is if there is a way to provide a default sort column and direction if the user does not specify one. I've learned that @EnableSpringDataWebSupport can help here, but we're not using Spring MVC, so I don't have any controllers to attach the @SortDefaults to. We are using Spring Data Rest though. Also, I've tried changing the method name to findWithFilterOrderByIdAsc, but that did not seem to help. Ran across this issue in the Spring JIRA, which I believe is exactly what I need, but until it's resolved, does anyone know of a work around?

Here's my entity...

@Entity
@Table(name = "identity", indexes = { @Index(columnList = "user_id", unique = true) })
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Audited
public class Identity implements Serializable, Identifiable<String> {

    /** 
     * The unique identifier for this identity within the IDD application.
     */
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "IDDUidGenerator")
    @GenericGenerator(name = "IDDUidGenerator")
    private String id;
    /**
     * The name of the identity provider wherein this identity is originally defined.
     */
    @Column(name = "source")
    private String source = INTERNAL_SOURCE;
    /**
     * The unique identifier for this identity within the customer's identity provider.
     */
    @NotNull
    @Column(name = "user_id", nullable = false, unique = true)
    private String userId;
    /**
     * The roles this identity is authorized to perform.
     */
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "identity", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<IdentityAuthority> authorities = new HashSet<>();
...
}

And its sub-entity...

@Entity
@Table(name = "identity_authority")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Audited
public class IdentityAuthority implements Serializable, Identifiable<Long> {

    private static final long serialVersionUID = -5315412946768343445L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @XmlTransient
    @JsonIgnore
    private Long id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "identity_id", nullable = false)
    @XmlTransient
    @JsonIgnore
    private Identity identity;

    @Enumerated(EnumType.STRING)
    @Column(name = "authority", length = 20, nullable = false)
    private Authority authority;

    @Enumerated(EnumType.STRING)
    @Column(name = "authority_source", length = 30, nullable = false)
    private AuthoritySource authoritySource;
...
}

Here's the test case I ran to demonstrate the problem...

@Test
public void testPagedRequestsReturnAllResults() {
    // Create identities
    String source = "One Hundred Identities Generator";
    int numIdentities = 100;
    int pageSize = 5;
    List<Identity> input = new ArrayList<>();
    for (int i=0; i<numIdentities; i++) {
        Identity identity = new Identity();
        identity.setUserId(UUID.randomUUID().toString());
        identity.setSource(source);
        input.add(identity);
    }

    // Save identities
    List<Identity> output = repository.saveBulk(input);
    Set<String> savedIds = collectIds(output, null);
    assertThat(savedIds.size()).isEqualTo(numIdentities);

    // Test Sorted Find Filter with Paging (THIS PASSES)
    Pageable pageRequest = new PageRequest(0, pageSize, new Sort(Direction.ASC, "id"));
    Set<String> foundPagedIds = new HashSet<>();
    do {
        Page<Identity> page = repository.findOrderByIdAsc(source, null, null, null, pageRequest);
        List<Identity> foundIdentities = page.getContent();
        foundPagedIds = collectIds(foundIdentities, foundPagedIds);
        pageRequest = page.nextPageable();
    } while (pageRequest != null);
    assertThat(foundPagedIds.size()).isEqualTo(numIdentities);
    assertThat(foundPagedIds).isEqualTo(savedIds);

    // Test Unsorted Find Filter with Paging (THIS FAILS)
    pageRequest = new PageRequest(0, pageSize);
    foundPagedIds = new HashSet<>();
    do {
        Page<Identity> page = repository.findOrderByIdAsc(source, null, null, null, pageRequest);
        List<Identity> foundIdentities = page.getContent();
        foundPagedIds = collectIds(foundIdentities, foundPagedIds);
        pageRequest = page.nextPageable();
    } while (pageRequest != null);
    assertThat(foundPagedIds.size()).isEqualTo(numIdentities);
    assertThat(foundPagedIds).isEqualTo(savedIds);
}

来源:https://stackoverflow.com/questions/38614247/default-sort-on-a-spring-data-jpa-repository-method-with-custom-query-and-pageab

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!