目录
用户信息列表展示案例
1. 需求:
1. 简单功能
1. 列表查询 2. 登录 3. 添加 4. 删除 5. 修改
2. 复杂功能
1. 删除选中 2. 分页查询 * 好处: 1. 减轻服务器内存的开销 2. 提升用户体验 3. 复杂条件查询
2. 设计:
1. 技术选型:
3. 开发:
1. 环境搭建
1. 创建数据库环境 2. 创建项目,导入需要的jar包
细节:
1.添加jar包要注意版本是否适配
2.配置文件要根据实际情况进行修改编辑
3.注意查询语句是否正确
4、据要写在forEach内编辑
2. 编码
4. 测试
进阶:
关于登录界面login.jsp
添加联系人功能
删除功能
修改功能
复杂功能
删除选中
分页查询
编辑
复杂功能
复杂条件查询功能
动态查询小技巧:
用户信息列表展示案例
1. 需求:
1. 简单功能
1. 列表查询
2. 登录
3. 添加
4. 删除
5. 修改
2. 复杂功能
1. 删除选中
2. 分页查询
* 好处:
1. 减轻服务器内存的开销
2. 提升用户体验
3. 复杂条件查询
2. 设计:
1. 技术选型:
Servlet+JSP+MySQL+JDBCTempleat+Duird+BeanUtils+tomcat
控制器 + 页面展示+ 操作数据库+ 自动封装 + 连接池提高连接效率+ 封装数据 + 部署到服务器上
2. 数据库设计:
create database test01; -- 创建数据库
use test01; -- 使用数据库
CREATE TABLE user2( -- 创建表
id int PRIMARY KEY auto_increment,
name VARCHAR(20) not null,
gender VARCHAR(5),
age INT,
address VARCHAR(32),
qq VARCHAR(20),
email VARCHAR(50)
);
3. 开发:
1. 环境搭建
1. 创建数据库环境
2. 创建项目,导入需要的jar包
细节:
1.添加jar包要注意版本是否适配
2.配置文件要根据实际情况进行修改
3.注意查询语句是否正确
4、据要写在forEach内
2. 编码
4. 测试
在/test02/userListServlet下可以输出,但是list.jsp下没有打印出来
试着打印UserListServlet,
response.getWriter();
输出无法识别中文,句首加入
//简单的形式,设置编码,是在获取流之前设置 response.setContentType("text/html;charset=utf-8"); 即可。
最后终于得出错误原因,不是代码错误也不是配置错愕,是进入的网址错误,得从index.jsp进入首页
成了!
接着添加增删改操作
先在UserService接口添加对应接口
添加数据,报错
org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [insert into user2 set name=?,gender=?,age=?,adress=?,qq=?,email=?]; nested exception is java.sql.SQLSyntaxErrorException: Unknown column 'adress' in 'field list'
查了半天是拼写错误
一个好消息,一个坏消息
好消息:写进去了
坏消息:但没完全写进去
出现中文乱码且丢失
//设置request编码 request.setCharacterEncoding("utf-8");
会发现性别和籍贯因为是有默认选择的复选框,不是手动输入的数据,所以未被存储进去,需要在UserAddServlet里添加存储进去,这样选择框里的对象也能获取到
UserAddServlet代码:
package cn.web.servlet;
import cn.domain.User;
import cn.service.UserService;
import cn.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;
/**
* @author 乱码酱
* @date :2022-11-29 15:44
* @program: HTMLStudy
* @create:
*/
@WebServlet("/userAddServlet")
public class UserAddServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置request编码
request.setCharacterEncoding("utf-8");
//调用UserService完成添加
UserService service = new UserServiceImpl();
//获取参数
String name = request.getParameter("name");
String gender = request.getParameter("gender");
int age = Integer.parseInt(request.getParameter("age"));
String adress = request.getParameter("address");
String qq = request.getParameter("qq");
String email = request.getParameter("email");
//添加到User里
User user = new User();
user.setName(name);
user.setGender(gender);
user.setAge(age);
user.setAddress(adress);
user.setQq(qq);
user.setEmail(email);
// 调用UserService层
User user1 = service.addUser(user);
// 将list存入request域
request.setAttribute("user1",user);
//转发到
request.getRequestDispatcher("/index.jsp").forward(request,response);
3.调转到重新查询的servlet
// response.sendRedirect(request.getContextPath()+"/userListServlet");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
进阶:
将已有用户信息列表改进为后面一种格式
使用Bootstrap上的内联表单样式,但是出现了分层
解决方法:在div标签的style样式加一个float浮动即可
<div style="float: left">
至于分页,在Bootstrap的中文档首页“组件”复制分页即可。
关于登录界面login.jsp
验证码执行一个refreshCode();方法
添加登录所需字段username和password
可以在数据库查询是否添加成功
SELECT * FROM user2;
添加好内容
数据库添加字段,对应实体类User也要跟着修改
先进入登录界面login.jsp,登录成功会提示
添加联系人功能
在list.jsp中添加联系人按钮路径跳转到add.jsp
add.jsp也要连接指定servlet
之前的addservlet没用util工具类,需要一个一个添加,
@WebServlet("/addUserServlet")
public class AddUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置request编码
request.setCharacterEncoding("utf-8");
//调用UserService完成添加
UserService service = new UserServiceImpl();
//获取参数
String name = request.getParameter("name");
String gender = request.getParameter("gender");
int age = Integer.parseInt(request.getParameter("age"));
String adress = request.getParameter("address");
String qq = request.getParameter("qq");
String email = request.getParameter("email");
//添加到User里
User user = new User();
user.setName(name);
user.setGender(gender);
user.setAge(age);
user.setAddress(adress);
user.setQq(qq);
user.setEmail(email);
// 调用UserService层
User user1 = service.addUser(user);
// 将list存入request域
request.setAttribute("user1",user);
//转发到
request.getRequestDispatcher("/index.jsp").forward(request,response);
3.调转到重新查询的servlet
// response.sendRedirect(request.getContextPath()+"/userListServlet");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
后来加入了工具类,效率提高
//1.设置编码
request.setCharacterEncoding("utf-8");
//2.获取参数
Map<String, String[]> map = request.getParameterMap();
//3.封装对象
User user = new User();
try {
BeanUtils.populate(user,map);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//4.调用service方法报错
UserService service = new UserServiceImpl();
service.addUser(user);
//5.跳转到/userListServlet
// 没有共享数据使用重定向 加上虚拟路径request.getContextPath()
response.sendRedirect(request.getContextPath()+"/userListServlet");
UserDaoImpl中的addUser方法也优化了
@Override
public User addUser(User user) {
try {
//第一版
//String sql = "insert into user2 set name = ?, gender = ?, age = ?, address = ?, qq= ?, email = ?";
//第二版
String sql ="insert into user2 values(null, ?, ?, ?, ?, ?, ?,null, null )";
template.update(sql, user.getName(), user.getGender(), user.getAge(), user.getAddress(), user.getQq(), user.getEmail());
} catch (DataAccessException e) {
e.printStackTrace();
}
return user;
}
添加用户成功!
删除功能
在list.jsp删除按钮添加路径
编辑DelUserService
原版本:
//1.设置request编码 request.setCharacterEncoding("utf-8"); int id = Integer.parseInt(request.getParameter("id")); //2.调用service UserService service = new UserServiceImpl(); service.deleteUser(id); //3.调转到重新查询的servlet response.sendRedirect(request.getContextPath()+"/userListServlet"); 3.转发到 // request.getRequestDispatcher("/index.jsp").forward(request,response);
现版本:
// 因为是根据id删除,不涉及汉字,可以不用设置编码 // 1.获取id对象 String id = request.getParameter("id"); // 2.调用service删除 UserService service = new UserServiceImpl(); service.deleteUser(id); //3.跳转查询所有servle response.sendRedirect(request.getContextPath()+"/userListServlet");
测试:
轻易就删除了记录,但是不排除误删的可能性,想加入一个确认删除的提示框
在list.jsp删除按钮添加一个方法,点击会有确认框
放到上面去获取不到id,于是传参
测试一下:
修改功能
修改update.jsp指向路径并在<input>标签添加value,编写对应service、dao层接口和实现类方法
测试时出现404错误
原因是重新建了一个findUserServlet,需要重新启动服务器,启动即可成功访问
之前的修改性别和地址不知道默认值,所以要判断选择框里的性别和籍贯,需要在update.jsp开头加上标签引入
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
加入判断:
在<select>中selected是默认值
有几个就做几次判断(默默删除几个选项减少工作量)
用户修改都是基于id,但是id是数据库生成的,在表单中没有体现,所以建一个隐藏域,将id创建在里面获取
<%--隐藏域提交id--%> <input type="hidden" name="id" value="${user.id}">
代码编写好 测试一下
报了一个500的错
原来是没重启的锅,重启后恢复正常
但是新的问题出现了,无法修改内容,除去一部分原因是我在数据库写入数据,里面的地址不在<select>选框中无法显示
于是试了其他的也没能成功修改,查找原因发现是sql语句缺了逗号,离谱!
成了!
复杂功能
删除选中
获取选中条目id的方法:
在<table>外加一个<form>表单
【更正】一下,连接的是/delSelectedServlet(删除选中)不是/delUserServlet(删除用户)
【更正】复选框的name和value也写错位置了
写对应方法
全选和全不选的实现
选中后的提示(防止误删)
测试:
全选然后提示框弹出后取消删除会跳转404页面
原因:在删除选中的超链接写了一个script方法,但是冒号是中文,识别不到方法
改为英文就能识别了!
分页查询
要实现的功能及数据
在三层架构的调用
细节:
//4.调用dao查询List集合 //计算开始的记录索引 0-5 5-10 10-15 ... (含前不含后) int start = (currentPage - 1) * rows; List<User> list = dao.findByPage(start, rows); //5.计算总页码 // 分情况,看能不能整除,例如一页5条记录,15条记录就是显示15/5=3页,17条记录就是显示17/5=3...2,所以是(17/5)+1=4页, int totalPage = (totalCount % rows) == 0 ? (totalCount / rows) : (totalCount / rows) + 1; pb.setTotalPage(totalPage);
UserServiceImpl:
@Override //分页查询
public PageBean<User> findUserByPage(String _currentPage, String _rows, Map<String, String[]> condition) {
//将String类型的参数封装成Integer
int currentPage = Integer.parseInt(_currentPage);
int rows = Integer.parseInt(_rows);
//1.创建空的PageBean对象
PageBean<User> pb = new PageBean<User>();
//2.设置参数
pb.setCurrentPage(currentPage);
pb.setRows(rows);
//3.调用dao查询总记录数
int totalCount = dao.findTotalCount();
pb.setTotalCount(totalCount);
//4.调用dao查询List集合
//计算开始的记录索引 0-5 5-10 10-15 ... (含前不含后)
int start = (currentPage - 1) * rows;
List<User> list = dao.findByPage(start, rows);
pb.setList(list);
//5.计算总页码
// 分情况,看能不能整除,例如一页5条记录,15条记录就是显示15/5=3页,17条记录就是显示17/5=3...2,所以是(17/5)+1=4页,
int totalPage = (totalCount % rows) == 0 ? (totalCount / rows) : (totalCount / rows) + 1;
pb.setTotalPage(totalPage);
return pb;
}
测试异常
http://localhost/test02/findUserByPageServlet?currentPage=1&rows=5
重启后正常了
继续编写前台代码
替换成真正的数据
分页栏和实际数据保持一致
<%--分页栏设计--%>
<c:forEach begin="1" end="${pb.totalPage}" var="i">
<li>
<a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${i}&rows=5">${i}</a>
</li>
</c:forEach>
分页栏的激活与禁用&添加判断,确保分页栏和当前界面地址栏页码一致
<%--分页栏设计--%> <c:forEach begin="1" end="${pb.totalPage}" var="i"> <%--添加判断,确保分页栏和当前界面地址栏页码一致--%> <c:if test="${pb.currentPage == i}"> <%--li标签里class="active"确保分页栏激活--%> <li class="active"><a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${i}&rows=5"></a> </li> </c:if> <c:if test="${pb.currentPage != i}"> <%--分页栏和当前界面地址栏页码不匹配,不激活--%> <li><a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${i}&rows=5">${i}</a></li> </c:if> </c:forEach>
分页栏的激活与禁用
确保分页栏和当前界面地址栏页码一致
优化上一页和下一页
在当前页码的基础上进行-1和+1操作
<li> <%--当前页码-1--%> <a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${pb.currentPage - 1}&rows=5" aria-label="Previous"> <span aria-hidden="true">«</span> </a> </li>
为了逻辑更严谨,当页码为第一页时无法返回上一页,为最后一页时无法进入下一页
如此就是样式设置成功啦!
虽然样式设置不让点,但是实际上还是可以点,这需要到后台编写代码解决
在后台UserServiceImpl 做判断,分页查询方法findUserByPage
//判断页码是否小于等于0,如果是,设为1
这样就能确保第一页的上一页无法点击,诸如此类方法还有很多,css样式里也能设置无法选择。。。
最后一页也是类似
list.jsp部分改动后代码:
<c:if test="${pb.currentPage == pb.totalPage}"> <%--为了逻辑更严谨,当页码为最后一页时无法进入下一页--%> <li class="disabled"> </c:if> <c:if test="${pb.currentPage != pb.totalPage}"> <%--当页码不为为最后一页时可以进入下一页--%> <li> </c:if> <%--当前页码+1--%> <a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${pb.currentPage + 1}&rows=5" aria-label="Next"> <span aria-hidden="true">»</span> </a>
UserServiceImpl部分改动代码:
测试出错
出错原因及解决方法:
设置参数存入应该在计算之后而不是之前,将存入部分代码移到方法最后即可解决
@Override //分页查询
public PageBean<User> findUserByPage(String _currentPage, String _rows, Map<String, String[]> condition) {
//将String类型的参数封装成Integer
int currentPage = Integer.parseInt(_currentPage);
int rows = Integer.parseInt(_rows);
//判断页码是否小于等于0,如果是,设为1
if (currentPage <= 0) {
currentPage = 1;
}
//1.创建空的PageBean对象
PageBean<User> pb = new PageBean<User>();
//3.调用dao查询总记录数
int totalCount = dao.findTotalCount();
pb.setTotalCount(totalCount);
//4.调用dao查询List集合
//计算开始的记录索引 0-5 5-10 10-15 ... (含前不含后)
int start = (currentPage - 1) * rows;
List<User> list = dao.findByPage(start, rows);
pb.setList(list);
//5.计算总页码
// 分情况,看能不能整除,例如一页5条记录,15条记录就是显示15/5=3页,17条记录就是显示17/5=3...2,所以是(17/5)+1=4页,
int totalPage = (totalCount % rows) == 0 ? (totalCount / rows) : (totalCount / rows) + 1;
pb.setTotalPage(totalPage);
//判断页码是否大于等于最后一页,如果是,设为最后一页
if (currentPage >= pb.getTotalPage()) {
currentPage = pb.getTotalPage();
}
//2.设置参数
pb.setCurrentPage(currentPage);
pb.setRows(rows);
return pb;
}
复杂功能
复杂条件查询功能
动态查询小技巧:
在原本查询语句
"select count(*) from user2"改为
"select count(*) from user2 where 1 = 1"
这样代码结果不变且可以在后面添加查询条件
sb.append("and key like ?")
在form表单添加路径和读取方法,跳转到分页查询servlet
FindUserByPageServlet添加条件查询参数
UserServiceImpl中findUserByPage分页查询方法添加条件查询参数
在数据库上查询到的:
控制台上查询到的
一条记录,对上了!
接下来完善UserDaoImpl分页查询每页记录方法findByPage()
改写sql语句复制之前我们写的findTotalCount()方法语句过去
一些注意的点:
@Override //分页查询每页记录 public List<User> findByPage(int start, int rows, Map<String, String[]> condition) { String sql = "select * from user2 where 1 = 1 "; //留空格拼接字符串 StringBuilder sb = new StringBuilder(sql); //字符串拼接 //2.遍历map Set<String> keySet = condition.keySet(); //定义参数的集合 List<Object> params = new ArrayList<Object>(); for (String key : keySet) { //排除分页条件参数 只要name、address...参数查询 if("currentPage".equals(key) || "rows".equals(key)){ continue;//结束当前循环进入下一循环 } //获取value 如果最后不加[0]限定,出来的结果是一个values,所以要加上限定 String value = condition.get(key)[0]; //只获取一个值 System.out.println(value); //判断value是否有值 if(value != null && !"".equals(value)){ //有值 sb.append(" and "+key+" like ? ");//注意加空格(与前面查询语句拼接) 一个问号对应一个值 params.add("%"+value+"%");//?条件的值 加% %类似 like "%李%" 模糊查询 } } //添加分页查询 记得空格 sb.append(" limit ? , ? "); //添加分页查询参数值 params.add(start); params.add(rows); sql = sb.toString(); //不能在调用直接写template.query(sql,... ,start,rows) sql此时变成了sb.toString(), start,rows变成了params.toArray() //但是很多时候会忘记template.query(),干脆将sql = sb.toString(); //测试 System.out.println(sql); System.out.println(params); // return template.query(sql,new BeanPropertyRowMapper<User>(User.class),start,rows); return template.query(sql,new BeanPropertyRowMapper<User>(User.class),params.toArray()); }
打印测试:
打印后查询记录不见了,只要在map里存入即可
结果:
新的问题:跳转页码后查询条件消失
解决方法:
在每个分页地址后面拼接查询条件字符串
&name=${condition.name[0]}&address=${condition.address[0]}&email=${condition.email[0]}
这样即便跨页也能保存查询条件
以上就是全部的流程,有很多细节和操作值得注意!
累了,喝茶~