说明:
“用户信息查询”和后面的“综合小案例”是分开学的,只是写在了一个博客里。
“综合小案例”是基于“用户信息查询”来写的。但需求更多,也会相应的调整页面之类的。
案例下载链接:javaweb综合小案例_用户信息的增删改查.zip
里面包含了已经写好的前端页面,以及相关的配置文件和jar包,如果有问题,请查看最终成果。
一、用户信息的查询
1. 需求:
用户信息的查询,并展示在前端,以列表的形式展示。
2. 设计:
- 技术选型:Servlet+JSP+MySQL+JDBCTempleat+Duird+BeanUtilS+tomcat
- 数据库设计:
注意,数据库的编码为utf8,连接的时候,也要注意设置连接的URL,这样可以避免中文乱码。
create database day17; -- 创建数据库
use day17; -- 使用数据库
create table user( -- 创建表
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. 环境搭建
- 创建数据库环境
- 创建项目,导入需要的jar包
(1)导入的jar包:
(2)最终的项目文件列表:
我用的IDEA。
2. 编码
(1)导入已经写好的前端页面,在下载的案例文件里有。
导入之后,先运行查看一下效果。
点击初始页面的“查询所有用户信息”的超链接后,跳转到用户信息列表,点击修改和添加均可跳转到相应页面。
导入之后,将导入的index.html页面里的内容全部复制粘贴到新建项目时自动生成的index.jsp默认首页中。注意要保留原先jsp文件里的第一行的指令,其他内容覆盖。
另外,将标签<a>
里的跳转地址更改为动态获取的地址:
<a href="${pageContext.request.contextPath}/userListServlet" style="text-decoration:none;font-size:33px">查询所有用户信息 </a>
(2)案例逻辑:
(3)在src/cn/itcast/domain包下写User类:
package cn.itcast.domain;
// 对照数据库表来写
public class User {
private int id;
private String name;
private String gender;
private int age;
private String address;
private String qq;
private String email;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getQq() {
return qq;
}
public void setQq(String qq) {
this.qq = qq;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
", address='" + address + '\'' +
", qq='" + qq + '\'' +
", email='" + email + '\'' +
'}';
}
}
(4)在src/cn/itcast/service包下写一个接口UserService:
package cn.itcast.service;
import cn.itcast.domain.User;
import java.util.List;
/*
* 用户管理的业务接口
* */
public interface UserService {
/*
* 查询所有用户列表
* */
public List<User> findAll();
}
接口写好之后,定义一个子包src/cn/itcast/service/impl,在这个包下定义一个类UserServiceImpl:
package cn.itcast.service.impl;
import cn.itcast.domain.User;
import cn.itcast.service.UserService;
import java.util.List;
public class UserServiceImpl implements UserService {
@Override
public List<User> findAll() {
// 调用Dao完成查询
return null;
}
}
这个类需要调用Dao,此时去写Dao
(5)在src/cn/itcast/dao下写一个接口UserDao
package cn.itcast.dao;
import cn.itcast.domain.User;
import java.util.List;
/*
* 用户操作的DAO
* */
public interface UserDao {
public List<User> findAll();
}
在src/cn/itcast/dao下新建一个子包impl,新建一个类UserDaoImpl,这里先不做具体的操作。
package cn.itcast.dao.impl;
import cn.itcast.dao.UserDao;
import cn.itcast.domain.User;
import java.util.List;
public class UserDaoImpl implements UserDao{
@Override
public List<User> findAll() {
// 使用JDBC操作数据库;
return null;
}
}
先在UserServiceImpl中添加和修改如下:
package cn.itcast.service.impl;
import cn.itcast.dao.UserDao;
import cn.itcast.dao.impl.UserDaoImpl;
import cn.itcast.domain.User;
import cn.itcast.service.UserService;
import java.util.List;
public class UserServiceImpl implements UserService {
private UserDao dao = new UserDaoImpl();
@Override
public List<User> findAll() {
// 调用Dao完成查询
return dao.findAll();
}
}
(6)在Servlet中完成逻辑,在UserListServlet中添加:
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 {
// 1. 调用UserService完成查询
UserService service = new UserServiceImpl();
List<User> users = service.findAll();
// 2.将 list 存入request域
request.setAttribute("users",users);
// 3.转发到list.jsp
request.getRequestDispatcher("/list.jsp").forward(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
(7)现在需要补充dao的实现类。
先导入配置文件druid.properties连接池,注意修改数据库名字和密码。
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///day17?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
username=root
password=root
# 初始化连接数量
initialSize=5
# 最大连接数
maxActive=10
# 最大等待时间
maxWait=3000
写一个工具类JDBCUtils获取连接池对象等等,在javaweb用户登录案例博客的开发步骤第四点有代码,这里不再粘贴。
接下来,在dao的实现类里补充代码:
package cn.itcast.dao.impl;
import cn.itcast.dao.UserDao;
import cn.itcast.domain.User;
import cn.itcast.util.JDBCUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class UserDaoImpl implements UserDao{
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
@Override
public List<User> findAll() {
// 使用JDBC操作数据库;
//1. 定义sql
String sql = "select * from user";
List<User> users = template.query(sql,new BeanPropertyRowMapper<User>(User.class));
return users;
}
}
(8)然后还需要一个list.jsp页面
将写好的前端页面list.html复制粘贴为list.jsp。
引进tagdir的标签库,用foreach迭代,生成动态的数据。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<!-- 网页使用的语言 -->
<html lang="zh-CN">
<head>
<!-- 指定字符集 -->
<meta charset="utf-8">
<!-- 使用Edge最新的浏览器的渲染方式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- viewport视口:网页可以根据设置的宽度自动进行适配,在浏览器的内部虚拟一个容器,容器的宽度与设备的宽度相同。
width: 默认宽度与设备的宽度相同
initial-scale: 初始的缩放比,为1:1 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<title>用户信息管理系统</title>
<!-- 1. 导入CSS的全局样式 -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- 2. jQuery导入,建议使用1.9以上的版本 -->
<script src="js/jquery-2.1.0.min.js"></script>
<!-- 3. 导入bootstrap的js文件 -->
<script src="js/bootstrap.min.js"></script>
<style type="text/css">
td, th {
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<h3 style="text-align: center">用户信息列表</h3>
<table border="1" class="table table-bordered table-hover">
<tr class="success">
<th>编号</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>籍贯</th>
<th>QQ</th>
<th>邮箱</th>
<th>操作</th>
</tr>
<c:forEach items="${users}" var="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>
<td colspan="8" align="center"><a class="btn btn-primary" href="add.html">添加联系人</a></td>
</tr>
</table>
</div>
</body>
</html>
4. 测试
5. 部署运维
二、综合小案例
这个部分,因为每个需求实现的时候,需要修改或者添加的代码都有些多,大部分都没有贴出来。可以在项目文件里查看最终写好的小案例。
1.需求
-
简单功能
- 列表查询
- 登录
- 添加
- 删除
- 修改
-
复杂功能
- 删除选中
- 分页查询
- 好处:
- 减轻服务器内存的开销
- 提升用户体验
- 复杂条件查询
- 好处:
-
登录
- 调整页面,加入验证码功能
- 代码实现
2. 实现
1. 修改list.jsp页面
前端用了框架Bootstrap,查看文档后,修改成自己想要的样式内容,并且加上分页。
改后的效果图:
list.jsp代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<!-- 网页使用的语言 -->
<html lang="zh-CN">
<head>
<!-- 指定字符集 -->
<meta charset="utf-8">
<!-- 使用Edge最新的浏览器的渲染方式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- viewport视口:网页可以根据设置的宽度自动进行适配,在浏览器的内部虚拟一个容器,容器的宽度与设备的宽度相同。
width: 默认宽度与设备的宽度相同
initial-scale: 初始的缩放比,为1:1 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<title>用户信息管理系统</title>
<!-- 1. 导入CSS的全局样式 -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- 2. jQuery导入,建议使用1.9以上的版本 -->
<script src="js/jquery-2.1.0.min.js"></script>
<!-- 3. 导入bootstrap的js文件 -->
<script src="js/bootstrap.min.js"></script>
<style type="text/css">
td, th {
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<h3 style="text-align: center">用户信息列表</h3>
<div style="float: left;">
<form class="form-inline">
<div class="form-group">
<label for="exampleInputName2">姓名</label>
<input type="text" class="form-control" id="exampleInputName2" >
</div>
<div class="form-group">
<label for="exampleInputName3">籍贯</label>
<input type="text" class="form-control" id="exampleInputName3" >
</div>
<div class="form-group">
<label for="exampleInputEmail2">邮箱</label>
<input type="email" class="form-control" id="exampleInputEmail2" >
</div>
<button type="submit" class="btn btn-default">查询</button>
</form>
</div>
<div style="float:right; margin: 5px;">
<a class="btn btn-primary" href="add.html">添加联系人</a>
<a class="btn btn-primary" href="add.html">删除选中</a>
</div>
<table border="1" class="table table-bordered table-hover">
<tr class="success">
<th><input type="checkbox"></th>
<th>编号</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>籍贯</th>
<th>QQ</th>
<th>邮箱</th>
<th>操作</th>
</tr>
<c:forEach items="${users}" var="user" varStatus="s">
<tr>
<th><input type="checkbox"></th>
<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="${pageContext.request.contextPath}/userListServlet">删除</a>
</td>
</tr>
</c:forEach>
</table>
<div>
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
<span style="font-size: 25px;margin-left: 5px;">
共16条记录,共4页
</span>
</ul>
</nav>
</div>
</div>
</body>
</html>
3.登录功能
(1)页面:修改login.jsp页面,代码部分不再粘贴。可以在项目里查看。
(2)验证码:在登录页面,验证码部分的图片指向checkCodeServlet,需要在web/servlet下写一个Servlet类。关于自己写验证码的内容,可以参考博客Javaweb自动生成验证码图片。
(3)完成登录功能。
需要先改造数据库里的表,因为原先没有username和password的字段。记得更改实体类User。
再写一个LoginServlet,同时需要在dao层加上查询密码和账号的方法。
4. 添加功能
(1)实现逻辑:
(2)当前的数据库设计不合理,因为一张user表既实现了展示用户信息的功能,又实现了登录功能。
以后应该分两张表。
(3)在添加页面,可以再做一个表单校验。
5. 删除功能
(1)实现逻辑:
(2)修改list.jsp页面里,修改对应的超链接,并添加删除前的确认认证。
把相应的代码完成。
6. 修改功能
(1)实现逻辑:
(2)完成相应的代码。
7. 删除选中
(1)实现逻辑:
(2)完成代码。
要完成全选全不选的操作。
8. 分页查询
(1)实现逻辑:
分析:
(2)完成代码。
参考文档Bootstrap文档的组件里的分页。有禁用和激活状态。
在domain中新建一个分页对象的类PageBean。
补充逻辑图:
(3)继续完成代码。
9. 复杂条件分页查询
(1)实现逻辑:
(2)完成代码。
在分页查询的基础上完成。
记得把前面的查询,修改删除等的访问路径,修改成复杂条件查询的路径。
三、添加过滤器
1. 登录验证
1. 需求:
- 访问“综合小案例”的资源。验证其是否登录。
- 如果登录了,则直接放行。
- 如果没有登录,则跳转到登录页面,提示"您尚未登录,请先登录"。
2. 登录验证分析
3. 实现
在web下创建一个子包Filter后,再创建一个Filter。
package cn.itcast.web.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter( "/*")
public class LoginFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 0.强制转换
HttpServletRequest request = (HttpServletRequest) req;
// 1.获取资源请求路径
String uri = request.getRequestURI();
// 2.判断是否包含登录相关资源路径,要注意排除掉 css/js/图片/验证码等资源
if(uri.contains("/login.jsp") || uri.contains("/loginServlet")|| uri.contains("/css/")|| uri.contains("/js/")| uri.contains("/fonts/")| uri.contains("/checkCodeServlet")){ //需要把css等相关资源也排除掉
//包含,用户想要登录,放行
chain.doFilter(req,resp);
}else {
//不包含,需要验证用户是否登录
// 3.从获取session 中获取user
Object user = request.getSession().getAttribute("user");
if(user != null){
// 登录了,放行
chain.doFilter(req,resp);
}else {
// 没有登录,跳转登录页面
request.setAttribute("login_msg","您尚未登录,请登录");
request.getRequestDispatcher("/login.jsp").forward(request,resp);
}
}
//chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
}
}
2. 过滤敏感词
1. 过滤敏感词需求
需求:
- 对“综合小案例”案例录入的数据进行敏感词汇过滤
- 敏感词汇参考《敏感词汇.txt》
- 如果是敏感词汇,替换为 ***
2. 分析:
分析:
- 对request对象进行增强。增强获取参数相关方法
- 放行。传递代理对象
增强对象的功能:
- 设计模式:一些通用的解决固定问题的方式
- 装饰模式
- 代理模式
- 概念:
- 真实对象:被代理的对象
- 代理对象:
- 代理模式:代理对象代理真实对象,达到增强真实对象功能的目的
- 实现方式:
- 静态代理:有一个类文件描述代理模式
- 动态代理:在内存中形成代理类
- 实现步骤:
- 代理对象和真实对象实现相同的接口
- 代理对象 = Proxy.newProxyInstance();
- 使用代理对象调用方法。
- 增强方法
- 增强方式:
- 增强参数列表
- 增强返回值类型
- 增强方法体执行逻辑
3.实现
将包含敏感词的txt文件,拷贝到项目文件下,注意编码方式要与项目的编码方式一致,否则会出现乱码。
代码部分没有写完。。。。
在web/fileter下新建一个Filter:
package cn.itcast.web.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
@WebFilter("/*")
public class SensitiveWordsFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 1.创建代理对象,增强 getRarameter方法
ServletRequest proxy_req = (ServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 增强 getParameter方法
// 判断是否是 getParameter方法
if(method.getName().equals("getParameter")){
// 增强返回值
// 获取返回值
String value =(String) method.invoke(req,args);
if(value != null){
for(String str :list){
if(value.contains(str)){ //包含敏感字符
value = value.replaceAll(str,"****");
}
}
}
return value;
}
// 下面这部分自己写。。。没写完。。。
// 判断方法名是否是 getRarameterMap方法
// 判断方法名是否是 getRarameterValue方法
return method.invoke(req,args);
}
});
// 放行
chain.doFilter(proxy_req, resp);
}
private List<String> list = new ArrayList<>(); // 存放敏感词汇的集合
public void init(FilterConfig config) throws ServletException {
try {
// 1.获取文件真实路径
ServletContext servletContext = config.getServletContext();
String realPath = servletContext.getRealPath("WEB-INF/classes/敏感词汇.txt");
// 2.读取文件
BufferedReader br = new BufferedReader(new FileReader(realPath));
// 3.将文件的每一行数据添加到list中
String line = null;
while ((line = br.readLine())!=null){
list.add(line);
}
br.close();
System.out.println("list: "+list);
} catch (Exception e) {
e.printStackTrace();
}
}
}