简体   繁体   English

如何使用 Hibernate 在 Spring Boot 中实现分页

[英]How to implement pagination in spring boot with hibernate

I am using spring boot with hibernate and I want to use pagination in my project.我正在将 Spring Boot 与 Hibernate 一起使用,并且我想在我的项目中使用分页。 I have searched on google and saw many examples but I am unable to implement it in my project.我在谷歌上搜索并看到了很多例子,但我无法在我的项目中实现它。

I want like if I pass 1 in my url then 10 results should come and if I pass 2 then next 10 results should come and so on.我想如果我在我的 url 中传递 1,那么应该有 10 个结果,如果我传递 2,那么接下来的 10 个结果应该出现,依此类推。

Here is my my Dao这是我的我的

@Transactional
public interface PostDao extends CrudRepository<Post, Long>{

@Query(getAllPostsByRank)
List<Post> getAllPostsByRank();

final String getAllPostsByRank= "from Post order by value DESC";
}

Here is my Controller这是我的控制器

@RequestMapping("/top")
    @ResponseBody 
     public List<Post> getAllPosts(HttpServletRequest req, HttpServletResponse res) throws ServletException {

List<Post> postobj = postDao.getAllPostsByRank();
return postobj;
}

And here is my url:这是我的网址:

http://localhost:8888/v1.0/post/top/1

Please suggest.请建议。

I would consider using org.springframework.data.domain.Pageable directly into your controller.我会考虑将org.springframework.data.domain.Pageable直接用于您的控制器。 This object can then be passed to your JPA layer where it will handle the number of returned results and the size.然后可以将此对象传递到您的 JPA 层,在那里它将处理返回结果的数量和大小。

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.使用Pageable在于它返回一个Page对象,可以在前端使用它来形成上一页/下一页逻辑。

By default this class uses url parameters ' page ' and ' size ';默认情况下,此类使用 url 参数“页面”和“大小”; hence page=0&size=10 will return the first 10 items.因此 page=0&size=10 将返回前 10 个项目。

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.注意@PageableDefault注释只是设置默认值,它不是必需的。

In the front-end the next page call can be <a href="/top/pages?page=1">Next</a> ;在前端,下一页调用可以是<a href="/top/pages?page=1">Next</a> this will return a list of Posts from 11 to 20.这将返回从 11 到 20 的帖子列表。

Implement pagination in Spring Boot is quite easy only you need to follow basic steps -在 Spring Boot 中实现分页非常简单,只需遵循基本步骤即可 -

1 - Extends PagingAndSortingRepository in repository interface 1 - 在存储库接口中扩展 PagingAndSortingRepository

public interface UserRepository extends PagingAndSortingRepository <User, Long> 

2 - Method declaration should be like below example 2 - 方法声明应该像下面的例子

Page<User> userList(Pageable pageable);

3 - Method implementation in Service class should be like below example 3 - Service 类中的方法实现应该像下面的例子

@Override
public Page<User> userList(Pageable pageable) {
        return userRepository.findAll(pageable);
}

4 - Controller class code should be like below 4 - 控制器类代码应如下所示

@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:分页基础

Spring Boot : Pagination Advanced Spring Boot:分页高级

Thanks for reading谢谢阅读

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

I have implemented pagination in spring boot.我已经在 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>

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.在这里,您可以找到存储库和服务层以及您的数据传输对象 (DTO),它们将用于映射我们的结果并将其发送到控制器层。

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: Hibernate 将您的查询翻译如下:

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.优点和缺点优点:与使用 Spring Data 相比,将生成更少的 SQL 查询。 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.这些复杂的查询无法用 Spring Data 编写,我们必须将我们的查询指定为原生查询,仍然可以使用这种方法进行分页。

Cons : The "object" array must map to a Java object.缺点:“对象”数组必须映射到 Java 对象。 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.如何使用 Spring Data 实现 OffsetLimit 分页 据我所知,默认 Spring Data 存储库中没有“开箱即用”的支持。 But you can create a custom implementation of Pageable objects that will take limit/offset parameters.但是您可以创建一个自定义实现的 Pageable 对象,这些对象将采用限制/偏移参数。

Make a pageable object and pass it to PaginationAndSortingRepository:创建一个可分页对象并将其传递给 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.请注意,您不需要手动映射结果,这会占用大量开发时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM