如何使用hibernate在spring boot中实现分页
Posted
技术标签:
【中文标题】如何使用hibernate在spring boot中实现分页【英文标题】:How to implement pagination in spring boot with hibernate 【发布时间】:2015-12-02 17:41:44 【问题描述】:我正在使用带有休眠功能的 Spring Boot,并且我想在我的项目中使用分页。我在谷歌上搜索并看到了很多例子,但我无法在我的项目中实现它。
我希望如果我在我的 url 中传递 1,那么应该有 10 个结果,如果我传递 2,那么接下来应该有 10 个结果,依此类推。
这是我的道
@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;
这是我的网址:
http://localhost:8888/v1.0/post/top/1
请提出建议。
【问题讨论】:
你试过 PagingAndSortingRepository 吗? (docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/…) 没有没有尝试.. 因为我是新来的 Spring Boot,所以无法理解 你能编辑我的代码吗? Custom Query in Spring JPA Repository with Pagination的可能重复 【参考方案1】:我会考虑在您的控制器中直接使用org.springframework.data.domain.Pageable
。然后可以将此对象传递给您的 JPA 层,在那里它将处理返回结果的数量和大小。
使用Pageable
的好处在于它返回一个Page
对象,可以在前端使用该对象来形成上一页/下一页逻辑。
默认这个类使用url参数'page'和'size';因此 page=0&size=10 将返回前 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>
;这将返回从 11 到 20 的帖子列表。
【讨论】:
这应该是默认答案!在 Spring Boot 中执行此操作的正确方法。 如果我使用 DTO 作为返回,我应该如何将它映射到实体,以便在存储库中查询,使用这个“Pageable”接口? 我不确定你到底是什么意思;但是org.springframework.data.domain.Page
包装了 DTO 的内容,因此您可以使用 page.getContent()
。在我的示例中,您可以将 Post
视为 DTO,并通过 Page<Post> page = new PageImpl<>(typedQuery.getResultList(), pageable, totalRows);
完成创建
不起作用:org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.domain.Pageable]: Specified class is an interface
@mpr 你确定这是因为在你的方法中添加了Pageable
参数。这很奇怪,因为它不应该引起BeanInstantiationException
。【参考方案2】:
在 Spring Boot 中实现分页非常简单,只需遵循基本步骤 -
1 - 在存储库接口中扩展 PagingAndSortingRepository
public interface UserRepository extends PagingAndSortingRepository <User, Long>
2 - 方法声明应该像下面的例子
Page<User> userList(Pageable pageable);
3 - Service 类中的方法实现应如下例所示
@Override
public Page<User> userList(Pageable pageable)
return userRepository.findAll(pageable);
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";
来自前端的调用应该如下所示
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
感谢阅读
【讨论】:
谢谢,这是真正实用的解决方案。它对我有用【参考方案3】:检查一下。你的控制器
@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";
【讨论】:
有趣的是,我刚刚使用from Foo order by id DESC
尝试了这个,它在启动时抛出了异常。 from Foo f order by id DESC
没有。
页面请求现已弃用。【参考方案4】:
我已经在 Spring Boot 中实现了分页。下面是我的存储库。
@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";
下面是视图,因为我用的是百里香。
<!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>
【讨论】:
【参考方案5】:如何使用原生查询实现动态分页
在这里,您可以找到存储库和服务层以及您的数据传输对象 (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 Data 相比,将生成更少的 SQL 查询。这些复杂的查询不能用 Spring Data 编写,我们必须将我们的查询指定为原生查询,仍然可以使用这种方法进行分页。
缺点:“对象”数组必须映射到 Java 对象。这是痛苦且难以维持的。
如何使用 Spring Data 实现 OffsetLimit 分页 据我所知,默认 Spring Data 存储库中没有“开箱即用”的支持。但是您可以创建一个自定义实现 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();
请注意,您不需要手动映射结果,这会占用大量开发时间。
【讨论】:
以上是关于如何使用hibernate在spring boot中实现分页的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Spring Boot 上使用 Hibernate 生成自动 UUID
如何在 Spring Boot 中使用 Hibernate/JPA 返回多级 json
如何使用 Hibernate 在 Spring Boot 中处理数据库迁移?
如何通过使用 JPA + Hibernate 和 Spring-boot 在一个数据库中使用多个模式?