Pagination dans les données de printemps JPA (limite et offset)

je veux que l'utilisateur puisse spécifier la limite de la taille de la valeur retournée) et l'offset (le premier enregistrement retourné / index retourné dans ma méthode de requête.

Voici mes classes, sans pagination capacités. Mon entité:

@Entity
public Employee {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int id;

    @Column(name="NAME")
    private String name;

    //getters and setters
}

mon dépôt:

public interface EmployeeRepository extends JpaRepository<Employee, Integer> {

    @Query("SELECT e FROM Employee e WHERE e.name LIKE :name ORDER BY e.id")
    public List<Employee> findByName(@Param("name") String name);
}

mon interface de service:

public interface EmployeeService {

    public List<Employee> findByName(String name);
}

mise en oeuvre de mon service:

public class EmployeeServiceImpl {

    @Resource
    EmployeeRepository repository;

    @Override
    public List<Employee> findByName(String name) {
        return repository.findByName(name);
    }
}

maintenant voici ma tentative de fournir des capacités de pagination qui supportent l'offset et la limite. Ma classe d'entités reste la même.

Mon "nouveau" référentiel prend en paginable paramètre:

public interface EmployeeRepository extends JpaRepository<Employee, Integer> {

    @Query("SELECT e FROM Employee e WHERE e.name LIKE :name ORDER BY e.id")
    public List<Employee> findByName(@Param("name") String name, Pageable pageable);
}

Ma "nouvelle" interface de service prend deux paramètres supplémentaires:

public interface EmployeeService {

    public List<Employee> findByName(String name, int offset, int limit);
}

Mon "nouveau" service de la mise en œuvre:

public class EmployeeServiceImpl {

    @Resource
    EmployeeRepository repository;

    @Override
    public List<Employee> findByName(String name, int offset, int limit) {
        return repository.findByName(name, new PageRequest(offset, limit);
    }
}

mais ce n'est pas ce que je veux. PageRequest spécifie la page et la taille (numéro de page et la taille de la page). Maintenant, spécifier la taille est exactement ce que je veux, cependant, je ne veux pas spécifier la page de départ No, je veux que l'utilisateur puisse spécifier l'enregistrement de début / d'index. Je veux quelque chose de similaire à

public List<Employee> findByName(String name, int offset, int limit) {
    TypedQuery<Employee> query = entityManager.createQuery("SELECT e FROM Employee e WHERE e.name LIKE :name ORDER BY e.id", Employee.class);
    query.setFirstResult(offset);
    query.setMaxResults(limit);
    return query.getResultList();
}

spécifiquement les méthodes setFirstResult() et setMaxResult (). Mais je ne peux pas utiliser cette méthode parce que je veux utiliser l'interface du dépôt des employés. (Ou est-il réellement meilleur pour définir des requêtes par le biais de l'entityManager?) De toute façon, y a-t-il un moyen de spécifier l'offset sans utiliser l'entityManager? Merci à l'avance!

19
demandé sur Etienne Neveu 2014-07-29 08:56:39

5 réponses

au-dessous du code devrait le faire. J'utilise dans mon propre projet et testé pour la plupart des cas.

utilisation:

   Pageable pageable = new OffsetBasedPageRequest(offset, limit);
   return this.dataServices.findAllInclusive(pageable);

et le code source:

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.data.domain.AbstractPageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import java.io.Serializable;

/**
* Created by Ergin
**/
public class OffsetBasedPageRequest implements Pageable, Serializable {

    private static final long serialVersionUID = -25822477129613575L;

    private int limit;
    private int offset;
    private final Sort sort;

    /**
     * Creates a new {@link OffsetBasedPageRequest} with sort parameters applied.
     *
     * @param offset zero-based offset.
     * @param limit  the size of the elements to be returned.
     * @param sort   can be {@literal null}.
     */
    public OffsetBasedPageRequest(int offset, int limit, Sort sort) {
        if (offset < 0) {
            throw new IllegalArgumentException("Offset index must not be less than zero!");
        }

        if (limit < 1) {
            throw new IllegalArgumentException("Limit must not be less than one!");
        }
        this.limit = limit;
        this.offset = offset;
        this.sort = sort;
    }

    /**
     * Creates a new {@link OffsetBasedPageRequest} with sort parameters applied.
     *
     * @param offset     zero-based offset.
     * @param limit      the size of the elements to be returned.
     * @param direction  the direction of the {@link Sort} to be specified, can be {@literal null}.
     * @param properties the properties to sort by, must not be {@literal null} or empty.
     */
    public OffsetBasedPageRequest(int offset, int limit, Sort.Direction direction, String... properties) {
        this(offset, limit, new Sort(direction, properties));
    }

    /**
     * Creates a new {@link OffsetBasedPageRequest} with sort parameters applied.
     *
     * @param offset zero-based offset.
     * @param limit  the size of the elements to be returned.
     */
    public OffsetBasedPageRequest(int offset, int limit) {
        this(offset, limit, new Sort(Sort.Direction.ASC,"id"));
    }

    @Override
    public int getPageNumber() {
        return offset / limit;
    }

    @Override
    public int getPageSize() {
        return limit;
    }

    @Override
    public int getOffset() {
        return offset;
    }

    @Override
    public Sort getSort() {
        return sort;
    }

    @Override
    public Pageable next() {
        return new OffsetBasedPageRequest(getOffset() + getPageSize(), getPageSize(), getSort());
    }

    public OffsetBasedPageRequest previous() {
        return hasPrevious() ? new OffsetBasedPageRequest(getOffset() - getPageSize(), getPageSize(), getSort()) : this;
    }


    @Override
    public Pageable previousOrFirst() {
        return hasPrevious() ? previous() : first();
    }

    @Override
    public Pageable first() {
        return new OffsetBasedPageRequest(0, getPageSize(), getSort());
    }

    @Override
    public boolean hasPrevious() {
        return offset > limit;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;

        if (!(o instanceof OffsetBasedPageRequest)) return false;

        OffsetBasedPageRequest that = (OffsetBasedPageRequest) o;

        return new EqualsBuilder()
                .append(limit, that.limit)
                .append(offset, that.offset)
                .append(sort, that.sort)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(17, 37)
                .append(limit)
                .append(offset)
                .append(sort)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("limit", limit)
                .append("offset", offset)
                .append("sort", sort)
                .toString();
    }
}
35
répondu codingmonkey 2016-08-04 09:35:39

Vous pouvez le faire en créant votre propre Paginable.

essayez cet échantillon de base. Fonctionne très bien pour moi:

public class ChunkRequest implements Pageable {

private int limit = 0;
private int offset = 0;

public ChunkRequest(int skip, int offset) {
    if (skip < 0)
        throw new IllegalArgumentException("Skip must not be less than zero!");

    if (offset < 0)
        throw new IllegalArgumentException("Offset must not be less than zero!");

    this.limit = offset;
    this.offset = skip;
}

@Override
public int getPageNumber() {
    return 0;
}

@Override
public int getPageSize() {
    return limit;
}

@Override
public int getOffset() {
    return offset;
}

@Override
public Sort getSort() {
    return null;
}

@Override
public Pageable next() {
    return null;
}

@Override
public Pageable previousOrFirst() {
    return this;
}

@Override
public Pageable first() {
    return this;
}

@Override
public boolean hasPrevious() {
    return false;
}

}
13
répondu Tobias Michelchen 2015-09-24 14:24:05

vous ne pouvez probablement pas faire cela avec spring data jpa. Si l'offset est très petit, vous pouvez simplement supprimer les instructions X du haut de la requête après la récupération.

sinon, vous pouvez définir la taille de la page comme étant l'offset et commencer à la page+1.

4
répondu membersound 2014-07-29 13:09:27

peut-être que la réponse est un peu tardive, mais j'ai pensé à la même chose. Calculez la page actuelle en fonction de l'offset et de la limite. Eh bien, ce n'est pas exactement la même parce qu'il "suppose" que l'offset est un multiple de la limite, mais peut-être que votre application est adapté pour cela.

@Override
public List<Employee> findByName(String name, int offset, int limit) {
    // limit != 0 ;)
    int page = offset / limit;
    return repository.findByName(name, new PageRequest(page, limit);
}

je suggère un changement d'architecture. Changez votre controller ou ce qui appelle le service pour vous donner initialement la page et la limite si possible.

4
répondu Sebastian 2015-04-14 12:40:31

Ici, vous allez:

public interface EmployeeRepository extends JpaRepository<Employee, Integer> {

    @Query(value="SELECT e FROM Employee e WHERE e.name LIKE ?1 ORDER BY e.id offset ?2 limit ?3", nativeQuery = true")
    public List<Employee> findByNameAndMore(String name, int offset, int limit);
}
2
répondu supercobra 2018-02-06 20:25:22