在实际开发中我们经常会用到表格展示数据,那么问题来了,当数据比较多的时候,每次查看都加载全部数据会严重影响系统性能,用户大部分时候也只是查看某几条数据,不需要加载全部数据。分页能比较好的解决这个问题,一次加载只加载一定数量的数据,提高系统性能和用户体验。比较常用的分页实现方法是使用PageHelper分页插件。所以本篇我们就不用了,自己写,凭实力分页。
前置条件:耐性100%;IDEA2019;jstl标签;EL表达式;ssm框架;lombok插件(也可以自己写get/set方法)
文章目录
创建一张数据不少的表
这里我用了之前学爬虫时候爬下来的一张表(几百条数据),盆友们可以自己建一张表写点数据
数据库实现分页
说到数据库分页应该很多盆友就想到了—— LIMIT
从0开始,显示10条数据
接下来程序实现的分页功能走到最后其实就是实现这条分页sql语句
创建对应的实体类
没用lombok也可以自己写get/set方法
package com.ssm.domain;
import lombok.Data;
/**
* @author Mr.锵
* date 2020-04-22
*/
@Data
public class Movie {
private Integer id;
private String mname;
private String mdesc;
private String mimg;
private String mlink;
}
创建Dao接口
这里想想我们需要什么,分页的数据(毋庸置疑),怎么知道分多少页?得知道总共有多少条数据
/**
* 分页查询电影信息
* @param num
* @param size
* @return
*/
@Select("select * from movie_resource order by id limit #{num},#{size}")
List<Movie> findAll(@Param("num") Integer num, @Param("size") Integer size);
/**
* 查询总电影数
* @return
*/
@Select("select count(1) from movie_resource")
Integer countAll();
创建service接口及实现方法
接口
package com.ssm.service;
import com.ssm.domain.Movie;
import java.util.List;
/**
* @author Mr.锵
* date 2020-04-22
*/
public interface IMovieService {
/**
* 分页查询电影信息
* @param num
* @param size
* @return
*/
List<Movie> findAll(Integer num, Integer size);
/**
* 查询总电影数
* @return
*/
Integer countAll();
}
实现类
package com.ssm.service.Impl;
import com.ssm.dao.IMovieDao;
import com.ssm.domain.Movie;
import com.ssm.service.IMovieService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author Mr.锵
* date 2020-04-22
*/
@Service
public class IMovieServiceImpl implements IMovieService {
@Autowired
private IMovieDao movieDao;
@Override
public Integer countAll() {
return movieDao.countAll();
}
@Override
public List<Movie> findAll(Integer num, Integer size) {
return movieDao.findAll(num,size);
}
}
创建控制器
这里传两个参数page(跳转页数,默认1),size(显示条数,默认10)
package com.ssm.controller;
import com.ssm.domain.Movie;
import com.ssm.service.IMovieService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
/**
* @author Mr.锵
* date 2020-04-22
*/
@Controller
@RequestMapping("/movie")
public class MovieController {
@Autowired
private IMovieService movieService;
@RequestMapping("/findAll")
public ModelAndView findAll(@RequestParam(required = false,defaultValue ="1")Integer page,
@RequestParam(required = false,defaultValue ="10")Integer size){
Integer countAll = movieService.countAll();
ModelMap modelMap=new ModelMap();
Integer num=(page-1)*size;
List<Movie> all = movieService.findAll(num, size);
modelMap.addAttribute("movielist",all);//当前页数据
modelMap.addAttribute("countAll",countAll);//总行数
ModelAndView mv=new ModelAndView("movie",modelMap);
return mv;
}
}
创建jsp页面
-
这里用了bootstrap框架画表格,可以忽略,搞个表格就行了;
-
注意,因为用了jstl标签和el表达式,所以要开启一下
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %><%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2020/4/22
Time: 13:06
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Movie</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css">
<script type="text/javascript" src="http://code.jquery.com/jquery-1.10.0.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
<style>
table{
table-layout:fixed;
}
td{
overflow:hidden;
white-space:nowrap;
text-overflow:ellipsis;
}
</style>
</head>
<body>
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<td >编号</td>
<td>电影名</td>
<td>描述</td>
<td>链接</td>
</tr>
</thead>
<tbody>
<c:forEach items="${movielist}" var="movie">
<tr>
<td title="${movie.id}">${movie.id}</td>
<td title="${movie.mname}">${movie.mname}</td>
<td title="${movie.mdesc}">${movie.mdesc}</td>
<td title="${movie.mlink}"><a href="${movie.mlink}">${movie.mlink}</a></td>
</tr>
</c:forEach>
</tbody>
</table>
</body>
</html>
地址栏实现分页
然后就可以通过地址栏直接控制页数和显示条数啦
当然,仅仅这样是满足不了我们的需求的,我们还需要什么呢?
- 需要知道总共有多少页有多少条数据,当前是第几页;
- 解决页数不存在的问题
- 一页显示多少条数据;
- 通过点击实现跳转,上一页、下一页;
- 直接到某一页、去首页、去最后一页;
- 自己决定一页显示多少条数据
优化分页功能(controller部分)
主要是在控制器做优化
总条数——直接count函数统计
Integer countAll = movieService.countAll();
modelMap.addAttribute("countAll",countAll);//总条数
每页显示条数——就是传过来的size,但是要判断是否有效,且需要在总页数前,因为size做分母,不能为0
if(size<=1){
size=1;
}else if (size >= countAll) {
size=countAll;
}
modelMap.addAttribute("page_size",size);//每页显示行数
总页数——总条数/每页显示条数,有余数则加一
Integer add=0;
if(countAll%size!=0){
add =1;
}
Integer total = (countAll/size)+add;
modelMap.addAttribute("page_total",total);//总页数
当前页数——就是传过来的page,但是要判断是否有效
if(page<=1){
page=1;
}else if(page>=total){
page = total;
}
modelMap.addAttribute("page_now",page);//当前页
控制jsp页面显示第几页到第几页——设置begin和end两个参数,默认显示5个页码(细品)
int begin = page-2;
if(begin<1)begin=1;
int end =begin+4;
if(end>total)end = total;
if(end<=5)begin = 1;
else begin = end-4;
modelMap.addAttribute("begin",begin);//开始页数
modelMap.addAttribute("end",end);//结束页数
通过modelandview传到movie.jsp
modelMap.addAttribute("movielist",all);//当前页数据
modelMap.addAttribute("page_now",page);//当前页
modelMap.addAttribute("page_total",total);//总页数
modelMap.addAttribute("page_size",size);//每页显示行数
modelMap.addAttribute("countAll",countAll);//总行数
modelMap.addAttribute("begin",begin);//开始页数
modelMap.addAttribute("end",end);//结束页数
ModelAndView mv=new ModelAndView("movie",modelMap);
优化分页功能(jsp部分)
controller把数据传过来后就进行布局吧
当前:${page_now}页 总共:${page_total}页/${countAll}条 一页显示<input value="${page_size}" class="input_text" style="width: 30px" onfocusout="change_size()">条
<c:if test="${page_now != 1}" var="result">
<a href="" id="frist">首页</a>
<a href="" id="pre">上一页</a>
</c:if>
<c:forEach var="i" begin="${begin}" end="${end}" step="1">
<c:if test="${page_now != i}" var="result"> <a href="" class="pagelink" onclick="change_page('${i}')">${i}</a> </c:if>
<c:if test="${!result}" var="result"> ${i} </c:if>
</c:forEach>
<c:if test="${page_now != page_total}" var="result">
<a href="" id="next">下一页</a>
<a href="" id="last">尾页</a>
</c:if>
如无意外,现在表格下面应该就多出这一行了,并且细心的盆友会发现少了“首页”和“上一页”这两个按钮,因为第一页就是首页,我们不需要这两个按钮,所以进行判断去掉,同时我们也可以通过这样把跳转页数控制在有效范围内,那为什么controller还要判断呢,万一用户通过地址栏跳转呢是吧
当然,现在各个元素都是死的,我们要用JavaScript注入灵魂。
优化分页功能(JavaScript部分)
先来实现a标签的跳转,点击的时候根据对应的id/class对href赋值并进行跳转;
- 注意 显示的页码数因为是循环赋值的,所以用id的话会很麻烦,这里选用class,点击哪一页,所有的页码数的href属性都会赋予点击页的值,因为href的赋值是一样的,所以还是会跳转到点击的页码数,进而实现功能(给自己鼓掌~)
$(function () {
$("#frist").click(function () {
$("#frist").attr("href","../movie/findAll?page=1&&size="+$(".input_text").val()+"")
});
$("#last").click(function () {
$("#last").attr("href","../movie/findAll?page=${page_total}&&size="+$(".input_text").val()+"")
});
$("#pre").click(function () {
$("#pre").attr("href","../movie/findAll?page=${page_now - 1}&&size="+$(".input_text").val()+"")
});
$("#next").click(function () {
$("#next").attr("href","../movie/findAll?page=${page_now + 1}&&size="+$(".input_text").val()+"")
});
});
function change_page(num) {
$(".pagelink").attr("href","../movie/findAll?page="+num+"&&size="+$(".input_text").val()+"")
}
然后是自定义每页显示多少条数据,因为select标签不够自由,所以选择了input标签,输入多少条就显示多少条,当然,输入值肯定是要在1~总页数之间的,不在这个范围内的弹框提示错误,这里选择在取消焦点的时候(onfocusout)调用函数实现判断和刷新
function change_size() {
var input_text=$(".input_text").val();
if(input_text>=1&&input_text<=${countAll})
window.location.href="../movie/findAll?page=${page_now}&&size="+$(".input_text").val()+"";
else {
alert("请输入正确行数");
$(".input_text").val("${page_size}")
}
}
结尾
好了,现在就可以随心所欲、为所欲为的分页了。坚持看到这里的盆友,不知道有没有觉得自己变牛逼了,绝大部分的分页逻辑就是这样的,pythonDjango框架的分页实现,底层逻辑也差不多是这样的,一通百通。当然,相信很多盆友会觉得这样很麻烦,所以实际开发中我们可以直接用PageHelper分页插件,这个插件就是把这些方法封装了起来,下一篇介绍。
第10篇啦!!!感觉有用就赏个赞呗~