如何在 spring boot with hibernate 中实现分页

How to implement pagination in spring boot with hibernate

我正在使用 spring 带休眠的启动,我想在我的项目中使用分页。我在 google 上搜索并看到了很多示例,但我无法在我的项目中实现它。

我想如果我在我的 url 中通过 1,那么应该会出现 10 个结果,如果我通过 2,那么接下来的 10 个结果应该会出现,依此类推。

这是我的 Dao

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

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

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

这是我的控制器

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

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

这是我的 url:

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

求推荐。

检查一下。您的控制器

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

你的道

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

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

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

我会考虑在您的控制器中直接使用 org.springframework.data.domain.Pageable。然后可以将该对象传递到您的 JPA 层,在该层中它将处理 returned 结果的数量和大小。

使用 Pageable 的好处在于它 return 是一个 Page 对象,可以在前端使用以形成 previous/next 页面逻辑。

默认情况下,此 class 使用 url 参数“page”和“size”;因此 page=0&size=10 将 return 前 10 个项目。

因此在您的情况下,代码可能类似于:

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

注意注释 @PageableDefault 只是为了设置默认值,它不是必需的。

在前端调用下一页即可<a href="/top/pages?page=1">Next</a>;这将 return 11 到 20 的帖子列表。

在 Spring 中实现分页 启动非常简单,只需遵循基本步骤 -

1 - 在存储库接口中扩展 PagingAndSortingRepository

public interface UserRepository extends PagingAndSortingRepository <User, Long> 

2 - 方法声明应如下例所示

Page<User> userList(Pageable pageable);

3 - 服务中的方法实现 class 应该像下面的例子

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

4 - 控制器 class 代码应如下所示

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

从前端调用应该如下所示

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

有关详细信息,请观看下面的视频

Spring Boot : Pagination Basic

Spring Boot : Pagination Advanced

感谢阅读

如何使用本机查询实现动态分页

在这里,您可以找到存储库和服务层以及您的数据传输对象 (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 按如下方式翻译您的查询:

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

优缺点 优点:与使用 Spring 数据相比,将生成更少的 SQL 查询。这些复杂的查询不能写在 Spring 数据中,我们必须将我们的查询指定为原生查询,仍然可以使用这种方法进行分页。

缺点:"object" 数组必须映射到 Java 对象。很痛苦,很难维持。

如何使用 Spring 数据实现 OffsetLimit 分页 据我所知,在默认 Spring 数据存储库中没有 "out-of-the-box" 支持您需要的内容。但是您可以创建将采用 limit/offset 参数的 Pageable 对象的自定义实现。

创建一个可分页对象并将其传递给 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;
    }
    ....
}

这意味着不需要更改存储库层。您唯一需要更改的是服务层,如下所示:

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

请注意,您不需要手动映射结果,这会占用大量开发时间。

我在 spring 引导中实现了分页。下面是我的存储库。

    @Repository("userRepository")
    public interface UserRepository extends PagingAndSortingRepository<User, Long> {
  }

下面是我的控制器。

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

下面是视图,为此我使用了 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>