SSM虾米音乐项目2--分页查询
1.分页查询的底层逻辑
- 首先根据用户输入的流派,进行模糊查询
- 根据查询的数据进行分页
- 需要前端用户提供pageNo(当前页数)和pageSize(每页的数据量)
- 并且要从后端计算count(总数据量)和totalPage(总页数),以及startNum(每页开始的记录)
- 从而将对应的页面数据展示给用户
2.分页查询的实现
分页查询所需要的所有属性
1.pageNo 当前页码 要查看的页码 前端用户决定
2.pageSize 当前每页的展示数量 前端用户决定或者后台设定好
3.startNum 开始行号 (pageNo-1)*pageSize sql语句做分页要查询的条件,通过计算得出
4.totalNum 所有页数 count/pageSize 通过计算得出
5.List 查询出的数据
6.totalCount 所有数据量 count(*) 通过查询数据库得出
分页查询的代码实现
首先创建分页的对象Page 为泛型类,因为每一个部分都需要实现分页,传递给前端,进行解读
page实体类
package com.qcby.utils;
import java.util.List;
/**
* 封装前端需要的承载数据以及分页相关的一个实体
* 自定义页的类
*/
public class Page<T> {
/**
* 每页记录数(已知)
*/
private Integer pageSize = 5;
/**
* 页码(已知)
*/
private Integer pageNo = 1;
/**
* 指定查询条件下的总记录数(已知)
*/
private Integer totalCount = 0;
/**
* 指定查询条件下 的总页数
*/
private Integer totalPage = 1;
/**
* 使用sql查询的时候的开始行号
*/
private Integer startNum = 0;
/**
* 数据结果集
*/
private List<T> list;
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getPageNo() {
return pageNo;
}
public void setPageNo(Integer pageNo) {
this.pageNo = pageNo;
}
public Integer getTotalCount() {
return totalCount;
}
public void setTotalCount(Integer totalCount) {
this.totalCount = totalCount;
}
public Integer getTotalPage() {
totalPage = totalCount/pageSize;
if(totalCount == 0 || totalCount%pageSize != 0){//如果不能整除的话,需要取整+1
totalPage++;
}
return totalPage;
}
public void setTotalPage(Integer totalPage) {
this.totalPage = totalPage;
}
public Integer getStartNum() {
return (pageNo -1 )*pageSize;
}
public void setStartNum(Integer startNum) {
this.startNum = startNum;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
}
需要将page对象传递给前端,实现对应的页面渲染
mtype实体类
package com.qcby.model;
import java.io.Serializable;
public class Mtype implements Serializable {
private Integer tid;
private String tname;
private String tdesc;
public Mtype() {
}
public Mtype(Integer tid, String tname, String tdesc) {
this.tid = tid;
this.tname = tname;
this.tdesc = tdesc;
}
public Integer getTid() {
return tid;
}
public void setTid(Integer tid) {
this.tid = tid;
}
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname == null ? null : tname.trim();
}
public String getTdesc() {
return tdesc;
}
public void setTdesc(String tdesc) {
this.tdesc = tdesc == null ? null : tdesc.trim();
}
@Override
public String toString() {
return "Mtype{" +
"tid=" + tid +
", tname='" + tname + '\'' +
", tdesc='" + tdesc + '\'' +
'}';
}
}
需要创建mtypeQuery实体类,将前端的页面数据pageNo等的数据,传递给后端进行数据查询,
mtypeQuery实体类
package com.qcby.query;
import com.qcby.model.Mtype;
/**封装query对象的目的是接受前端的请求参数,在后端处理业务逻辑,
*知道用户的分页条件和查询条件
* 只作为表现层接收前端参数封装使用
*/
public class MtypeQuery extends Mtype{
private Integer pageNo;
private Integer startNum;
private Integer pageSize=5;
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getPageNo() {
return pageNo;
}
public void setPageNo(Integer pageNo) {
this.pageNo = pageNo;
}
public Integer getStartNum() {
return startNum;
}
public void setStartNum(Integer startNum) {
this.startNum = startNum;
}
}
为什么mtype实体类当中没有设置有关页面的数据
因为在用page对象对mtype进行封装的时候,页面数据并没有用到前端的数据展示当中,而前端需要向后台服务器传递页面数据,因此需要再创建一个mtypeQuery实体类进行页面数据的封装
表现层
package com.qcby.controller;
import com.qcby.model.Mtype;
import com.qcby.utils.Page;
import com.qcby.query.MtypeQuery;
import com.qcby.service.MtypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 流派业务的表现层
* 处理前端页面的请求
*/
@Controller
@RequestMapping("/mtype")
public class MtypeController {
@Autowired
private MtypeService mtypeService;
/**
* 流派的分页条件查询接口
* @param mq
* @param model
* @return
*/
//展示流派信息
@RequestMapping("/list")
public String listType(MtypeQuery mq, Model model){
if(mq.getPageNo() == null||mq.getPageNo()<=0){
mq.setPageNo(1);
}
Page<Mtype> page = mtypeService.selectObjectByCondition(mq);
//把Page对象传递给前端,进行解析呈现数据
model.addAttribute("page", page);
model.addAttribute("mq",mq);
return "mtype";
}
}
通过model将用户查询到的page传递到前端进行页面渲染
前端用户通过mq将查询条件和分页条件 传递到后端
根据用户的查询条件进行分页的方法selectObjectByCondition需要创建在父接口BaseDao当中,再由浮层的实现类BaseServiceImpl实现,因为所有的类都需要实现该分页操作
BaseDao
package com.qcby.dao;
import java.util.List;
public interface BaseDao<Q, T> {
/**
* 保存数据
* @param t
*/
public void insert(T t);
/**
* 根据主键查询对象
* @param id
* @return
*/
public T selectByPrimaryKey(Integer id);
/**
* 根据主键删除数据
* @param id
*/
public void deleteByPrimaryKey(Integer id);
/**
* 修改数据
* @param
*/
public void updateByPrimaryKeySelective(T t);
/**
* 查询所有的记录
* @return
*/
public List<T> selectObjectAll();
/**
* 根据查询条件来查询数据
* @param q
* @return
*/
public List<T> selectObjectByCondition(Q q);
/**
* 根据查询条件来查询符合条件的记录数
* @param q
* @return
*/
public Integer selectObjectByConditionCount(Q q);
}
BaseService
package com.qcby.service;
import com.qcby.utils.Page;
import java.util.List;
public interface BaseService<Q, T> {
/**
* 保存数据
* @param t
*/
public void insert(T t);
/**
* 根据主键查询对象
* @param id
* @return
*/
public T selectByPrimaryKey(Integer id);
/**
* 根据主键删除数据
* @param id
*/
public void deleteByPrimaryKey(Integer id);
/**
* 修改数据
* @param
*/
public void updateByPrimaryKeySelective(T t);
/**
* 查询所有的记录
* @return
*/
public List<T> selectObjectAll();
/**
* 分页查询
* @param q
* @return
*/
public Page<T> selectObjectByCondition(Q q);
}
BaseServiceImpl实现方法 分页的主要逻辑实现
package com.qcby.service.impl;
import com.qcby.dao.BaseDao;
import com.qcby.utils.Page;
import com.qcby.service.BaseService;
import java.lang.reflect.Method;
import java.util.List;
public class BaseServiceImpl<Q,T> implements BaseService<Q,T> {
/**
* 可以支持两次注入,但是不好
* 选择java基础的权限修饰符,以及set方法注入的形式改成一次注入
*/
protected BaseDao<Q, T> baseDao;
@Override
public void insert(T t) {
baseDao.insert(t);
}
@Override
public T selectByPrimaryKey(Integer id) {
return baseDao.selectByPrimaryKey(id);
}
@Override
public void deleteByPrimaryKey(Integer id) {
baseDao.deleteByPrimaryKey(id);
}
@Override
public void updateByPrimaryKeySelective(T t) {
baseDao.updateByPrimaryKeySelective(t);
}
@Override
public List<T> selectObjectAll() {
return baseDao.selectObjectAll();
}
//想办法给要返回的page对象所有属性赋值的过程
@Override
public Page<T> selectObjectByCondition(Q q) {
//获得查询对象的类对象
//反射
Class<? extends Object> qclass = q.getClass();
Page<T> page = new Page<T>();
try {
//获得getPageNo对象
Method method = qclass.getMethod("getPageNo", null);
//反射调用getPageNo方法
Integer pageNo = (Integer) method.invoke(q, null);
//创建page对象,给返回去的page设置值
page.setPageNo(pageNo);
//计算开始行号和结束行号
Integer startNum = page.getStartNum();
System.out.println(startNum);
//好的查询对象 的设置开始行号和结束行号的方法
Method setStartNumMethod = qclass.getMethod("setStartNum", new Class[]{Integer.class});
setStartNumMethod.invoke(q, startNum);
} catch (Exception e) {
e.printStackTrace();
}
//查询结果集
List<T> list = baseDao.selectObjectByCondition(q);
//查询指定查询条件下的总记录数
Integer count = baseDao.selectObjectByConditionCount(q);
//把总记录数设置给page对象
page.setTotalCount(count);
page.setList(list);
return page;
}
}
为什么要通过使用反射
通过反射获取类的类对象,可以知道当前的类到底对应的具体分页对象是谁。根据传递的q,通过反射调用方法拿到具体的每一个对象的getPageNo和getPageSize,设置给要返回到前端的page对象
MtypeMapper
package com.qcby.dao;
import com.qcby.model.Mtype;
import com.qcby.query.MtypeQuery;
public interface MtypeMapper extends BaseDao<MtypeQuery,Mtype>{
}
MtypeService
package com.qcby.service;
import com.qcby.model.Mtype;
import com.qcby.query.MtypeQuery;
import com.qcby.query.MtypeQuery;
public interface MtypeService extends BaseService<MtypeQuery,Mtype>{
}
MtypeServiceImpl
package com.qcby.service.impl;
import com.qcby.query.MtypeQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.qcby.dao.MtypeMapper;
import com.qcby.model.Mtype;
import com.qcby.query.MtypeQuery;
import com.qcby.service.MtypeService;
@Service
public class MtypeServiceImpl extends BaseServiceImpl<MtypeQuery,Mtype> implements MtypeService {
private MtypeMapper mtypeMapper;
@Autowired
public void setMtypeMapper(MtypeMapper mtypeMapper) {
this.mtypeMapper = mtypeMapper;
this.baseDao=mtypeMapper;
}
}
前端页面
数据展示前端
<div class="body">
<table class="table table-striped table-images"
style="color: white;font-size: 14px">
<thead>
<tr>
<th class="hidden-xs-portrait">序号</th>
<th class="hidden-xs">流派</th>
<th class="hidden-xs">描述</th>
<th></th>
</tr>
</thead>
<tbody>
<c:forEach items="${page.list}" var="mtype" varStatus="status">
<tr>
<td class="hidden-xs-portrait">${mtype.tid}</td>
<td class="hidden-xs-portrait">${mtype.tname}</td>
<td class="hidden-xs"> ${mtype.tdesc} </td>
<td>
<button class="btn btn-sm btn-primary" type="button" modify tid="${mtype.tid}" > 修改</button>
<button data-toggle="button" class="btn btn-sm btn-warning" tid="${mtype.tid}"> 删除</button>
</td>
</tr>
</c:forEach>
</tbody>
</table>
<jsp:include page="pagination.jsp"></jsp:include>
</div>
分页部分前端
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="header.jsp"%>
<html>
<head>
<title>Title</title>
</head>
<body>
<div class="clearfix text-right">
<%--隐藏域--%>
<input type="hidden" id="pageNo" name="pageNo" value="${mq.pageNo}">
<input type="hidden" id="totalPage" value="${page.totalPage}">
<ul class="pagination no-margin">
<li id="prev" class="disabled"><a href="#">Prev</a></li>
<c:forEach begin="1" end="${page.totalPage}" var="myPageNo">
<li <c:if test="${myPageNo == mq.pageNo}">class="active"</c:if>>
<a pageNoButton href="#">${myPageNo}</a>
</li>
</c:forEach>
<li id="next"><a href="#">Next</a></li>
</ul>
</div>
</body>
</html>
上一页和下一页的逻辑
var pageNo = $("#pageNo").val();
var totalPage = $("#totalPage").val();
pageNo = parseInt(pageNo);
totalPage = parseInt(totalPage);
//如果已经到首页和尾页,并且只有一页
if (pageNo == 1 && pageNo == totalPage) {
$("#prev").addClass("disabled");
$("#next").addClass("disabled");
}
//如果在首页,且不只有一页
if (pageNo == 1 && pageNo < totalPage) {
$("#prev").addClass("disabled");
$("#next").removeClass("disabled");
}
//如果不只有一页,且不在首页和尾页
if (pageNo > 1 && pageNo < totalPage) {
$("#prev").removeClass("disabled");
$("#next").removeClass("disabled");
}
//如果不只有一页,且不在尾页
if (pageNo > 1 && pageNo == totalPage) {
$("#prev").removeClass("disabled");
$("#next").addClass("disabled");
}
$("#prev").click(function () {
$("#pageNo").val(--pageNo);
$("#txForm").submit();
})
$("#next").click(function () {
$("#pageNo").val(++pageNo);
$("#txForm").submit();
})
$("a[pageNoButton]").click(function () {
var pageNo = $(this).html();
$("#pageNo").val(pageNo);
$("#txForm").submit();
})