为什么要通用分页?
1) 为了提高查询性能及节约网络流量,每次只查询指定的记录数,而不是全部,在数量比较大时很有用
2)当点击下一页或指定页面的数据时,将带着所有的查询条件,再次执行查询
最终效果展示:
图解
效果展示
实现思路:(理解)
怎么封装一个通用分页查询方法,先编写一个查询sql语句,将查询条件与分页实体放入做为方法的参数。
判断是否需要分页
1.不分页,直接查询,先遍历,然后返回结果集
2.分页,查询总页数,如果总记录为0,直接返回空的结果集,如果不为0,先编写需要显示分页条数的sql语句,然后查询当前页的数据,返回当前页的结果集(1~10页)。
封装一个分页信息实体,因为我们后面需要用它的属性
package com.zking.utils;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import com.mysql.jdbc.StringUtils;
public class PageBean {
/**
* 页码
*/
private int page = 1;
/**
* 每页显示的记录数
*/
private int rows = 10;
/**
* 总记录数
*/
private int total = 0;
/**
* 是否分页
*/
private boolean pagination = false;
/**
* 记录查询的url,以便于点击分页时再次使用
*/
private String url;
/**
* 存放请求参数,用于生成隐藏域中的元素
*/
private Map<String,String[]> parameterMap;
/**
* 根据传入的Request初始化分页对象
* @param request
*/
public void setRequest(HttpServletRequest request) {
if(!StringUtils.isNullOrEmpty(request.getParameter("page"))) {
this.page = Integer.valueOf(request.getParameter("page"));
}
if(!StringUtils.isNullOrEmpty(request.getParameter("rows"))) {
this.rows = Integer.valueOf(request.getParameter("rows"));
}
if(!StringUtils.isNullOrEmpty(request.getParameter("pagination"))) {
this.pagination = Boolean.valueOf(request.getParameter("pagination"));
}
this.url = request.getRequestURI();
this.parameterMap = request.getParameterMap();
request.setAttribute("pageBean", this);
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
this.rows = rows;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public boolean isPagination() {
return pagination;
}
public void setPagination(boolean pagination) {
this.pagination = pagination;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Map<String, String[]> getParameterMap() {
return parameterMap;
}
public void setParameterMap(Map<String, String[]> parameterMap) {
this.parameterMap = parameterMap;
}
//计算起始行号
public int getStartIndex() {
if(this.page > this.getTotalPage()) {
this.page = this.getTotalPage();
}
if(this.page < 1) {
this.page = 1;
}
return (this.page - 1) * this.rows;
}
//获取总页数
public int getTotalPage() {
if (this.getTotal() % this.rows == 0) {
return this.getTotal() / this.rows;
} else {
return this.getTotal() / this.rows + 1;
}
}
//上一页
public int getPreviousPage() {
return this.page - 1 > 0 ? this.page - 1 : 1;
}
//下一页
public int getNextPage() {
return this.page + 1 > getTotalPage() ? getTotalPage() : this.page + 1;
}
}
版本一
public List<Book> queryBook(String bname,PageBean pb){
String sql="select * from t_book";
if(!StringUtils.isNullOrEmpty(bname)) {
sql+=" where bname like ?";
}
List<Book> list=new ArrayList<>();
Connection con=null;
PreparedStatement ps=null;
ResultSet rs=null;
//false || true 或者,一个为真即为真,后续不再计算,一个为假再计算右边的表达式。
//true && true 与,先计算左边的,如果左边的不满足,后续将不再计算;如果满足,必须要满足两个才行
if(pb ==null || !pb.isPagination()) {//不分页
try {
con=DBUtil.getConection();
ps=con.prepareStatement(sql);
/**
* 由于mysql数据库自动添加成以下sql
* select * from t_book where bname like '%strName%';
* 所以这里不用转义"%";
*/
if(!StringUtils.isNullOrEmpty(bname)) {
ps.setObject(1, "%"+bname+"%");
}
rs=ps.executeQuery();
while (rs.next()) {
Book b=new Book();
b.setBid(rs.getInt(1));
b.setBname(rs.getString(2));
b.setPrice(rs.getDouble(3));
list.add(b);
}
return list;
} catch (Exception e) {
e.printStackTrace();
}
}else {
try{//需要分页
//总页面
String countsql="select count(*) from ("+sql+") p";
con=DBUtil.getConection();
ps=con.prepareStatement(countsql);
if(!StringUtils.isNullOrEmpty(bname)) {
ps.setObject(1, bname+"%");
}
rs=ps.executeQuery();
if(rs.next()) {
pb.setTotal(rs.getInt(1));
}
if(pb.getTotal()<=0) {//无记录
return list;
}
//查询当前页的数据
String pageql=sql+" limit "+pb.getStartIndex()+","+pb.getRows();
ps=con.prepareStatement(pageql);
if(!StringUtils.isNullOrEmpty(bname)) {
ps.setObject(1, bname+"%");
}
rs=ps.executeQuery();
while (rs.next()) {
Book b=new Book();
b.setBid(rs.getInt(1));
b.setBname(rs.getString(2));
b.setPrice(rs.getDouble(3));
list.add(b);
}
}catch(Exception e){
e.printStackTrace();
}finally {
DBUtil.closeDB(rs, ps, con);
}
}
return list;
}
版本二:
通用查询思路
将sql语句,参数,分页对象,转换器做为参数传入该方法,判断是否需要分页,
如果需要,编写sql(显示条数)查询当前页的数据,将数据结果集转换成对象,显示当前页面;
如果不需要,执行查询,直接返回结果集。
public class BaseDao {
/*
* 转换接口:
* 我不知道怎么将数据库结果集转换成对象,我需要covert该接口帮我进行转换。
*
*/
public interface IConvert<T>{
public List<T> convert(ResultSet rs) throws SQLException;
}
public final <T> List<T> query(
String sql,//sql
Object[] params,//参数
PageBean pb,//分页对象
IConvert<T> convert//转换器
){
List<T> list=new ArrayList<>();
Connection con=null;
PreparedStatement ps=null;
ResultSet rs=null;
/**
* ||条件或 只要满足第一个条件,后面的条件就不再判断
* && 条件与 要满足第一个条件与第二条件
*/
if(pb ==null || !pb.isPagination()) {//不分页
try {
con=DBUtil.getConection();
ps=con.prepareStatement(sql);
/*
* 循环Object[]数组,将参数一个个放入sql
*/
int i=1;
for (Object param : params) {
ps.setObject(i, param);
i++;
}
rs=ps.executeQuery();
//将查询到数据库结果集转换成对象
list=convert.convert(rs);
return list;
} catch (Exception e) {
e.printStackTrace();
}finally {
DBUtil.closeDB(rs, ps, con);
}
}else {
try{//需要分页
//总页面
String countsql="select count(*) from ("+sql+") p";
con=DBUtil.getConection();
ps=con.prepareStatement(countsql);
int i=1;
for (Object param : params) {
ps.setObject(i, param);
i++;
}
rs=ps.executeQuery();
if(rs.next()) {
pb.setTotal(rs.getInt(1));
}
if(pb.getTotal()<=0) {//无记录
return list;
}
//查询当前页的数据
String pageql=sql+" limit "+pb.getStartIndex()+","+pb.getRows();
ps=con.prepareStatement(pageql);
int j=1;
for (Object param : params) {
ps.setObject(j, param);
j++;
}
rs=ps.executeQuery();
list=convert.convert(rs);
}catch(Exception e){
e.printStackTrace();
}finally {
DBUtil.closeDB(rs, ps, con);
}
}
return list;
}
}
测试类
public class Bookdao extends BaseDao{
/**
* @param bname 查询关键字
* @param pb 分页对象
* @return
*/
public List<Book> getbook(String bname,PageBean pb){
String sql="select * from t_book";
List<Object> params=new ArrayList<>();
if(!StringUtils.isNullOrEmpty(bname)) {//判断字符串是否为空引用或者值为空的方法
sql+=" where bname like ?";
params.add(bname+"%");//添加查询条件
}
//params.toArray()当使用Arraylist数组,可以获取一个实际的数组
/**
* 子类继承父类后,this对象表达的意思,主要看this调用的是那个类的方法。如果指定父类方法,就表示调用父类方法
* 如果指定子类方法,就表示调用子类方法
*/
List<Book> list=this.query(sql, params.toArray(), pb, new IConvert<Book>() {
@Override
public List<Book> convert(ResultSet rs) throws SQLException {
List<Book> list = new ArrayList<>();
while (rs.next()) {
Book b=new Book();
b.setBid(rs.getInt(1));
b.setBname(rs.getString(2));
b.setPrice(rs.getDouble(3));
list.add(b);
}
return list;
}
});
return list;
}
public static void main(String[] args) {
Bookdao bs=new Bookdao();
PageBean pageBean = new PageBean();
System.out.println(pageBean.isPagination());//结果:flase
pageBean.setPagination(true);
List<Book> queryBook = bs.getbook("",pageBean );
System.out.println(queryBook);
}
}
注意事项:
||条件或 只要满足第一个条件,后面的条件就不再判断
&& 条件与 要满足第一个条件与第二条件转换接口:
作用:我不知道怎么将数据库结果集转换成对象,我需要covert该接口帮我进行转换。
代码展示
public interface IConvert<T>{ public List<T> convert(ResultSet rs) throws SQLException; } public final <T> List<T> query( String sql,//sql Object[] params,//参数 PageBean pb,//分页对象 IConvert<T> convert//转换器 ){ //将查询到数据库结果集转换成对象 list=convert.convert(rs); }
this对象
public class Bookdao extends BaseDao{ /** * * @param bname 查询关键字 * @param pb 分页对象 * @return */ public List<Book> getbook(String bname,PageBean pb){ String sql="select * from t_book"; List<Object> params=new ArrayList<>(); if(!StringUtils.isNullOrEmpty(bname)) {//判断字符串是否为空引用或者值为空的方法 sql+=" where bname like ?"; params.add(bname+"%");//添加查询条件 } //params.toArray()当使用Arraylist数组,可以获取一个实际的数组 List<Book> list=this.query(sql, params.toArray(), pb, new IConvert<Book>() { @Override public List<Book> convert(ResultSet rs) throws SQLException { return list; } }); return list; }
this子类继承父类后,this对象表达的意思,主要看this调用的是那个类的方法。如果指定父类方法,就表示调用父类方法;如果指定子类方法,就表示调用子类方法。
公用代码
只要是分页,就会统计总记录数,而总记录数的统计是在业务sql外封装了一个select count(*)是有规律可循的,可以通用
只要是分页,则封装分页sql也是有规律可循的(在业务sql后加limit子句即可),可以通用
Book页面
<h2>学生信息</h2>
<form action="<%=request.getContextPath()%>/BookServlet" method="post">
<input type="text" name="bname"> <input type="submit"
value="查询">
</form>
<!--如果该集合为空,就将servlet数据加载到页面 -->
<c:if test="${empty list }">
<jsp:forward page="/BookServlet"></jsp:forward>
</c:if>
<table border="1" style="width: 100%" >
<tr>
<td>编号</td>
<td>书名</td>
<td>价格</td>
</tr>
<c:forEach items="${list }" var="b">
<tr>
<td>${b.bid }</td>
<td>${b.bname }</td>
<td>${b.price }</td>
</tr>
</c:forEach>
</table>
<div style="text-align: right; width:98%;">
第${pageBean.page}页
共${pageBean.totalPage}条记录
<a href="javascript:goPage(1)">首页</a>
<a href="javascript:goPage(${pageBean.previousPage })">上页</a>
<a href="javascript:goPage(${pageBean.nextPage })">下页</a>
<a href="javascript:goPage(${pageBean.totalPage })">尾页</a>
第<input type="text" size="2" id="pageNumber" onkeyup="toPage(event,this.value)" />
<a href="#">GO</a>
</div>
<!-- 用于分页的隐藏表单 -->
<form action="${pageBean.url}" id="pagingForm" method="post">
<input type="hidden" name="page" value="${pageBean.page}"/>
<!-- 先只考虑本功能的查询参数,没有考虑公用性(不同功能的参数不同) -->
<input type="hidden" name="bname" value="<%=request.getParameter("bname")%>"/>
</form>
<script type="text/javascript">
function goPage(pageNum){
let form=document.getElementById("pagingForm");
form.page.value=pageNum;
form.submit();
}
/**
*键盘点击事件
*event事件对象
*/
function toPage(e,pageNumber){
if(e.keyCode == 13){//回车键的阿斯特吗值
goPage(pageNumber);
}
}
</script>
与数据库交互的BookServlet
@WebServlet("/BookServlet")
public class BookServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PageBean pb=new PageBean();
pb.setRows(5);
pb.setPagination(true);
pb.setRequest(request);//PageBean保存的
String bname=request.getParameter("bname");
Bookdao b=new Bookdao();
List<Book> list = b.query1Book(bname,pb);
request.setAttribute("list", list);
request.getRequestDispatcher("Book.jsp").forward(request, response);
}
}