18

i'm looking at migrating our traditional jpa/dao solution to Spring Data.

However, one of our front-ends is SmartGWT, and their databound components load data progressively using limit/offset only, making it hard to use Pageable.

This causes problems, since it's not certain that the limit/offset can be translated evently into a page number. (it might differ depending on how the user scrolls, screen is resized etc.).

I looked at Slice etc, but wasn't able to find a way to use the limit/offset values anywhere.

Was wondering if someone has any pointers? Optimally i would like to continue using limit/offset, but use them in my Repository interfaces without having to code an implementation and set them manually like i do now (query.setMaxResults etc.)

Edit: To clarify why i have issues - The limit/offset might differ between the initial and subsequent data fetches in a smartgwt component. For a listgrid, the first fetch might have limit set to 89 for example, since that's the amount of rows visible on screen, and offset 0. The next request, though, might have offset 89, and limit 50 since that's the component's "datapagesize" value to 50, so that's what it'll fetch when i scroll down. If i scroll to far down before releasing, it might, depending on the settings, fetch for example rows 159-209 instead. Basically, there's no guarantee that the offset is a multiple of anything. It's hard to convert offset 17, limit 5 to a page.

Mathias
  • 3,879
  • 5
  • 36
  • 48
  • 1
    doesn't limit = size, and offset is just page * size? which is in default Pageable implementation? – ikumen May 13 '15 at 14:34
  • No, because it might vary. Say i have a Listgrid. The SmartGWT logic will initially set a offset to 0, and a large enough limit to load what can be seen on screen. This can be 50, 79, 99 etc. depending on screensize, browser size etc. The next time, it will use the "pagesize" value, which for example might be 50. Then, depending on how you scroll, it might next time fetch rows 250-300 for example. My problem is that i cannot control the initial LIMIT value, basically, nor what smartgwt decides that the offset is on subsequent requests. – Mathias May 13 '15 at 16:04

2 Answers2

20

Pagebale implementations do use limit and offset to create pagination. The page value in the constructor is used to generate the offset value in the AbstractPageRequest class getOffset method:

public int getOffset() {
    return this.page * this.size;
}

If you want to only use limit and offset and discard the page parameter from the mix, take a look at the Spring Data documentation on web support, particularly the part about overriding the default configuration. You could create your own implementation of Pageable that takes limit and offset as constructor arguments and the implement your own HandlerMethodArgumentResolver to replace the standard PageRequest resolving. Quick-and-dirty example:

Pageable implementation

public class BetterPageRequest implements Pageable {

    public BetterPageRequest(int limit, int offset){
        this.limit = limit;
        this.offset = offset;
    }

    // Other method implementations

}

HandlerMethodArgumentResolver implementation

public class BetterPageableResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter){
        return Pageable.class.equals(parameter.getParameterType());
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container, NativeWebRequest request, WebDataBinderFactory factory){
        Map<String,String[]> params = request.getParameterMap();
        return new BetterPageRequest(params.get('limit')[0], params.get('offset')[0]);
    }

}
woemler
  • 7,089
  • 7
  • 48
  • 67
  • Ah, implement my own Pageable is probably something i could try, thanks! Since LIMIT/OFFSET is a staple-database-concept, i find it a bit strange that it isn't offered off-the-shelf. – Mathias May 13 '15 at 16:07
  • But your code actually highlight why i have problems with pageable and created this question! What if i get a request with offset 17 and limit 5 for example? – Mathias May 13 '15 at 16:16
  • I kind of see what you're doing, although i have never tried something like that myself, so a little bit worried i'll spend forever getting it to work :). I think i could also just extend the AbstractPageRequest and have it return limit in the "getpagesize" and offset in getOffset. Then just just my custom pagerequest everywhere i need this. – Mathias May 13 '15 at 19:31
  • It should not be too difficult to configure and implement. The benefit of doing it this way will be that there will be no need to alter the repository functionality for methods with `Pageable` as an argument, and there will be no extra code in your controllers needed to handle your version of `Pageable`. This will also be useful if you are using Spring HATEOAS. – woemler May 13 '15 at 19:41
  • Yeah... turns out it won't work. smartGWT sets the limit and offset, which they call "startrow" and "endrow" as parameters in the http body. Until i migrate to their "restservice" i'll have to dig out them from the request myself anyway. I'll keep this for the future though, thanks! – Mathias May 14 '15 at 11:18
  • You don't have to use the query parameters `limit` and `offset`, you can have the `HandlerMethodArgumentResolver` map any parameters to create your `PageRequest` replacement. There is no reason you can't take the request to `http://myapp/search/stuff?startrow=100&endrow=200` and translate that to `new BetterPageRequest(params.get('endrow')[0]-params.get('startrow')[0], params.get('startrow')[0]);` – woemler May 14 '15 at 13:32
  • Yeah i saw that actually, and it would have been neat. However, smartGWT sends those parameter as part of an xml document in the http body, not as url parameters. Its very proprietary, lets just say that... – Mathias May 15 '15 at 08:30
  • How do you implement `getPageNumber()` when it's technically no longer a request for a page? Can I just return zero here and it won't break? – Zyl Nov 26 '21 at 14:57
  • Same problem with `hasPrevious()`. Shouldn't that return `getOffset() > 0` instead of `page > 0` then? – Zyl Nov 26 '21 at 16:40
13
public class OffsetLimitPageable extends PageRequest {
    private int offset;
    public OffsetLimitPageable(int offset, int limit){
        super(offset,limit);
        this.offset=offset;
    }
    @Override
    public long getOffset(){
        return this.offset;
    }
}

And example

Page<WashComment> washComments = washCommentRepository.findWashCommentByWashId_CarWashIdOrderByDateDesc(carWash, new OffsetLimitPageable(offsetNumberRepresentation,
                 limitNumberRepresentation > Config.getMaxLimitAmount() ? Config.getMaxLimitAmount() : limitNumberRepresentation));

Let me know if this is what you want

fukit0
  • 924
  • 1
  • 11
  • 35
Almas Abdrazak
  • 3,209
  • 5
  • 36
  • 80
  • Nice workaround. But it worth to mention that other `PageRequest` methods like `previous()` and `next()` won't work correctly. So, instance of `OffsetLimitPageable` is not a complete implementation of `PageRequest`. – chill appreciator Jul 20 '20 at 20:38
  • I was answered here https://stackoverflow.com/a/63911746/2740179 – LuisComS Sep 16 '20 at 01:04