问题
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