1.总结一下编码的问题
-
对于request.setCharacterEncoding(“utf-8”);用来确保发往服务器的参数的编码格式,设置从request中取得的值或从数据库中取出的值。指定后可以通过request.getParameter()获取自己想要的字符串,如果没有提前指定,则会按照服务器端默认的“iso-8859-1”来进行编码;
当提交的表单中存在中文字符时务必使用这个编码语句。 -
response.setContentType(“text/html;charset=utf-8”);的作用是使客户端浏览器,区分不同种类的数据,并根据不同的MIME调用浏览器内不同的程序嵌入模块来处理相应的数据。当你采用response想要输出中文字符串到浏览器中时,务必使用这个语句。
针对以上两个,我们一般都会默认加载servlet代码中。
- 在利用response下载文件的案例中,当我们出现中文文件名的时候,每个浏览器对中文数据的展示都不尽相同,这就需要我们根据浏览器不同的版本信息,设置filename的编码方式不同。
具体使用到了DownLoadUtils工具类
package cn.itcast.web.util;
//import sun.misc.BASE64Encoder;
import org.apache.commons.codec.binary.Base64;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class DownLoadUtils {
public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
if (agent.contains("MSIE")) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐浏览器
filename = "=?utf-8?B?" + Base64.encodeBase64String(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}
无独有偶,在后面的cookie的学习中,cookie支持中文数据。但是特殊字符还是不支持。所以当我们涉及到有时间日期等数据的展示时,依然建议使用URL编码存储,URL解码解析。
2.采用Cookie做一个每次用户登录都能看自己上一次登录时间的功能。
需求:
1. 访问一个Servlet,如果是第一次访问,则提示:您好,欢迎您首次访问。
2. 如果不是第一次访问,则提示:欢迎回来,您上次访问时间为:显示时间字符串
思路:
1.首先获取所有的cookie,判断cookie数组是否为空,是否长度为0,判断cookie的name中是否有我们提前设定的那个参数。
2.如果存在该参数名,那么直接通过键值对的方式通过姓名获取值,并以字符串的方式输出到浏览器上进行显示。
3.把当前登录的时间节点存入cookie
4.如果找不到这个参数名,那么就直接新创建一个cookie,获取当前时间节点并将名字和值放入cookie中。并给前端输出“欢迎首次访问”的字样
package cn.itcast.cookie;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
@WebServlet("/cookieTest")
public class CookieTest extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//1.获取所有的Cookie
Cookie[] cookies = request.getCookies();
boolean flag = false;
//2.遍历所有的cookie
if(cookies != null && cookies.length>0){
for (Cookie cookie : cookies) {
String name = cookie.getName();
if("lasttime".equals(name)){
flag=true;
String value = cookie.getValue();
System.out.println("解码前:"+value);
value = URLDecoder.decode(value,"utf-8");
System.out.println("解码后:"+value);
response.getWriter().write("<h1>欢迎回来,您上次访问时间为:"+value+"</h1>");
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String str_date = sdf.format(date);
System.out.println("编码前"+str_date);
str_date = URLEncoder.encode(str_date, "utf-8");
System.out.println("编码后"+str_date);
cookie.setValue(str_date);
cookie.setMaxAge(60 * 60 * 24 * 30);//一个月
response.addCookie(cookie);
break;
}
}
}
if(cookies == null || cookies.length==0 || flag==false){
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String str_date = sdf.format(date);
System.out.println("编码前"+str_date);
str_date = URLEncoder.encode(str_date, "utf-8");
System.out.println("编码后"+str_date);
Cookie cookie = new Cookie("lasttime",str_date);
cookie.setValue(str_date);
cookie.setMaxAge(60 * 60 * 24 * 30);//一个月
response.addCookie(cookie);
response.getWriter().write("<h1>您好,欢迎您首次访问</h1>");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
3.数据共享
我们会议一下request的转发机制和response的重定向。
转发:
- 通过request对象获取请求转发器对象:
RequestDispatcher getRequestDispatcher(String path)
- 使用RequestDispatcher对象来进行转发:
forward(ServletRequest request, ServletResponse response)
其中可以通过setAttribute(String name,Object obj)
来存储数据,通过getAttribute(String name)
来获取数据,这样就实现了数据的共享
重定向:
通过response.sendRedirect("/day15/responseDemo2");
直接实现重定向,它本身并不存在共享数据这一说
ServletContext对象:
这个也可以实现数据的共享,但是其范围太大了,在所有用户所有请求的数据间进行共享
Session
它正是为了解决重定向无法在规定范围内传输数据的问题
4.带验证码的登录功能
- 访问带有验证码的登录页面login.jsp
- 用户输入用户名,密码以及验证码。
- 如果用户名和密码输入有误,跳转登录页面,提示:用户名或密码错误
- 如果验证码输入有误,跳转登录页面,提示:验证码错误
- 如果全部输入正确,则跳转到主页success.jsp,显示:用户名,欢迎您
- 先写一个前端的简单的表单用于基本信息的提交,当然我们现在都采用jsp来进行前端的编写:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>login</title>
<script>
window.onload = function(){
document.getElementById("img").onclick = function(){
this.src="/day16/checkCodeServlet?time="+new Date().getTime();
}
}
</script>
</head>
<body>
<table>
<tr>
<td>用户名</td>
<td><input placeholder="请输入用户名" name="username" type="text"></td>
</tr><tr>
<td>密码</td>
<td><input placeholder="请输入密码" name="password" type="text"></td>
</tr><tr>
<td>验证码</td>
<td><input placeholder="请输入验证码" name="checkCode" type="text"></td>
</tr><tr>
<td colspan="2"><img src="session/CheckCodeServlet" id="img"></td>
</tr><tr>
<td colspan="2"><input type="submit" value="登录"></td>
</tr>
</table>
</body>
</html>
- 一旦涉及到数据库的操作,我们先进行导包,写好配置文件,创建好User类并给其加上getter/setter方法,再重写toString方法,如下所示
package cn.itcast.domain;
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
- 可以开始编写loginServlet类了
- 获取请求参数
- 从session获取验证码,然后迅速对session进行清空(防止验证码被重复使用)
- 先判断验证码是否正确,正确就再对用户进行验证,不正确需要采用request的转发机制进行反馈。
- 对用户的验证,需要先创建一个用户对象,然后再把对象传入我们提前写好的数据库查询代码中。如果正确就需要利用session进行数据的共享,并采用response的转发机制,将其转发至成功登陆页面。不正确需要采用request的转发机制进行反馈。
package cn.itcast.servlet;
import cn.itcast.dao.UserDao;
import cn.itcast.domain.User;
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 javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
//获取请求参数
String username = request.getParameter("username");
String password = request.getParameter("password");
String checkCode = request.getParameter("checkCode");
//获取验证码
HttpSession session = request.getSession();
String checkCode_session = (String)session.getAttribute("checkCode_session");
//获取之后马上对session进行清空,避免重复使用
session.removeAttribute("checkCode_session");
//先对验证码进行判断
if(checkCode_session != null && checkCode_session.equalsIgnoreCase(checkCode)){
User loginuser = new User();
loginuser.setUsername(username);
loginuser.setPassword(password);
if(new UserDao().login(loginuser)!=null){
session.setAttribute("user",username);
response.sendRedirect(request.getContextPath()+"/success.jsp");
}else{
request.setAttribute("login_error","用户名或密码错误");
request.getRequestDispatcher("/login.jsp").forward(request,response);
}
}else {
request.setAttribute("cc_error","验证码输入错误");
request.getRequestDispatcher("login.jsp").forward(request,response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
- 这一步就是写数据库查询的代码了,直接利用queryForObject对单个对象进行查询,最后将查询到的对象反馈给服务器。只不过这里需要注意的是需要我们手动使用try…catch…将我们的函数代码进行包裹,因为当我们查询不到这个对象时就会报错,需要我们在catch中写上return null,并在服务器端使用是否等于null进行判断。
package cn.itcast.dao;
import cn.itcast.domain.User;
import cn.itcast.util.JDBCUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
public class UserDao {
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
public User login(User loginUser){
try {
String sql = "select * from user where username=? and password=?";
User user = template.queryForObject(sql,
new BeanPropertyRowMapper<User>(User.class),
loginUser.getUsername(), loginUser.getPassword());
return user;
} catch (DataAccessException e) {
e.printStackTrace();
return null;
}
}
}
- 最后一步就是完善前端,将服务器反馈的信息发送到前端,这一步比较简单就直接在原先的login.jsp后面加上:
<div> <%=request.getAttribute("cc_error") == null ? "" : request.getAttribute("cc_error") %> </div>
<div> <%=request.getAttribute("login_error") == null ? "" : request.getAttribute("login_error") %> </div>
如果觉得有必要的话,可以给其加上一定的样式来着重标注
对于success.jsp就只有一句话要写:
<h1><%= request.getSession().getAttribute("user") %>,登陆成功</h1>
5.最后一个大案例
- 综合练习
-
简单功能
- 列表查询
- 登录
- 添加
- 删除
- 修改
-
复杂功能
- 删除选中
- 分页查询
- 好处:
- 减轻服务器内存的开销
- 提升用户体验
- 好处:
-
复杂条件查询
最后这个大案例整体坐下来还是要注意很多细节的,主要把握两点:- 注意信息共享与程序调用之间的逻辑
- 注意细节,不断优化代码
-
-
首先介绍一些大致的代码框架和静态页面
着这个代码中,我们前端都是采用jsp代码进行展示,后端主要分了三个包承担不同的职能:
* servlet包负责对前端请求的接收以及响应,他会将数据发送给service中的* 相关代码进行处理
* service主要是对servlet发来的一些数据进行处理,方便dao中的数据查询
* dao主要就是在数据库中查找我们想要的信息。
为了更加规范,我们在service和dao的包中采用接口先对要用到的方法进行定义,然后新建类来对其进行实现,对其中的方法进行重写
所以最后形成的文件结构如下图所示:
-
我们从简单开始,先实现第一个功能,即列表查询,即点击查询所有用户信息即可自动跳转到用户信息显示页面,将数据库中所有用户的信息显示出来。
这个逻辑很简单,一旦点击查询所有用户信息,那么我们就可以直接跳转到相关的userListServlet中,然后我们直接调用findAll方法对数据库中的数据进行查询(当然中间经过了一个service包)。然后我们将反馈回来的list采用request进行数据共享,接着将其转发给list.jsp。随后采用jsp对查询到的数据进行显示
servlet代码如下:
package cn.itcast.web.servlet;
import cn.itcast.domain.User;
import cn.itcast.service.UserService;
import cn.itcast.service.impl.UserServiceImpl;
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 java.io.IOException;
import java.util.List;
@WebServlet("/userListServlet")
public class UserListServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
UserService service = new UserServiceImpl();
List<User> user = service.findAll();
request.setAttribute("user",user);
request.getRequestDispatcher("list.jsp").forward(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
数据库代码如下:
public class UserDaoImpl implements UserDao {
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
@Override
public List<User> findAll() {
String sql = "select * from user";
List<User> Users = template.query(sql, new BeanPropertyRowMapper<User>(User.class));
return Users;
}
}
前端用于显示的代码如下:
</tr>
<c:forEach var="user" items="${user}" varStatus="s">
<tr>
<td>${s.count}</td>
<td>${user.name}</td>
<td>${user.gender}</td>
<td>${user.age}</td>
<td>${user.address}</td>
<td>${user.qq}</td>
<td>${user.email}</td>
<td><a class="btn btn-default btn-sm" href="update.html">修改</a> <a class="btn btn-default btn-sm" href="">删除</a></td>
</tr>
</c:forEach>
<tr>
- 功能2:登录功能,登录之后有跳转到index.jsp,上面有欢迎信息。这个时候需要我们在数据库中给每一行数据再添加两个属性:username和password。除此之外,我们还需要验证码点击切换的功能。然后登陆功能就和前面的类似了,唯一不同的是,我们将前端输入的数据以map的形式获取,然后直接封装成对象,并对其进行校验。如果登陆成功就通过respose进行重定向,并通过session将用户名信息传递给index.jsp页面。
主要代码如下:
servlet代码
package cn.itcast.web.servlet;
import cn.itcast.domain.User;
import cn.itcast.service.impl.UserServiceImpl;
import org.apache.commons.beanutils.BeanUtils;
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 javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.设置编码
request.setCharacterEncoding("utf-8");
//2.获取数据
String verifycode = request.getParameter("verifycode");
Map<String, String[]> map = request.getParameterMap();
//2.1获取用户验证码
//3.验证码校验
HttpSession session = request.getSession();
String checkcode_server = (String)session.getAttribute("CHECKCODE_SERVER");
session.removeAttribute("CHECKCODE_SERVER");
if(!checkcode_server.equalsIgnoreCase(verifycode)){
request.setAttribute("msg","验证码错误");
request.getRequestDispatcher("/login.jsp").forward(request,response);
return;
}
//4.封装对象
User user = new User();
try {
BeanUtils.populate(user,map);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//5.使用Service进行查询
UserServiceImpl service = new UserServiceImpl();
User loginUser = service.login(user);
if(loginUser!= null){
session.setAttribute("user",loginUser);
response.sendRedirect(request.getContextPath()+"/index.jsp");
}else{
request.setAttribute("msg","用户名或密码错误");
request.getRequestDispatcher("/login.jsp").forward(request,response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
数据库操作代码:
@Override
public User findUserByUsernameAndPassword(String username, String password) {
try {
String sql = "select * from user where username=? and password=?";
User user = template.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class),username,password);
return user;
} catch (DataAccessException e) {
return null;
}
}
前端展示在这里就不再说了,参考上一题。
- 添加联系人功能
需要点击添加联系人就跳转到联系人信息输入的表单界面,提交之后就在数据库中添加了一条联系人数据,并需要在list.jsp页面上进行显示
直接将前端传过来的数据封装成对象,然后利用数据库将信息存进去,最后再采用response进行跳转即可。
主要代码:
servlet代码:
package cn.itcast.web.servlet;
import cn.itcast.domain.User;
import cn.itcast.service.UserService;
import cn.itcast.service.impl.UserServiceImpl;
import org.apache.commons.beanutils.BeanUtils;
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 java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
@WebServlet("/addUserServlet")
public class AddUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
Map<String, String[]> map = request.getParameterMap();
User user = new User();
try {
BeanUtils.populate(user,map);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
UserService service = new UserServiceImpl();
service.addUser(user);
response.sendRedirect(request.getContextPath()+"/userListServlet");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
数据库代码:
public void add(User user) {
String sql = "insert into user value(null,?,?,?,?,?,?,666,6666)";
template.update(sql,user.getName(),user.getGender(),user.getAge(),user.getAddress(),user.getQq(),user.getEmail());
}
前端就是一个普通的表单而已,在这里就不再讲了
- 删除功能
删除功能需要在点击删除的时候进行提示,得到用户确认之后再将此数据进行删除。这里就不再说服务器和数据库的代码了,主要就是前端的一个操作,通过设置一个函数,在得到用户确认删除的信息之后再进行跳转。
<a class="btn btn-default btn-sm" href="javascript:deleteUser(${user.id})">删除</a>
<script>
function deleteUser (id) {
if(confirm("您确定要删除吗?")){
//直接地址栏跳转到删除功能的servlet
location.href="${pageContext.request.contextPath}/delUserServlet?id="+id;
}
}
</script>
- 修改功能
修改界面就采用类似于前面的联系人信息添加页面,只不过这里有两个步骤,首先要对被操作的这一行数据进行回显,修改之后提交并显示。
这里就涉及两个servlet,一个用于查询用户数据并进行回显,一个用于将提交过来的新的用户数据进行更新。
其中需要注意的是:用户信息进行回显的时候遇到单选的框需要采用标签对其进行判断,其主要代码如下:
FindUserServlet:
package cn.itcast.web.servlet;
import cn.itcast.domain.User;
import cn.itcast.service.UserService;
import cn.itcast.service.impl.UserServiceImpl;
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 java.io.IOException;
@WebServlet("/findUserServlet")
public class FindUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String id = request.getParameter("id");
UserService service = new UserServiceImpl();
User user = service.findUserById(id);
request.setAttribute("user",user);
request.getRequestDispatcher("/update.jsp").forward(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
UpdateUserServlet:
package cn.itcast.web.servlet;
import cn.itcast.domain.User;
import cn.itcast.service.UserService;
import cn.itcast.service.impl.UserServiceImpl;
import org.apache.commons.beanutils.BeanUtils;
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 java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
@WebServlet("/updateUserServlet")
public class UpdateUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
Map<String, String[]> map = request.getParameterMap();
User user = new User();
try {
BeanUtils.populate(user,map);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
UserService service = new UserServiceImpl();
service.updateUser(user);
response.sendRedirect(request.getContextPath()+"/userListServlet");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
前端界面主要就展示单选框的这一部分:
<div class="form-group">
<label>性别:</label>
<c:if test="${user.gender=='男'}">
<input type="radio" name="gender" value="男" checked />男
<input type="radio" name="gender" value="女" />女
</c:if>
<c:if test="${user.gender=='女'}">
<input type="radio" name="gender" value="男" />男
<input type="radio" name="gender" value="女" checked />女
</c:if>
</div>
- 删除选中功能
为了让我们的服务器知道删除对应的哪些信息,我们需要设置一个表单,将单选框像参数一样进行提交。还需要设置当表头的勾选框被勾选之后,整个表被全选,相反表头取消勾选,表格也取消全选。所以这个工作主要都在前端的jsp部分。
window.onload = function () {
document.getElementById("firstCb").οnclick=function () {
var cbs = document.getElementsByName("uid");
for (var i = 0; i < cbs.length; i++) {
cbs[i].checked = this.checked;
}
}
document.getElementById("delSelected").onclick = function () {
if(confirm("您确定要删除选中条目吗?")){
var cbs = document.getElementsByName("uid");
for (var i = 0; i < cbs.length; i++) {
//这里是为了避免空指针异常,必须要保证有数据选中后才提交表单
//除了在前端要进行这样的校验外,后端也应该有对应的校验代码。
if(cbs[i].checked){
document.getElementById("form").submit();
break;
}
}
}
}
}
<a class="btn btn-primary" href="javascript:void(0);" id="delSelected">删除选中</a>
<th><input type="checkbox" id="firstCb"></th>
<td><input type="checkbox" name="uid" value="${user.id}"></td>
- 分页查询是最复杂的功能了
主要需要解决输入和输出两个问题,一个是将我们前端的分页相关数据传递给后端的服务器,还有一个就是将我们在后台数据库中查找到的数据发送给前端进行展示。
针对第一个问题,我们需要创建一个分页对象,专门用于分页数据的表示,如下图:
我们将这个对象在服务器中创建好之后发给客户端,然后客户端直接进行显示就可以了。而创建对象需要客户端的两个数据输入到服务器,一个是当前页数currentPage,一个是每页显示条数rows,只有知道rows才能只带totalPage.
第一步:先将PageBean分页对象封装出来:
package cn.itcast.domain;
import java.util.List;
public class PageBean<T> {
private int totalCount;//总记录数
private int totalPage;//总页码数
private List<T> list;//每页的数据
private int currentPage;//当前页码数
private int rows;//每页显示的记录数
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
this.rows = rows;
}
@Override
public String toString() {
return "PageBean{" +
"totalCount=" + totalCount +
", totalPage=" + totalPage +
", list=" + list +
", currentPage=" + currentPage +
", rows=" + rows +
'}';
}
}
第二步:后端代码进行编写,先写一个FindUserByPage,在这里我们需要完成当前页和每页显示条数的数据获取,以及调用service和dao包中的函数实现对PageBean对象的创建并将其传递给前端界面进行显示
package cn.itcast.web.servlet;
import cn.itcast.domain.PageBean;
import cn.itcast.domain.User;
import cn.itcast.service.UserService;
import cn.itcast.service.impl.UserServiceImpl;
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 java.io.IOException;
import java.util.Map;
@WebServlet("/findUserByPageServlet")
public class FindUserByPageServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String currentPage = request.getParameter("currentPage");
String rows = request.getParameter("rows");
if(currentPage==null || currentPage.equals("")){
currentPage = "1";
}
if(rows==null || rows.equals("")){
rows="5";
}
//2.调用service查询
UserService service = new UserServiceImpl();
PageBean<User> pb = service.findUserByPage(currentPage,rows);
System.out.println(pb);
//3.将PageBean存入request
request.setAttribute("pb",pb);
//4.转发到list.jsp
request.getRequestDispatcher("/list.jsp").forward(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
第三步:也是后端中最重要的一步,就是通过service中的类处理出我们数据库代码可以直接使用的数据,然后调用数据库包中的相关函数来产生PageBean对象。
@Override
public PageBean<User> findUserByPage(String _currentPage, String _rows) {
int currentPage = Integer.parseInt(_currentPage);
int rows = Integer.parseInt(_rows);
PageBean<User> pb = new PageBean<User>();
pb.setCurrentPage(currentPage);
pb.setRows(rows);
//调用dao查询总记录数
int totalCount = dao.findTotalCount();
pb.setTotalCount(totalCount);
//调用查询每页上记录
int start = (currentPage-1)*rows;
List<User> list = dao.findByPage(start,rows);
pb.setList(list);
int totalPage = totalCount % rows == 0 ? totalCount/rows : totalCount/rows+1;
pb.setTotalPage(totalPage);
return pb;
}
第四步:写数据库查询函数
@Override
public int findTotalCount() {
String sql = "select count(*) from user";
return template.queryForObject(sql, Integer.class);
}
@Override
public List<User> findByPage(int start, int rows) {
String sql = "select * from user limit ? ,?";
List<User> list = template.query(sql, new BeanPropertyRowMapper<User>(User.class), start, rows);
return list;
}
第五步:写前端展示代码。主要就是涉及总页数、总结果数以及页数标签的显示和其对应的结果,以及前进后退标签。需要注意的是我们现在有了分页查询功能,前面的UserListServlet就没有存在的必要了,所以我们在这里最好将index.jsp中的跳转写成findUserByPageServlet。但是需要注意的是我们的当前页数和每页显示条数是通过GET方法传递给后端的,所以如果不传参的话会导致错误。所以我们上面写findUserByPageServlet时在上面加上了一个检测是否空的判断,如果是空,那就给他手动在程序中给定一个值。
<div>
<nav>
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li><a href="#">1</a></li>
<c:forEach begin="1" end="${pb.totalPage}" var="i" varStatus="s">
<li><a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${i}&rows=5">${i}</a></li></li>
</c:forEach>
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
<span style="font-size: 25px;margin-left: 5px;">
共${pb.totalCount}条记录,共${pb.totalPage}页
</span>
</ul>
</nav>
</div>
第六步,上下翻页的功能以及选中功能:
选中只需在遍历的时候判断是不是当前界面即可,上下翻页把currentPage给一个${currentPage-1}。
<nav>
<ul class="pagination">
<c:if test="${pb.currentPage == 1}">
<li class="disabled">
</c:if>
<c:if test="${pb.currentPage != 1}">
<li>
</c:if>
<a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${pb.currentPage-1}&rows=5" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<c:forEach begin="1" end="${pb.totalPage}" var="i" varStatus="s">
<c:if test="${pb.currentPage==i}">
<li class="active">
</c:if>
<c:if test="${pb.currentPage!=i}">
<li>
</c:if>
<a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${i}&rows=5">${i}</a>
</li>
</c:forEach>
<c:if test="${pb.currentPage == pb.totalPage}">
<li class="disabled">
</c:if>
<c:if test="${pb.currentPage != pb.totalPage}">
<li>
</c:if>
<a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${pb.currentPage+1}&rows=5" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
<span style="font-size: 25px;margin-left: 5px;">
共${pb.totalCount}条记录,共${pb.totalPage}页
</span>
</ul>
</nav>
- 复杂条件查询
这里我们需要考虑的就只是后端的数据库程序的这一部分,即如何将我们输入表单用于查询的这些个属性给添加到我们的查询语句中,因为我们用于查询的三个条件并不是每个都必须填上的,有时候查询的条件只有一个有时则都有,所以我们采用动态的数据库查询语句,即将字符串转换为StringBuilder对象,然后再一点点进行拼接:
@Override
public int findTotalCount(Map<String,String[]> condition) {
String sql = "select count(*) from user where 1=1";
StringBuilder sb = new StringBuilder(sql);
//遍历map,获取所有的键
Set<String> key = condition.keySet();
//定义参数集合
List<Object> params = new ArrayList<>();
for (String s : key) {
if("currentPage".equals(s) || "rows".equals(s)){
continue;
}
String value = condition.get(s)[0];
if(value != null && !"".equals(value)){
sb.append(" and "+s+" like ? ");
params.add("%"+value+"%");
}
}
System.out.println(sb.toString());
System.out.println(params);
return template.queryForObject(sb.toString(),Integer.class,params.toArray());
}
@Override
public List<User> findByPage(int start, int rows,Map<String,String[]> condition) {
String sql = "select * from user where 1=1";
StringBuilder sb = new StringBuilder(sql);
Set<String> keySet = condition.keySet();
List<Object> params = new ArrayList<>();
for (String key : keySet) {
if("currentPage".equals(key) || "rows".equals(key)){
continue;
}
String value = condition.get(key)[0];
if(value != null && !"".equals(value)){
sb.append(" and "+key+" like? ");
params.add("%"+value+"%");
}
}
sb.append(" limit ?,?");
params.add(start);
params.add(rows);
List<User> list = template.query(sb.toString(), new BeanPropertyRowMapper<User>(User.class), params.toArray());
return list;
}
在这之后我们主要解决两个细节问题,一个是信息的回显,一个翻页时仍然要带着查询条件。
回显很简单,在这里就不再说了
翻页时可以直接在后面跟着查询条件
href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${pb.currentPage+1}&rows=5&name=${condition.name[0]}&address=${condition.address[0]}&email=${condition.email[0]}"