分页简介
用来将数据分割成多个部分来分页面展示。
什么时候用?
数据量达到一定的时候,就需要使用分页来进行数据分割。要不然可能会面临以下问题:
- 客户端一次性显示太多数据会影响到用户的体验,比如很难找到客户想要的信息,以及加载页面数据过慢。
- 对于服务端来说,一次性传送的数据过多,可能造成内存溢出。
分页的分类
分页分为真分页和假分页:
- 真分页(物理分页):
在mysql中使用select * from table where …limit start , size(在oracle中复杂些)
(page_num 代表具体第多少页,pages代表每页有多少条数据)
第一个参数start代表(page_num-1)*pages
第二个参数是 pages,即页面数据量最大是多少
还有limit如果后面只有一个参数,则相当于0,num即0行到num-1行 - 假分页(逻辑分页):
直接使用select * from table where …
这样将所有数据查询出来存放到内存中,每次需要查询时直接从内存中去取出相应索引区间的数据
区别
真分页相比于假分页不会造成内存溢出,但翻页的数据相比于假分页又慢,所以根据实际情况选择分页方式,如果数据量不大,可以考虑使用假分页使翻页速度加快。
实现
假分页
假分页实际上使用List去存储所有查询出的值,再用subList方法获取两个索引之间的数据。
先在dao层,创建StudentMapper接口
List<Student> queryStudentsByArray();
再创建StudentMapper.xml文件,编写查询语句:
<select id="queryStudentsByArray" resultType="Student">
select * from student
</select>
然后定义Service接口并定义分页方法:
List<Student> queryStudentsByArray(int currPage, int pageSize);
再在Servlce实现类中重写该方法:
@Override
public List<Student> queryStudentsByArray(int currPage, int pageSize) {
List<Student> students = studentMapper.queryStudentsByArray();
// 从第几条数据开始
int firstIndex = (currPage - 1) * pageSize;
// 到第几条数据结束
int lastIndex = currPage * pageSize;
return students.subList(firstIndex, lastIndex);
}
这里控制器和前台就根据这个方法来编写。
真分页
这里以我自己在一个个人博客后台管理type和tag的分页为例:
先封装一个Page类,用来做分页(为了简便):
package com.yuer.entity;
import java.util.ArrayList;
import java.util.List;
// 进行分页的工具类
// 使用泛型的原因是可能对不同类型进行分页
// 在first,last,start的get方法中,进行一些处理,保证这三个字段正确
public class Page<T> {
// 这里存放的每次查出来的一页内容
private List<T> content = new ArrayList<>();
// 当前页数 默认为1
private int page = 1;
// 总页数
private int totalPages = 0;
// 这个等于(page - 1) * size,代表该显示多少条数据的第一个索引
private int start = 0;
// 默认为4
private int size = 4;
// 当前是不是首页,默认为true
private boolean first = true;
// 当前是不是尾页,默认是false
private boolean last = false;
public List<T> getContent() {
return content;
}
public void setContent(List<T> content) {
this.content = content;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public int getTotalPages() {
return totalPages;
}
public void setTotalPages(int totalPages) {
this.totalPages = totalPages;
}
public int getStart() {
this.start = (page - 1) * size;
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public boolean isFirst() {
if (page != 1) {
this.first = false;
} else {
this.first = true;
}
return first;
}
public void setFirst(boolean first) {
this.first = first;
}
public boolean isLast() {
if (page != totalPages) {
this.last = false;
} else {
this.last = true;
}
return last;
}
public void setLast(boolean last) {
this.last = last;
}
public Page() {
start = (page - 1) * size;
}
}
接下来是显示层(只截取了关键部位):
<!--中间内容-->
<div class="m-container-small m-padded-tb-big main">
<div class="ui container">
<div class="ui success message" th:unless="${#strings.isEmpty(message)}">
<i class="close icon"></i>
<div class="header">提示:</div>
<p th:text="${message}">恭喜,操作成功!</p>
</div>
<table class="ui compact table">
<thead>
<tr>
<th></th>
<th>名称</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr th:each="type,iterStat : ${page.content}">
<td th:text="${iterStat.count}">1</td>
<td th:text="${type.typeName}">刻意练习清单</td>
<td>
<a href="#" th:href="@{/admin/types/{id}/input(id=${type.id})}" class="ui mini teal basic button">编辑</a>
<a href="#" th:href="@{/admin/types/{id}/delete(id=${type.id})}" class="ui mini red basic button">删除</a>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<th colspan="6" >
<!-- ${page.totalPages}>1代表看总页数是否大于1,大于则显示这两个上一页,下一页 -->
<div class="ui mini pagination menu" th:if="${page.totalPages}>1">
<!-- 这里page.first为是否是第一页,是不显示,同理page.last
page.number代表当前页数 -->
<a href="#" th:href="@{/admin/types/{page}(page=${page.page}-1)}" class=" item" th:unless="${page.first}">上一页</a>
<a href="#" th:href="@{/admin/types/{page}(page=${page.page}+1)}" class=" item" th:unless="${page.last}">下一页</a>
</div>
<a href="#" th:href="@{/admin/types/input}" class="ui mini right floated teal basic button">新增</a>
</th>
</tr>
</tfoot>
</table>
</div>
</div>
接下来是Controller层:
package com.yuer.controller.admin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.yuer.entity.Page;
import com.yuer.entity.Type;
import com.yuer.service.ITypeService;
@Controller
@RequestMapping("/admin")
public class TypeController {
@Autowired
private ITypeService typeService;
// 这个是默认的访问,不带参数
@GetMapping("/types")
public String typesDefault(Model model) {
Page<Type> page = getPage(-1, false);
model.addAttribute("page", page);
return "admin/types";
}
// page代表当前页数,当按上一页下一页会触发这个方法
@GetMapping("/types/{page}")
public String types(@PathVariable Integer page, Model model) {
Page<Type> page1 = getPage(page, true);
model.addAttribute("page", page1);
return "admin/types";
}
public Page<Type> getPage(Integer page, boolean flag) {
// 先 new Page
Page<Type> page1 = new Page<Type>();
// 先查出数据条数,再计算得出多少页
int total = typeService.getTotal();
int totalPages;
if (total % page1.getSize() == 0) {
totalPages = total / page1.getSize();
} else {
totalPages = total / page1.getSize() + 1;
}
page1.setTotalPages(totalPages);
if (flag) {
page1.setPage(page);
}
page1.setContent(typeService.listTypeByParam(page1.getStart(), page1.getSize()));
return page1;
}
}
控制层中的typesDefault方法代表首次进入分类管理,此时只需要显示第一页即可,即在Page类中配置好的默认值,而types方法则是点击上一页下一页时触发的方法,此时会传过来当前的页数,然后set进Page类中,此时不同的数据就会更新。
此时看typeService.listTypeByParam(page1.getStart(), page1.getSize()):这个方法是用来分页的关键方法:
@Transactional
@Override
public List<Type> listTypeByParam(int start, int size) {
return typeDao.listTypeByParam(start, size);
}
这个直接到mapper.xml文件中看此方法sql语句:
<!-- 这里返回值可能注入错误,需要进行Map映射 -->
<select id ="listTypeByParam" resultMap="TypeMap" parameterType="Integer">
select id,type_name from hi_type order by id limit #{start}, #{size}
</select>
<resultMap type="Type" id="TypeMap">
<result property="id" column="id"/>
<result property="typeName" column="type_name"/>
</resultMap>
总结一下:没点击上一页下一页之前默认显示0到size-1条数据,然后点击上一页下一页触发翻页功能时,就在Page类中注入相应的值,取其中的值时,就会同步的发生变化,最后通过mysql的简便分页方法limit start,size方法完成。

本文介绍了分页在大数据量展示时的重要性,详细讲解了分页的分类,包括真分页(物理分页)和假分页(逻辑分页),并分析了两者之间的区别。在实现部分,通过示例展示了如何在Java中使用假分页和真分页的实现方式,包括DAO层、Service层和Controller层的关键代码,强调了在不同场景下选择合适分页策略的必要性。
5455





