Check for page offsets exceeding Integer.MAX_VALUE.

Instead of relying on the JPA provider to handle page offsets exceeding Integer.MAX_VALUE, do the check inside Spring Data JPA and provide a more meaningful error message.

Closes #2502.
This commit is contained in:
Greg L. Turnquist
2022-05-16 10:48:59 -05:00
parent 6be9b1460e
commit 9fc36ef82a
5 changed files with 61 additions and 11 deletions

View File

@@ -18,6 +18,7 @@ package org.springframework.data.jpa.repository.query;
import jakarta.persistence.Query;
import org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling;
import org.springframework.data.jpa.support.PageableUtils;
import org.springframework.util.Assert;
/**
@@ -99,7 +100,7 @@ public class ParameterBinder {
return query;
}
query.setFirstResult((int) accessor.getPageable().getOffset());
query.setFirstResult(PageableUtils.getOffsetAsInteger(accessor.getPageable()));
query.setMaxResults(accessor.getPageable().getPageSize());
return query;

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.data.jpa.repository.support;
import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -22,9 +25,6 @@ import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;
import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
@@ -32,6 +32,7 @@ import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.query.EscapeCharacter;
import org.springframework.data.jpa.support.PageableUtils;
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.util.Assert;
@@ -66,8 +67,7 @@ class FetchableFluentQueryByExample<S, R> extends FluentQuerySupport<S, R> imple
private FetchableFluentQueryByExample(Example<S> example, Class<S> entityType, Class<R> returnType, Sort sort,
Collection<String> properties, Function<Sort, TypedQuery<S>> finder, Function<Example<S>, Long> countOperation,
Function<Example<S>, Boolean> existsOperation,
EntityManager entityManager, EscapeCharacter escapeCharacter) {
Function<Example<S>, Boolean> existsOperation, EntityManager entityManager, EscapeCharacter escapeCharacter) {
super(returnType, sort, properties, entityType);
this.example = example;
@@ -83,8 +83,8 @@ class FetchableFluentQueryByExample<S, R> extends FluentQuerySupport<S, R> imple
Assert.notNull(sort, "Sort must not be null!");
return new FetchableFluentQueryByExample<>(example, entityType, resultType, this.sort.and(sort), properties,
finder, countOperation, existsOperation, entityManager, escapeCharacter);
return new FetchableFluentQueryByExample<>(example, entityType, resultType, this.sort.and(sort), properties, finder,
countOperation, existsOperation, entityManager, escapeCharacter);
}
@Override
@@ -168,7 +168,7 @@ class FetchableFluentQueryByExample<S, R> extends FluentQuerySupport<S, R> imple
TypedQuery<S> pagedQuery = createSortedAndProjectedQuery();
if (pageable.isPaged()) {
pagedQuery.setFirstResult((int) pageable.getOffset());
pagedQuery.setFirstResult(PageableUtils.getOffsetAsInteger(pageable));
pagedQuery.setMaxResults(pageable.getPageSize());
}

View File

@@ -31,6 +31,7 @@ import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.support.PageableUtils;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.util.Assert;
@@ -170,7 +171,7 @@ class FetchableFluentQueryBySpecification<S, R> extends FluentQuerySupport<S, R>
TypedQuery<S> pagedQuery = createSortedAndProjectedQuery();
if (pageable.isPaged()) {
pagedQuery.setFirstResult((int) pageable.getOffset());
pagedQuery.setFirstResult(PageableUtils.getOffsetAsInteger(pageable));
pagedQuery.setMaxResults(pageable.getPageSize());
}

View File

@@ -55,6 +55,7 @@ import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.query.EscapeCharacter;
import org.springframework.data.jpa.repository.query.QueryUtils;
import org.springframework.data.jpa.repository.support.QueryHints.NoHints;
import org.springframework.data.jpa.support.PageableUtils;
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.data.util.ProxyUtils;
@@ -650,7 +651,7 @@ public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T
@Nullable Specification<S> spec) {
if (pageable.isPaged()) {
query.setFirstResult((int) pageable.getOffset());
query.setFirstResult(PageableUtils.getOffsetAsInteger(pageable));
query.setMaxResults(pageable.getPageSize());
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2008-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.support;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Pageable;
/**
* Provide a set of utility methods to support interfacing {@link Pageable}s.
*
* @author Greg Turnquist
* @since 3.0
*/
public final class PageableUtils {
private PageableUtils() {
throw new IllegalStateException("Cannot instantiate a utility class!");
}
/**
* Convert a {@link Pageable}'s offset value from {@link Long} to {@link Integer} to support JPA spec methods.
*
* @param pageable
* @return integer
*/
public static int getOffsetAsInteger(Pageable pageable) {
if (pageable.getOffset() > Integer.MAX_VALUE) {
throw new InvalidDataAccessApiUsageException("Page offset exceeds Integer.MAX_VALUE (" + Integer.MAX_VALUE + ")");
}
return Math.toIntExact(pageable.getOffset());
}
}