Append default order by id to Pageable with Spring Data
Asked Answered
F

4

9

I'm using spring Pageable data and objects. When sorting by a field that can have the same values in database, changing page retrieves erroneous results.

I'm trying to add default order by id with HandlerInterceptorAdapter as follows:

My Interceptor:

public class OrderByIdWebArgumentResolver extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

        HandlerMethod hm= (HandlerMethod) handler;
        Method method = hm.getMethod();
        OrderById orderById = method.getAnnotation(OrderById.class);
        if (orderById != null) {
            for (MethodParameter parametro : hm.getMethodParameters()) {
                if (parametro.getGenericParameterType().equals(Pageable.class)) {
                    Map<String, String[]> parameters = request.getParameterMap();
                    String[] sortById = new String[2];
                    sortById[0] = "id";
                    sortById[0] = "desc";
                    parameters.put("sort", sortById);
                }

            }

        }

        return true;
    }
}

My Controller:

@OrderById
@RequestMapping(value = "/print", method = RequestMethod.GET)
public String printMensagges(@ModelAttribute MensaggesOption messageSelector, final ModelMap model,
       @SortDefault(sort = "date", direction = Sort.Direction.DESC) @PageableDefault(value = 5) final Pageable pageable, final Principal principal) {

    //I need the pageable has order by id here or in a service method
    List<Message> messages = messageService.findAll(pageable);

    return "/index";
}

I get this error:

java.lang.IllegalStateException: JBWEB000096: No modifications are allowed to a locked ParameterMap

Is there any way to add default order ever? Can you add on service methods that have a Pageable parameter?

Flivver answered 11/4, 2016 at 15:3 Comment(3)
Why not @SortDefault(sort = "id", direction = Sort.Direction.DESC)?Stacy
If you want to continue with this approach then you need to look at wrapping the HttpServletRequest in an HttpServletRequestWrapper: this will allow you to modify the request parameters as required. See here for an example: #16848680Dowdell
Because @DefaultSort is only when there is not order. I want to add. If the view table I select the "name" field, I want to sort first by "name" and then for "id"Flivver
R
21

Instead of adding a separate @SortDefault, you can just add the sorting in the @PageableDefault definition.

@PageableDefault(sort = {"name", "id"}, direction = Sort.Direction.DESC, value = 5) final Pageable pageable
Rafael answered 2/4, 2018 at 15:37 Comment(1)
This is not what the author of the question wants. The essence of the problem is that each time it is necessary to additionally add an identifier (by the last level) to the sorting.Photocopy
A
1

Add Sort and direction with @PageableDefault:

 public String printMensagges(@PageableDefault(size = 40, sort = "id", direction = Direction.DESC) Pageable pageable, Model model)
Adeleadelheid answered 11/5, 2020 at 18:57 Comment(0)
P
1

This is my solution (with customization field name for additional sort).

Annotation for parameter of pageable (for customize field name):

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface AdditionalSort {
    String value() default "id";
}

Configuration (here main answer for question):

@Configuration
public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        PageableHandlerMethodArgumentResolver pageResolver =
                new PageableHandlerMethodArgumentResolver(new AdditionalSortHandlerMethodArgumentResolver());
        pageResolver.setFallbackPageable(PageRequest.of(0, Integer.MAX_VALUE));
        argumentResolvers.add(pageResolver);
    }


    private static class AdditionalSortHandlerMethodArgumentResolver extends SortHandlerMethodArgumentResolver {

        private static final String DEFAULT_ADDITIONAL_SORT_FIELD = "id";

        @Override
        public Sort resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                    NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
            Sort sort = super.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
            String additionalSortField = parameter.hasParameterAnnotation(AdditionalSort.class) ?
                    parameter.getParameterAnnotation(AdditionalSort.class).value() : DEFAULT_ADDITIONAL_SORT_FIELD;
            return sort.and(Sort.by(additionalSortField));
        }

    }

}

Controller:

@GetMapping(value = "/byOrganization/{organizationId}", produces = APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<JsonItem<PageDto<EmployeeDto>>> getByOrganization(@PathVariable
                                                                                String organizationId,
                                                                        EmployeeFilter filter,
                                                                        @SortDefault(sort = CREATION_DATE, direction = DESC)
                                                                        @AdditionalSort("personId")
                                                                                Pageable pageable) {
    return ResponseEntity.ok(JsonItem.withData(employeeService.findEmployees(organizationId, filter, pageable)));
}
Photocopy answered 28/10, 2020 at 11:8 Comment(0)
A
0

Spring lets to provide multiple sorting rules, each with a different sorting direction using a dedicated annotation @SortDefault.SortDefaults. Note, that though @PageableDefault annotation allows for multiple sorting columns, it doesn't allow to apply different sorting directions, but only the single value that will be applied for all columns.

  @PageableDefault(size = 30)
  @SortDefault.SortDefaults({
      @SortDefault(
          sort = "createdAt", // column name of underlying DB table
          direction = Sort.Direction.DESC),
      @SortDefault(
          sort = "updatedAt", // column name of underlying DB table
          direction = Sort.Direction.ASC)
  }) Pageable pageable
Albrecht answered 15/6, 2023 at 12:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.