it-swarm.com.de

Paginierung in Spring Data JPA (Limit und Offset)

Ich möchte, dass der Benutzer in der Abfragemethode das Limit (die Größe des zurückgegebenen Betrags) und den Offset (den ersten zurückgegebenen Datensatz/den zurückgegebenen Index) angeben kann. 

Hier sind meine Klassen ohne Paging-Funktionen ... Meine Entität:

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

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

    //getters and setters
}

Mein Repository:

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);
}

Meine Serviceschnittstelle:

public interface EmployeeService {

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

Meine Service-Implementierung:

public class EmployeeServiceImpl {

    @Resource
    EmployeeRepository repository;

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

Hier ist mein Versuch, Paging-Funktionen bereitzustellen, die Offset und Limit unterstützen . Meine Entitätsklasse bleibt gleich.

Mein "neues" Repository enthält einen auslagerbaren Parameter:

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);
}

Meine "neue" Service-Schnittstelle enthält zwei zusätzliche Parameter:

public interface EmployeeService {

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

Meine "neue" Service-Implementierung:

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);
    }
}

Dies ist jedoch nicht das, was ich will. PageRequest gibt die Seite und die Größe an (Seitennummer und die Größe der Seite). Nun ist die Angabe der Größe genau das, was ich möchte, ich möchte jedoch nicht die Start # angeben, sondern möchte, dass der Benutzer den Startdatensatz/-index angeben kann. Ich möchte etwas Ähnliches

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();
}

Speziell die Methoden setFirstResult () und setMaxResult (). Ich kann diese Methode jedoch nicht verwenden, weil ich die Employee Repository-Schnittstelle verwenden möchte. (Oder ist es eigentlich besser, Abfragen über den entityManager zu definieren?) Gibt es eine Möglichkeit, den Offset ohne Verwendung des entityManagers anzugeben? Danke im Voraus! 

21
chinesewhiteboi

Der folgende Code sollte es tun. Ich benutze es in meinem eigenen Projekt und habe es in den meisten Fällen getestet. 

verwendungszweck: 

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

und der Quellcode: 

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();
    }
}
40
codingmonkey

Sie können dies tun, indem Sie Ihr eigenes Pageable erstellen. 

Probieren Sie dieses grundlegende Beispiel aus. Funktioniert gut für mich:

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;
}

}
14

Vielleicht ist die Antwort etwas spät, aber ich dachte darüber nach. Berechnen Sie die aktuelle Seite basierend auf Offset und Limit. Nun, es ist nicht genau dasselbe, weil "angenommen" wird, dass der Versatz ein Vielfaches des Grenzwerts ist, aber vielleicht ist Ihre Anwendung dafür geeignet.

@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);
}

Ich würde eine Änderung der Architektur vorschlagen. Ändern Sie Ihren Controller oder wie auch immer Sie den Dienst aufrufen, um Ihnen anfänglich eine Seite und ein Limit zu geben, wenn möglich.

5
Sebastian

Sie können dies mit Spring Data JPA wahrscheinlich nicht tun. Wenn der Versatz sehr klein ist, entfernen Sie nach dem Abrufen möglicherweise nur die obersten X-Anweisungen aus der Abfrage.

Andernfalls können Sie die Seitengröße als Versatz definieren und mit Seite + 1 beginnen.

4
membersound

Bitte schön:

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);
}
4
supercobra

Angenommen, Sie filtern und sortieren gleichzeitig und paging @Query hilft Ihnen dabei

    @Query(value = "SELECT * FROM table  WHERE firstname= ?1  or lastname= ?2 or age= ?3 or city= ?4 or "
        + " ORDER BY date DESC OFFSET ?8 ROWS FETCH NEXT ?9 ROWS ONLY" , nativeQuery = true)
List<JobVacancy> filterJobVacancyByParams(final String firstname, final String lastname,
        final String age, final float city,int offset, int limit);
0

Versuch das:

public interface ContactRepository extends JpaRepository<Contact, Long> 
{
    @Query(value = "Select c.* from contacts c where c.username is not null order by c.id asc limit ?1,  ?2 ", nativeQuery = true)         
    List<Contact> findContacts(int offset, int limit);        
}
0
Abhi