Stephane
Stephane

Reputation: 12740

How to prevent sorting on a given column?

My repository allows for sorting on any column when retrieving a list of users:

public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {

    @Query("SELECT u FROM User u")
    public Page<User> all(Pageable page);

}

The trouble is that a user has some properties that cannot offer sorting, like his confirmedEmail property.

@Entity
@Table(name = "user_account")
@SequenceGenerator(name = "id_generator", sequenceName = "user_account_id_seq", allocationSize = 10)
public class User extends AbstractEntity {

    @Column(nullable = false)
    private String firstname;
    @Column(nullable = false)
    private String lastname;
    @Column(nullable = false, unique = true)
    private EmailAddress email;
    @Column(nullable = false)
    private boolean confirmedEmail;

}

How can I prevent the Pageable argument from sorting on this boolean confirmedEmail property ?

I stumbled upon this issue when I sorted by clicking on the Confirmed header in my Angular data table.

This front-end event triggered the following request:

SELECT u FROM com.thalasoft.user.data.jpa.domain.User u order by u.confirmed asc

I know I can make this data table column not sortable, and I did. But I'd like to also have a server side safety in place.

As a side note, I wonder if I could also provide a default sorting if none is specified in the client request.

UPDATE: I created the utility method:

  public static final Sort stripColumnsFromSorting(Sort sort, Set<String> nonSortableColumns) {
    return Sort.by(sort.stream().filter(order -> {
      return !nonSortableColumns.contains(order.getProperty());
    }).collect(Collectors.toList()));
  }

which I call like:

Set<String> nonSortableColumns = new HashSet<String>(Arrays.asList("id", "confirmedEmail"));

public ResponseEntity<PagedResources<UserResource>> all(@PageableDefault(sort = { "lastname", "firstname" }, direction = Sort.Direction.ASC) Pageable pageable, Sort sort,
        PagedResourcesAssembler<User> pagedResourcesAssembler, UriComponentsBuilder builder) {
    sort = CommonUtils.stripColumnsFromSorting(sort, nonSortableColumns);
    userService.addSortToPageable(pageable, sort);

But it is still invoking the sorting on the non sortable column:

Invoking 'com.thalasoft.user.rest.controller.UserController.all' with arguments [Page request [number: 0, size 5, sort: confirmedEmail: DESC], confirmedEmail: DESC, org.springframework.data.web.MethodParameterAwarePagedResourcesAssembler@78817e7e, org.springframework.web.servlet.support.ServletUriComponentsBuilder@3e49647a]

Upvotes: 4

Views: 4820

Answers (1)

Cepr0
Cepr0

Reputation: 30289

I think you can check Pageable argument of your controller method and then remove from it unnecessary fields, something like this:

public ResponseEntity<?> myControllerMethod(..., Pageable page) {

    Sort newSort = Sort.by(page.getSort()
        .get()
        .filter(order -> !order.getProperty().equals("confirmedEmail"))
        .collect(Collectors.toList()));

    PageRequest newPage = PageRequest.of(page.getPageNumber(), page.getPageSize(), newSort);

    // using newPage instead of page...          
}

To specify a default order you can use @PageableDefault annotation, for example:

public ResponseEntity<?> myControllerMethod(..., @PageableDefault(sort = "lastname", direction = ASC) Pageable page) {
    //...
}

Upvotes: 3

Related Questions