Spring Data JPA中的分页(限制和偏移)

chi*_*boi 34 paging spring pagination jpa spring-data

我希望用户能够在我的查询方法中指定限制(返回的数量的大小)和偏移量(返回的第一个记录/返回的索引).

这是我没有任何分页功能的类.我的实体:

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

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

    //getters and setters
}
Run Code Online (Sandbox Code Playgroud)

我的存储库:

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);
}
Run Code Online (Sandbox Code Playgroud)

我的服务界面:

public interface EmployeeService {

    public List<Employee> findByName(String name);
}
Run Code Online (Sandbox Code Playgroud)

我的服务实施:

public class EmployeeServiceImpl {

    @Resource
    EmployeeRepository repository;

    @Override
    public List<Employee> findByName(String name) {
        return repository.findByName(name);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,我尝试提供支持偏移和限制的分页功能.我的实体类保持不变.

我的"新"存储库包含一个可分页的参数:

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);
}
Run Code Online (Sandbox Code Playgroud)

我的"新"服务接口有两个额外的参数:

public interface EmployeeService {

    public List<Employee> findByName(String name, int offset, int limit);
}
Run Code Online (Sandbox Code Playgroud)

我的"新"服务实现:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,这不是我想要的.PageRequest指定页面和大小(页面#和页面大小).现在指定大小正是我想要的,但是,我不想指定起始页面#,我希望用户能够指定起始记录/索引.我想要类似的东西

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();
}
Run Code Online (Sandbox Code Playgroud)

特别是setFirstResult()和setMaxResult()方法.但是我不能使用这种方法,因为我想使用Employee存储库接口.(或者通过entityManager定义查询实际上更好吗?)无论如何,有没有办法在不使用entityManager的情况下指定偏移量?提前致谢!

小智 59

下面的代码应该这样做.我在我自己的项目中使用并测试了大多数情况.

用法:

   Pageable pageable = new OffsetBasedPageRequest(offset, limit);
   return this.dataServices.findAllInclusive(pageable);
Run Code Online (Sandbox Code Playgroud)

和源代码:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 在框架中直接包含这个或类似的东西是个好主意...... (6认同)
  • 在 Spring 2.3.4 中 - 排序方法“Sort(Direction, List&lt;string&gt;) 出现错误”具有私有访问权限 org.springframework.data.domain.Sort。offset 也已更改为 long 数据类型而不是 int (3认同)
  • 对于“ hasPrevious()”,它不应该是“ return offset&gt; = limit;”吗? (2认同)
  • @shivajibhosale 通过检查文档,我发现 `Sort.by(Direction, String...)` 可以根据方向和属性构造一个 `Sort` 对象,以替换以前的构造函数。 (2认同)

Tob*_*hen 15

您可以通过创建自己的Pageable来实现.

试试这个基本样本.对我来说很好:

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

}
Run Code Online (Sandbox Code Playgroud)

  • 为什么“offset=skip”和“limit=offset”?为什么不直接设置`offset`和`limit`呢? (2认同)

小智 8

使用长偏移量Sort.by()调整良好的@codingmonkey awnser

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.Pageable;
import org.springframework.data.domain.Sort;
import java.io.Serializable;

public class OffsetBasedPageRequest implements Pageable, Serializable {

    private static final long serialVersionUID = -25822477129613575L;

    private final int limit;
    private final long 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(long 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(long offset, int limit, Sort.Direction direction, String... properties) {
        this(offset, limit, Sort.by(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, Sort.unsorted());
    }

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

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

    @Override
    public long 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 2.6.0 版本中的 Spring-boot-parent 还需要一个名为“withPage”的方法。如果有人需要它,我将把它留在这里: ```@Override public Pageable withPage(int pageNumber) { return new OffsetBasedPageRequest((long) pageNumber * getPageSize(), getPageSize(), getSort()); }``` (7认同)

Seb*_*ian 7

也许答案有点迟了,但我想到了同样的事情.根据偏移和限制计算当前页面.嗯,它并不完全相同,因为它"假设"偏移是极限的倍数,但也许你的应用程序适合这个.

@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));
}
Run Code Online (Sandbox Code Playgroud)

我建议改变架构.更改您的控制器或任何调用服务,以便最初为您提供页面和限制(如果可能).

  • 这只能在非常严格的情况下运作.如果偏移低于限制,它将被窃听,因为页面将无法控制地回合.Offset = 9,例如,Limit = 100,仍将返回前9行. (4认同)

sup*_*bra 6

干得好:

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);
}
Run Code Online (Sandbox Code Playgroud)

  • 读者,请注意,这是使用本机SQL查询而不是JPQL的解决方案。 (2认同)