前言:在上一讲博客《Java Web基础入门第四十九讲 客户关系管理系统之分页查询》中,我们就完成了客户信息的分页查询,现在我们趁热打铁来实现学生信息的分页查询,做这个练习来巩固我们的知识。
创建MVC架构的Web项目
在Eclipse中新创建一个day15_page项目,导入项目所需要的开发包(jar包),创建项目所需要的包,在Java开发中,架构的层次是以包的形式体现出来的。
以上就是根据此项目的实际情况创建的包,可能还需要创建其他的包,这个得根据项目的需要来定了。
接着,为应用创建相应库和表。根据如下建表SQL语句在数据库中创建一张student表:
create database day15;
use day15;
create table student
(
id varchar(40),
name varchar(40)
);
并使用如下SQL语句向student表中插入一些数据:
insert into student(id,name) values('1','aa1');
insert into student(id,name) values('2','aa2');
insert into student(id,name) values('3','aa3');
insert into student(id,name) values('4','aa4');
insert into student(id,name) values('5','aa5');
insert into student(id,name) values('6','aa6');
insert into student(id,name) values('7','aa7');
insert into student(id,name) values('8','aa8');
insert into student(id,name) values('9','aa9');
insert into student(id,name) values('10','aa10');
insert into student(id,name) values('11','aa11');
insert into student(id,name) values('12','aa12');
insert into student(id,name) values('13','aa13');
insert into student(id,name) values('14','aa14');
insert into student(id,name) values('15','aa15');
insert into student(id,name) values('16','aa16');
insert into student(id,name) values('17','aa17');
insert into student(id,name) values('18','aa18');
insert into student(id,name) values('19','aa19');
insert into student(id,name) values('20','aa20');
insert into student(id,name) values('21','aa21');
insert into student(id,name) values('22','aa22');
insert into student(id,name) values('23','aa23');
除此之外,还应在src目录下创建一个db.properties文件,在db.properties中编写MySQL数据库的连接信息,内容如下所示:
最后,创建好的项目架构如下图所示:
分层架构的代码编写
分层架构的代码也是按照【域模型层(domain)】→【数据访问层(dao、dao.impl)】→【业务逻辑层(service、service.impl)】→【表现层(web.controller、web.UI、web.filter、web.listener)】→【工具类(util)】→【测试类(junit.test)】的顺序进行编写的。
开发domain层
在cn.liayun.domain包下创建一个Student类,该类的具体代码如下:
package cn.liayun.domain;
public class Student {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
紧接着,创建实现分页显示的三个对象。在cn.liayun.domain包下创建QueryInfo类,用于封装用户的请求参数。
package cn.liayun.domain;
/**
* 封装查询信息的实体
* @author liayun
*
*/
public class QueryInfo {
private int currentpage = 1;//用户当前看的页
private int pagesize = 5;//记住用户想看的页面大小
private int startindex;//记住用户看的页的数据在数据库的起始位置
public int getCurrentpage() {
return currentpage;
}
public void setCurrentpage(int currentpage) {
this.currentpage = currentpage;
}
public int getPagesize() {
return pagesize;
}
public void setPagesize(int pagesize) {
this.pagesize = pagesize;
}
public int getStartindex() {
this.startindex = (this.currentpage - 1) * this.pagesize;
return startindex;
}
}
然后在cn.liayun.domain包下创建QueryResult类,用于封装查询结果。
package cn.liayun.domain;
import java.util.List;
/**
* 用来封装查询结果的实体
* @author liayun
*
*/
public class QueryResult {
private List list;//记住用户看的页的数据
private int totalrecord;//记住总记录数
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public int getTotalrecord() {
return totalrecord;
}
public void setTotalrecord(int totalrecord) {
this.totalrecord = totalrecord;
}
}
最后在cn.liayun.domain包下创建PageBean类,该类用于将QueryResult对象中封装的查询结果,生成显示分页数据的PageBean对象。
package cn.liayun.domain;
import java.util.List;
/**
* 封装页面显示需要的信息
* @author liayun
*
*/
public class PageBean {
private List list;
private int totalrecord;
private int pagesize;
private int totalpage;
private int currentpage;
private int previouspage;
private int nextpage;
private int[] pagebar;
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public int getTotalrecord() {
return totalrecord;
}
public void setTotalrecord(int totalrecord) {
this.totalrecord = totalrecord;
}
public int getPagesize() {
return pagesize;
}
public void setPagesize(int pagesize) {
this.pagesize = pagesize;
}
public int getTotalpage() {
//根据总记录数和页面大小算出来的
//100 5 20
//101 5 21
//99 5 20
if (this.totalrecord % this.pagesize == 0) {
this.totalpage = this.totalrecord / this.pagesize;
} else {
this.totalpage = this.totalrecord / this.pagesize + 1;
}
return totalpage;
}
public int getCurrentpage() {
return currentpage;
}
public void setCurrentpage(int currentpage) {
this.currentpage = currentpage;
}
public int getPreviouspage() {
if (this.currentpage - 1 < 1) {
this.previouspage = 1;
} else {
this.previouspage = this.currentpage - 1;
}
return previouspage;
}
public int getNextpage() {
if (this.currentpage + 1 >= this.totalpage) {
this.nextpage = this.totalpage;
} else {
this.nextpage = this.currentpage + 1;
}
return nextpage;
}
public int[] getPagebar() {
int startpage;
int endpage;
int[] pagebar = null;
if (this.totalpage <= 10) {
pagebar = new int[this.totalpage];
startpage = 1;
endpage = this.totalpage;
} else {
pagebar = new int[10];
startpage = this.currentpage - 4;
endpage = this.currentpage + 5;
//总页数=30 3 -1
//总页数=30 29 34
if (startpage < 1) {
startpage = 1;
endpage = 10;
}
if (endpage > this.totalpage) {
endpage = this.totalpage;
startpage = this.totalpage - 9;
}
}
int index = 0;
for (int i = startpage; i <= endpage; i++) {
pagebar[index++] = i;
}
this.pagebar = pagebar;
return this.pagebar;
/*
int[] pagebar = new int[this.totalpage];
for (int i = 1; i <= this.totalpage; i++) {
pagebar[i - 1] = i;
}
this.pagebar = pagebar;
return pagebar;
*/
}
}
开发数据访问层(dao、dao.impl)
在开发数据访问层时,由于要编写得到MySQL数据库链接和释放资源这些性质的操作,所以应该把它们放在一个工具类JdbcUtils中。于是,在cn.liayun.utils包下创建一个JdbcUtils类,类的具体代码如下:
package cn.liayun.utils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JdbcUtils {
private static Properties config = new Properties();
// 静态代码块只执行一次,因为静态代码块在类加载时执行,类永远只加载一次
static {
try {
config.load(JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties"));
Class.forName(config.getProperty("driver"));
} catch (Exception e) {
/*
* db.properties文件都无法读取,那么整个应用程序就无法连接数据库,
* 驱动都加载不了,那么整个应用程序都无法工作,
* 所以应该抛一个错误(ExceptionInInitializerError)
*/
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(config.getProperty("url"), config.getProperty("username"), config.getProperty("password"));
}
public static void release(Connection conn, Statement st, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if (st != null) {
try {
st.close();
} catch (Exception e) {
e.printStackTrace();
}
st = null;
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
conn = null;
}
}
}
最后,在cn.liayun.dao包下创建一个StudentDao类,该类的具体代码如下:
package cn.liayun.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import cn.liayun.domain.QueryResult;
import cn.liayun.domain.Student;
import cn.liayun.utils.JdbcUtils;
public class StudentDao {
public QueryResult pageQuery(int startindex, int pagesize) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
QueryResult qr = new QueryResult();
try {
conn = JdbcUtils.getConnection();
String sql = "select * from student limit ?,?";
st = conn.prepareStatement(sql);
st.setInt(1, startindex);
st.setInt(2, pagesize);
rs = st.executeQuery();
List<Student> list = new ArrayList<Student>();
while (rs.next()) {
Student s = new Student();
s.setId(rs.getString("id"));
s.setName(rs.getString("name"));
list.add(s);
}
qr.setList(list);
sql = "select count(*) from student";
st = conn.prepareStatement(sql);
rs = st.executeQuery();
if (rs.next()) {
qr.setTotalrecord(rs.getInt(1));
}
return qr;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.release(conn, st, rs);
}
}
}
照理说,开发完数据访问层,一定要对程序已编写好的部分代码进行测试。但是我们有信心认为以上代码都不会有任何问题,这点自信都没有,搞鬼啊!
开发service层(service层对web层提供所有的业务服务)
在cn.liayun.service包下创建一个BusinessService类,该类的具体代码如下:
package cn.liayun.service;
import cn.liayun.dao.StudentDao;
import cn.liayun.domain.PageBean;
import cn.liayun.domain.QueryInfo;
import cn.liayun.domain.QueryResult;
public class BusinessService {
public PageBean pageQuery(QueryInfo info) {
StudentDao dao = new StudentDao();
QueryResult qr = dao.pageQuery(info.getStartindex(), info.getPagesize());
PageBean bean = new PageBean();
bean.setCurrentpage(info.getCurrentpage());
bean.setList(qr.getList());
bean.setPagesize(info.getPagesize());
bean.setTotalrecord(qr.getTotalrecord());
return bean;
}
}
同理,开发完业务逻辑层,一定要对程序已编写好的部分代码进行测试,但是我们有信心认为业务逻辑层的代码没有任何问题,所以我们略过测试这一步。
开发web层
开发学生信息的分页查询功能
在cn.liayun.utils包下创建一个WebUtils工具类,该工具类的功能就是封装客户端提交的表单数据到Student对象中。
package cn.liayun.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
public class WebUtils {
public static <T> T request2Bean(HttpServletRequest request, Class<T> beanClass) {
try {
T bean = beanClass.newInstance();
//得到request里面的所有数据
Map<String, String[]> map = request.getParameterMap();
//注册一个日期转换器
ConvertUtils.register(new Converter() {
@Override
public <T> T convert(Class<T> type, Object value) {
if (value == null) {
return null;
}
String str = (String) value;
if (str.trim().equals("")) {
return null;
}
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
try {
return (T) df.parse(str);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}, Date.class);
//map{name=liayun,password=123321,birthday=1995-10-01} bean(name=liayun,password=123321)
BeanUtils.populate(bean, map);
return bean;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String generateID() {
return UUID.randomUUID().toString();
}
}
接着就在cn.liayun.web.controller包中创建一个ListStudentServlet,用于处理学生分页请求。
package cn.liayun.web.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.liayun.domain.PageBean;
import cn.liayun.domain.QueryInfo;
import cn.liayun.service.BusinessService;
import cn.liayun.utils.WebUtils;
@WebServlet("/ListStudentServlet")
public class ListStudentServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
QueryInfo info = WebUtils.request2Bean(request, QueryInfo.class);
BusinessService service = new BusinessService();
PageBean bean = service.pageQuery(info);
request.setAttribute("pagebean", bean);
request.getRequestDispatcher("/liststudent.jsp").forward(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
接下来就要创建学生显示界面(liststudent.jsp)了,分页显示这一块,有很多细节需要我们注意,比如:
- 当我们浏览第一页的数据时,上一页超链接就不能显示出来;当我们浏览尾页的数据时,下一页超链接也不能显示出来;
- 当我们浏览到某一页时,页码条中的这一页就应该显示为普通文字,而不是超链接,即该页是无法点击再次查看的;
- 除了点击上一页和下一页,我们还应可自由跳转到任何页面;
- 我们还应可自由控制每页可显示的记录数。
下面我们就按照以上细节来创建学生显示界面(liststudent.jsp),listcustomer.jsp页面的内容为:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>显示学生分页数据</title>
</head>
<body>
<c:forEach var="s" items="${pagebean.list }">
${s.id } ${s.name }<br/>
</c:forEach>
<script type="text/javascript">
function gotopage(currentpage) {
if ( currentpage < 1 || currentpage != parseInt(currentpage) || currentpage > ${pagebean.totalpage } ) {
alert("请输入有效值!!!");
document.getElementById("pagenum").value='';
} else {
var pagesize = document.getElementById("pagesize").value;
window.location.href = '${pageContext.request.contextPath }/ListStudentServlet?currentpage='+currentpage+'&pagesize='+pagesize;
}
}
function changesize(pagesize, oldvalue) {
if ( pagesize < 0 || pagesize != parseInt(pagesize) ) {
alert("请输入合法值!!!");
document.getElementById("pagesize").value = oldvalue;
} else {
// 当前页currentpage就不传过去了,也即一改变页面大小,由于没带当前页过去,所以就从第1页开始显示
window.location.href = '${pageContext.request.contextPath }/ListStudentServlet?pagesize='+pagesize;
}
}
</script>
共[${pagebean.totalrecord }]条记录,
每页显示<input type="text" id="pagesize" value="${pagebean.pagesize }" onchange="changesize(this.value, ${pagebean.pagesize })" style="width:30px" maxlength="2">条,
共[${pagebean.totalpage }]页,
当前第[${pagebean.currentpage }]页
<c:if test="${pagebean.currentpage!=1 }">
<a href="javascript:void(0)" onclick="gotopage(${pagebean.previouspage })">上一页</a>
</c:if>
<c:forEach var="pagenum" items="${pagebean.pagebar }">
<c:if test="${pagenum==pagebean.currentpage }">
<font color="red">${pagenum }</font>
</c:if>
<c:if test="${pagenum!=pagebean.currentpage }">
<a href="javascript:void(0)" onclick="gotopage(${pagenum })">${pagenum }</a>
</c:if>
</c:forEach>
<c:if test="${pagebean.currentpage!=pagebean.totalpage }">
<a href="javascript:void(0)" onclick="gotopage(${pagebean.nextpage })">下一页</a>
</c:if>
<input type="text" id="pagenum" style="width: 30px">
<input type="button" value="GO" onclick="gotopage(document.getElementById('pagenum').value)">
</body>
</html>
至此,学生信息查询的分页练习就算完成了,测试结果如下: