7

I need to be able to edit a Pageable object, more specifically one of the Sorts.

I have some DTO Member which has a property emailAddress. Unknown to the client, this is information that is gathered by a query from 2 different entities, either Account or Invite. A Member always has either one of these. This works well enough, I do a standard JPA query in a repo and gather the required information before transforming it into a DTO.

Now we want to sort on this referenced attribute. That's fine. I can create a query containing something like ORDER BY coalesce(i.emailAddress, a.emailAddress) ASC which works well. If the client doesn't pass any sorting in the Pageable object, I just use this sorting, if the client does pass sorting then I call a different version of the query without this default sort.

But now the client wants to sort on this emailAddress property and the trouble starts. Directly passing the Pageable to the Repository doesn't work because there is no property emailAddress. So I thought no worries! Let's just check in my code if there is a sorting on this property and do a special version of my query depending on the parameters.

if (pageable.getSort().isSorted()) {
    Sort.Order emailAddressOrder = pageable.getSort().getOrderFor("emailAddress");
    if (emailAddressOrder != null) {
        if (emailAddressOrder.getDirection() == Sort.Direction.ASC)
            members = memberRepo.findInProjectDefaultSortAsc(projectId, skipFullName, fullName, pageable);
        else
            members = memberRepo.findInProjectDefaultSortDesc(projectId, skipFullName, fullName, pageable);
    }
    else
        members = memberRepo.findInProject(projectId, skipFullName, fullName, pageable);
}
else
    members = memberRepo.findInProjectDefaultSortAsc(projectId, skipFullName, fullName, pageable);

Doesn't look superpretty but should work right? Wrong, because the Pageable STILL contains the invalid property emailAddress.

So how do I remove this property? I tried casting it to a PageRequest but even that implementation object isn't writeable. What I did now is this:

PageRequest newPageRequest = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize());

And then I pass this new object into the query instead. But that seems a bit error-prone, what if the PageRequest entity gets expanded with additional properties later on, then my object still only copies 2 properties. Is this really the best I can do now?

Sebastiaan van den Broek
  • 5,818
  • 7
  • 40
  • 73

2 Answers2

6

Yes, don't worry. There is nothing wrong with your approach. Pageable / PageRequest are immutable and that is actually a good practice. When working with immutable classes, it's standard practice to create copies with (possibly) modified properties.

Also, don't worry about future. If the PageRequest entity gets expanded in future, Spring developers will almost certainly keep it immutable. There will be a new parameter in the constructor / factory method and that will lead to compilation error. Getting such error is the best, that can happen, when you are upgrading version of a library.

Edit: You have one additional option: Do not embed Sort into Pageable. Use them as two separate parameters. First parameter would be Pageable without any sorting information. Second parameter can be Spring's Sort, but you are also free to use your own data types, e.g. some enum.

ygor
  • 1,726
  • 1
  • 11
  • 23
  • I like the idea in the edit. You mean I can pass an additional sort into the Repository right? Or am I reading it wrong? Would that mean that the Sort information that is in the Pageable just gets ignored? Because that object is directly inserted into my request method by Spring MVC, WITH sorting. – Sebastiaan van den Broek Dec 11 '18 at 12:25
  • Yes, `PageAble` will hold instance of `org.springframework.data.domain.Sort#UNSORTED`, which is effectively ignored by Spring. – ygor Dec 11 '18 at 12:28
0

You can do it in two ways :

  1. Using java stream map
  2. Using new PageImpl<>()

Described in details inside post How to update the content inside of Page object from Spring Data without changing the other properties

Tomasz
  • 884
  • 8
  • 12