I am using spring boot with hibernate and I want to use pagination in my project. I have searched on google and saw many examples but I am unable to implement it in my proje
I would consider using org.springframework.data.domain.Pageable
directly into your controller. This object can then be passed to your JPA layer where it will handle the number of returned results and the size.
The great thing about using Pageable
is that it returns a Page
object which can be used on the front-end to form previous/next page logic.
By default this class uses url parameters 'page' and 'size'; hence page=0&size=10 will return the first 10 items.
Hence in your case the code could look something like:
@ResponseBody
@RequestMapping("/top/pages/")
public List<Post> getAllPosts(@PageableDefault(value=10, page=0) Pageable pageable) throws ServletException {
Page page = postDao.findAll(pageable);
return page.getContent();
}
Notice the annotation @PageableDefault
is just to set up the defaults & it's not required.
In the front-end the next page call can be <a href="/top/pages?page=1">Next</a>
; this will return a list of Posts from 11 to 20.
I have implemented pagination in spring boot. Below is my Repository.
@Repository("userRepository")
public interface UserRepository extends PagingAndSortingRepository<User, Long> {
}
Below is my Controller.
@Controller
public class SampleController {
@Autowired
private UserRepository repository;
@GetMapping("/userview")
public String getEmployees(@PageableDefault(size = 1) Pageable pageable,
Model model) {
Page<User> page = repository.findAll(pageable);
model.addAttribute("page", page);
return "userdetail";
}
}
Below is the view,for that I have used thymeleaf.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<h2>USER DETAILS</h2>
<table class="table table-striped table-responsive-md">
<thead>
<tr>
<th> ID </th>
<th>Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Role</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${page.content}">
<td th:text="${user.id}"></td>
<td th:text="${user.email}"></td>
<td th:text="${user.name}"></td>
<td th:text="${user.lastName}"></td>
<td th:text="${user.roles[0].role}"></td>
</tr>
</tbody>
</table>
<div class="pagination-div">
<span th:if="${page.hasPrevious()}">
<a th:href="@{/userview(page=${page.number-1},size=${page.size})}">Previous</a>
</span>
<th:block th:each="i: ${#numbers.sequence(0, page.totalPages - 1)}">
<span th:if="${page.number == i}" class="selected">[[${i}+1]]</span>
<span th:unless="${page.number == i}">
<a th:href="@{/userview(page=${i},size=${page.size})}">[[${i}+1]]</a>
</span>
</th:block>
<span th:if="${page.hasNext()}">
<a th:href="@{/userview(page=${page.number+1},size=${page.size})}">Next</a>
</span>
</div>
</body>
</html>
Implement pagination in Spring Boot is quite easy only you need to follow basic steps -
1 - Extends PagingAndSortingRepository in repository interface
public interface UserRepository extends PagingAndSortingRepository <User, Long>
2 - Method declaration should be like below example
Page<User> userList(Pageable pageable);
3 - Method implementation in Service class should be like below example
@Override
public Page<User> userList(Pageable pageable) {
return userRepository.findAll(pageable);
}
4 - Controller class code should be like below
@GetMapping("/list")
public String userList(Model model, Pageable pageable) {
Page<User> pages = userService.userList(pageable);
model.addAttribute("number", pages.getNumber());
model.addAttribute("totalPages", pages.getTotalPages());
model.addAttribute("totalElements",
pages.getTotalElements());
model.addAttribute("size", pages.getSize());
model.addAttribute("users", pages.getContent());
return "/user/list";
}
From front-end call should be like below
http://localhost:8080/application/user/list?page=0&size=5
http://localhost:8080/application/user/list?page=1&size=5
http://localhost:8080/application/user/list?page=2&size=5
For more details watch below video
Spring Boot : Pagination Basic
Spring Boot : Pagination Advanced
Thanks for reading
How to Implement Dynamic Pagination Using a Native Query
Here, you can find the repository and service layers and your data transfer object (DTO), which will be used for mapping our result and sending it to the controller layer.
public interface CustomSomethingRepository {
List<Something> findPagedResultBySomethingElseId(long somethingElseId, int offset, int limit);
}
public class SomethingRepositoryImpl implements CustomSomethingRepository {
@Autowired
private EntityManager em;
@SuppressWarnings("unchecked")
@Override
public List<Something> findPagedResultBySomethingElseId(long somethingElseId, int offset, int limit) {
String query = "select s.* from Something s "
+ "join somethingelse selse on selse.id = s.fk_somethingelse "
+ "where selse.id = :somethingElseId "
+ "order by selse.date";
Query nativeQuery = em.createNativeQuery(query);
nativeQuery.setParameter("somethingElseId", somethingElseId);
//Paginering
nativeQuery.setFirstResult(offset);
nativeQuery.setMaxResults(limit);
final List<Object[]> resultList = nativeQuery.getResultList();
List<Something> somethingList = Lists.newArrayList();
resultList.forEach(object -> somethingList.add(//map obj to something));
return somethingList;
}
}
Hibernate translates your query as follows:
SELECT inner_query.*, ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __hibernate_row_nr__ FROM ( select TOP(?) t as page0_ from Something s join s.somethingelse as selse order by selse.date ) inner_query ) SELECT page0_ FROM query WHERE __hibernate_row_nr__ >= ? AND __hibernate_row_nr__ < ?
@Service
public class SomethingService {
private SomethingRepository somethingRepository;
@Autowired
public SomethingService(SomethingRepository somethingRepository){
this.somethingRepository = somethingRepository;
}
@Transactional(readOnly=true)
public PageDto getSomething(long somethingElseId, int page, int size){
List<Something> somethings = somethingRepository.findBySomethingElseId(somethingElseId, offset, limit);
return new PagedResult<>(somethings
.stream()
.map(SomethingDto::createDto)
.sorted(comparing(SomethingDto::getDatum))
.collect(toList()), somethings.getTotalElements(), somethings.getTotalPages();
}
}
@Controller
//....
public class PagedResult<T> {
public static final long DEFAULT_OFFSET = 0;
public static final int DEFAULT_MAX_NO_OF_ROWS = 100;
private int offset;
private int limit;
private long totalElements;
private List<T> elements;
public PagedResult(List<T> elements, long totalElements, int offset, int limit) {
this.elements = elements;
this.totalElements = totalElements;
this.offset = offset;
this.limit = limit;
}
public boolean hasMore() {
return totalElements > offset + limit;
}
public boolean hasPrevious() {
return offset > 0 && totalElements > 0;
}
public long getTotalElements() {
return totalElements;
}
public int getOffset() {
return offset;
}
public int getLimit() {
return limit;
}
public List<T> getElements() {
return elements;
}
}
Pros and Cons Pros: Fewer SQL queries will be generated, compared to using Spring Data. These complex queries cannot be written in Spring Data, and we have to specify our query as a native one, which can still be paged by using this methodology.
Cons: The "object" array must map to a Java object. It is painful and hard to maintain.
How to Implement OffsetLimit Pagination With Spring Data As far as I know, there is no "out-of-the-box" support for what you need in default Spring Data repositories. But you can create a custom implementation of Pageable objects that will take limit/offset parameters.
Make a pageable object and pass it to PaginationAndSortingRepository:
public class OffsetLimitRequest implements Pageable {
private int limit;
private int offset;
public OffsetLimitRequest(int offset, int limit){
this.limit = limit;
this.offset = offset;
}
@Override
public int getPageNumber() {
return 0;
}
@Override
public int getPageSize() {
return limit;
}
@Override
public int getOffset() {
return offset;
}
....
}
It means there is no need to change the repository layer. The only change you would need is to make is to the service layer, as follows:
@Service
public class SomethingService {
private SomethingRepository somethingRepository;
@Autowired
public SomethingService(SomethingRepository somethingRepository){
this.somethingRepository = somethingRepository;
}
@Transactional(readOnly=true)
public PageDto getSomething(long somethingElseId, int page, int size){
Page<Something> somethings = somethingRepository.findBySomethingElseId(somethingElseId, new OffsetLimitRequest(offset, limit));
return new PageDto(somethings.getContent()
.stream()
.map(SomethingDto::createDto)
.sorted(comparing(SomethingDto::getDatum))
.collect(toList()), somethings.getTotalElements(), somethings.getTotalPages();
}
}
Note that you don't need to map the result manually, and it will take off a good amount of time from development.
Check it. Your controller
@RequestMapping("/top/pages/{pageno}")
@ResponseBody
public List<Post> getAllPosts(@PathVariable("pageno") int pageno, HttpServletRequest req, HttpServletResponse res) throws ServletException {
List<Post> postobj = postDao.getAllPostsByRank(new PageRequest(pageno,10));
return postobj;
}
Your dao
@Transactional
public interface PostDao extends CrudRepository<Post, Long>{
@Query(getAllPostsByRank)
List<Post> getAllPostsByRank(Pageable pageable);
final String getAllPostsByRank= "from Post order by value DESC";
}