I want the user to be able to specify the limit (the size of the amount returned) and offset (the first record returned / index returned) in my query method.
Here are my classes without any paging capabilities. My entity:
@Entity public Employee { @Id @GeneratedValue(strategy=GenerationType.AUTO) private int id; @Column(name="NAME") private String name; //getters and setters }
My repository:
public interface EmployeeRepository extends JpaRepository { @Query("SELECT e FROM Employee e WHERE e.name LIKE :name ORDER BY e.id") public List findByName(@Param("name") String name); }
My service interface:
public interface EmployeeService { public List findByName(String name); }
My service implementation:
public class EmployeeServiceImpl { @Resource EmployeeRepository repository; @Override public List findByName(String name) { return repository.findByName(name); } }
Now here is my attempt at providing paging capabilities that support offset and limit. My entity class remains the same.
My "new" repository takes in a pageable parameter:
public interface EmployeeRepository extends JpaRepository { @Query("SELECT e FROM Employee e WHERE e.name LIKE :name ORDER BY e.id") public List findByName(@Param("name") String name, Pageable pageable); }
My "new" service interface takes in two additional parameters:
public interface EmployeeService { public List findByName(String name, int offset, int limit); }
My "new" service implementation:
public class EmployeeServiceImpl { @Resource EmployeeRepository repository; @Override public List findByName(String name, int offset, int limit) { return repository.findByName(name, new PageRequest(offset, limit); } }
This however isn't what i want. PageRequest specifies the page and size (page # and the size of the page). Now specifying the size is exactly what I want, however, I don't want to specify the starting page #, I want the user to be able to specify the starting record / index. I want something similar to
public List findByName(String name, int offset, int limit) { TypedQuery 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(); }
Specifically the setFirstResult() and setMaxResult() methods. But I can't use this method because I want to use the Employee repository interface. (Or is it actually better to define queries through the entityManager?) Anyways, is there a way to specify the offset without using the entityManager? Thanks in advance!
You probably can't to this with spring data jpa. If the offset is very small, you might just remove the top X statements from the query after retrieval.
Otherwise, you could define the page size to be the offset and start at page+1.
Below code should do it. I am using in my own project and tested for most cases.
usage:
Pageable pageable = new OffsetBasedPageRequest(offset, limit); return this.dataServices.findAllInclusive(pageable);
and the source code:
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 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(); } }
You can do that by creating your own Pageable.
Try out this basic sample. Works fine for me:
public class ChunkRequest implements Pageable { private int limit = 0; private int offset = 0; public ChunkRequest(int skip, int offset) { if (skip
Maybe the answer is kind of late, but I thought about the same thing. Compute the current page based on offset and limit. Well, it is not exactly the same because it "assumes" that the offset is a multiple of the limit, but maybe your application is suitable for this.
@Override public List findByName(String name, int offset, int limit) { // limit != 0 ;) int page = offset / limit; return repository.findByName(name, new PageRequest(page, limit); }
I would suggest a change of the architecture. Change your controller or whatever calls the service to initially give you page and limit if possible.
Here you go:
public interface EmployeeRepository extends JpaRepository { @Query(value="SELECT e FROM Employee e WHERE e.name LIKE ?1 ORDER BY e.id offset ?2 limit ?3", nativeQuery = true") public List findByNameAndMore(String name, int offset, int limit); }