基于Jsp和MySQL实现的个人记事备忘系统
1.项目简介
1.1 功能模块
云R记软件就是用于记录日常生活点滴。一款跨平台的简单快速的个人记事备忘工具,并且能够实现PC、移动设备和云端之间的信息同步,简洁高效。将会议记录、日程安排、生活备忘,奇思妙想、快乐趣事以及任何突发灵感都可快速记录到系统中。
本系统采用 B/S 架构,使用 BootStrap + Jsp + Servlet + MySQL+ Tomcat 开发,使用 Maven 构建,采用 Junit 单元测试、Log4j 搭建日志、使用 POI 导入导出报表,操作 DB 使用大名鼎鼎的 DBUtil,同时 V2 版本使用 MongoDB 数据库,底层使用 Redis 做缓存,采用 Git 进行分布式版本控制开发。
本项目包含用户模块、类型模块、云记模块、数据报表、首页模块这几个核心模块,核心主旨是掌握功能的分析以及前后台数据交互。
1.2 技术选型
-
JSP
-
Servlet
-
Jquery及插件
-
Ueditor
-
log4j
-
Junit
-
增删改查
-
免登录:Session 和 Cookie
-
非法访问:过滤器 Filter
-
文件上传
-
分页
-
Ajax:Dom操作
-
富文本编辑器
1.3 开发环境
-
IDEA
-
Maven
-
MySQL
-
Jdk1.8
-
Tomcat
2.数据库设计
2.1 表结构
根据系统架构设计,本系统采用简单灵巧的 MySQL 数据库,遵循关系型数据库标准,符合三范式设计,一切从简,相关表如下。
用户表
云记表
类型表
2.2 E-R图
3.开发环境搭建
3.1 修改配置文件
```xml
<!-- jstl依赖 --><dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
```
3.2 数据库连接
配置文件 在 src/main/resources 目录下,新建 db.properties 文件
```python
连接MYSQL数据库的配置文件 注:等号的前后不要写空格
驱动名
jdbcName=com.mysql.cj.jdbc.Driver
数据库连接 ( db_lezijie_note是数据库的名称)
dbUrl=jdbc:mysql://localhost:3306/db_lezijie_note?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
数据库的连接账号 (账号基本上都是root)
dbName=root
数据库的连接密码 (每个人的数据库密码可能不一致,需要修改)
dbPwd=root1234 ```
3.3 编写 DBUtil
java
/**
* 数据库的工具类
* 1、得到数据库连接
* 2、关闭资源
*
*/
public class DBUtil {
// 定义配置对象
private static Properties properties = new Properties();
static {
try {
// 加载配置文件(输入流)
InputStream in =
DBUtil.class.getClassLoader().getResourceAsStream("db.properties");
// 通过load()方法将输入流加载到配置文件对象中
properties.load(in);
// 通过配置对象的getProperty()方法得到驱动名,加载驱动
Class.forName(properties.getProperty("jdbcName"));
} catch (Exception e) {
e.printStackTrace(); // 打印异常
}
}
/**
* 得到数据库连接
*
* @return
*/
public static Connection getConnection() {
Connection connection = null;
try {
// 得到数据库连接信息
String dbUrl = properties.getProperty("dbUrl");
String dbName = properties.getProperty("dbName");
String dbPwd = properties.getProperty("dbPwd");
// 得到数据库连接
connection = DriverManager.getConnection(dbUrl, dbName, dbPwd);
} catch (Exception e) {
e.printStackTrace();
}
return connection;
}
/**
* 关闭资源
* 当资源对象不为空时,关闭资源
*
* @param resultSet
* @param preparedStatement
* @param connection
*/
public static void close(ResultSet resultSet, PreparedStatement
preparedStatement, Connection connection) {
try {
if (resultSet != null) {
resultSet.close(); // 关闭资源
}
if (preparedStatement != null) {
preparedStatement.close(); // 关闭资源
}
if (connection != null) {
connection.close(); // 关闭资源
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.4 编写日志配置文件
在 src/main/resources 目录下,新建 log4j.properties 文件
```python
设置###stdout,
log4j.rootLogger = all,D,E,stdout
输出信息到控制台
log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
输出DEBUG 级别以上的日志到=D://logs/log.log
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender log4j.appender.D.File = D://logs/log.log log4j.appender.D.Append = true log4j.appender.D.Threshold = DEBUG log4j.appender.D.layout = org.apache.log4j.PatternLayout log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
输出ERROR 级别以上的日志到=D://logs/error.log
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender log4j.appender.E.File =D://logs/error.log log4j.appender.E.Append = true log4j.appender.E.Threshold = ERROR log4j.appender.E.layout = org.apache.log4j.PatternLayout log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] ```
4.项目实现
4.1 用户登录
4.1.1 用户模块
通过用户行为来区分 | actionName |
---|---|
用户登录 | actionName="login" |
进入个人中心 | actionName="userCenter" |
加载头像 | actionName="userHead" |
验证昵称的唯一性 | actionName="checkNick" |
修改用户信息 | actionName="updateUser" |
用户退出 | actionName="logout" |
在UserServlet的service方法中:
-
获取用户行为
java String actionName = request.getParameter("actionName");
-
判断用户行为,调用对应的方法
java if ("login".equals(actionName)) { // 用户登录 } else if ("userCenter".equals(actionName)) { // 进入个人中心 }
4.1.2 前端实现
-
添加表单,设置表单属性
html action="user" method="post" id="loginForm"
-
设置表单元素的那么属性值,修改id属性值(首字母改小写)
- 设置文本框和密码框的那么属性值
- 设置复选框的那么属性值和value属性值(如果复选框未选中,在获取不到value属性值)
-
设置隐藏域,传递用户行为 (name="actionName" value="login")
-
在 config.js 文件中,添加按钮的点击事件绑定的函数 checkLogin()
- 获取表单元素的值(姓名、密码)
-
判断元素的值是否为空(引入util.js文件)
- 如果为空,设置提示信息 (通过设置span标签),并return
- 如果不为空,则提交表单
4.1.3 后端实现
-
获取参数 (姓名、密码)
-
参数的非空校验 (ResultInfo的封装类,用来封装响应结果 状态码、提示信息、返回的对象)
-
如果为空
- 设置ResultInfo对象的状态码和提示信息
- 将ResultInfo对象设置request作用域中
- 请求转发跳转到登录页面
- 返回
-
如果不为空,通过用户名查询用户对象
- 判断用户对象是否为空
-
如果为空
- 设置ResultInfo对象的状态码和提示信息
- 将ResultInfo对象设置request作用域中
- 请求转发跳转到登录页面
- 返回
-
如果用户对象不为空,将数据库中查询到的用户对象的密码与前台传递的密码作比较 (将密码加密后再比较)
- 如果密码不正确
- 设置ResultInfo对象的状态码和提示信息
- 将ResultInfo对象设置request作用域中
- 请求转发跳转到登录页面
- 返回
- 如果密码正确
- 将用户信息存到session作用域中
-
判断用户是否选择记住密码(rem的值是1)
- 如果是,将用户姓名与密码存到cookie中,设置失效时间,并响应给客户端
- 如果否,清空原有的cookie对象
- 重定向跳转到index页面
4.1.4 分层思想
- Web层:(控制层:接收参数、响应数据)
- 获取参数 (姓名、密码)
- 调用Service层的方法,返回ResultInfo对象
-
判断是否登录成功
- 如果失败
- 将resultInfo对象设置到request作用域中
- 请求转发跳转到登录页面
- 如果成功
- 将用户信息设置到session作用域中
-
判断用户是否选择记住密码(rem的值是1)
- 如果是,将用户姓名与密码存到cookie中,设置失效时间,并响应给客户端
- 如果否,清空原有的cookie对象
- 重定向跳转到index页面
-
Service层:(业务逻辑层:参数判断、业务逻辑处理)
-
判断参数是否为空
- 如果为空
- 设置ResultInfo对象的状态码和提示信息
- 返回resultInfo对象
- 如果不为空,通过用户名查询用户对象
-
判断用户对象是否为空
- 如果为空
- 设置ResultInfo对象的状态码和提示信息
- 返回resultInfo对象
- 如果用户对象不为空,将数据库中查询到的用户对象的密码与前台传递的密码作比较 (将密码加密后再比较)
-
如果密码不正确
- 设置ResultInfo对象的状态码和提示信息
- 返回resultInfo对象
-
如果密码正确
- 设置ResultInfo对象的状态码和提示信息
- 返回resultInfo对象
-
Dao层:(数据访问层:数据库中的增删改查操作)
- 获取数据库连接
- 定义sql语句
- 预编译
- 设置参数
- 执行查询,返回结果集
- 判断并分析结果集
- 关闭资源
4.1.5 非法访问拦截和免登录
-
非法访问拦截
-
拦截的资源:
- 所有资源
-
需要被放行的资源:
- 指定页面,放行 (用户无需登录即可访问的页面;例如:登录页面login.jsp、注册页面register.jsp等)
- 静态资源,放行 (存放在statics目录下的资源;例如:js、css、images等)
- 指定行为,放行 (用户无需登录即可执行的操作;例如:登录操作user?actionName=login、注册操作等)
- 登录状态,放行 (判断session作用域中是否存在用户信息;存在就放行,不存在,就拦截跳转到登录页面)
-
免登录(自动登录) :通过Cookie和Session对象实现
-
什么时候使用免登录:
- 当用户处于未登录状态,且去请求需要登录才能访问的资源时,调用自动登录功能
-
目的
- 让用户处于登录状态(自动调用登录方法)
-
实现:从Cookie对象中获取用户的姓名与密码,自动执行登录操作
- 获取cookie数组
- 判断cookie数组
- 遍历cookie数组,获取指定的cookie对象(name为user的cookie对象)
- 得到指定cookie对象的value(姓名与密码:userName-userPwd)
- 通过split()方法将value字符串转换成数组
- 从数组中别得到姓名和密码
- 请求转发跳转到登录操作 user?actionName=login&userName=姓名&userPwd=密码
- 返回
4.1.6 用户退出
- 前端
-
设置超链接的请求地址 user?actionName=logout
-
后台
- 销毁Session对象
- 删除Cookie对象
- 重定向跳转到登录页面
4.1.7 进入个人中心
- 前台
- 设置超链接的访问地址 user?actionName=userCenter
-
通过el表达式获取session作用域中的用户信息
-
后台
- 设置首页动态包含的页面值
- 请求转发跳转到index.jsp
4.1.8 加载头像
- 前台
- 设置img标签的src属性,请求后台加载头像
-
src="user?actionName=userHead&imageName=图片名称" (通过el表达式熊session中获取)
-
后台
- 获取参数 (图片名称)
- 得到图片的存放路径 (request.getServletContext().getealPathR("/"))
- 通过图片的完整路径,得到file对象
- 通过截取,得到图片的后缀
- 通过不同的图片后缀,设置不同的响应的类型
- 利用FileUtils的copyFile()方法,将图片拷贝给浏览器
4.1.9 验证昵称的唯一性
- 前台
-
昵称文本框的失焦事件 blur
- 获取昵称文本框的值
- 判断值是否为空
- 如果为空,提示用户,禁用按钮,并return
- 判断昵称是否做了修改,从session作用域中获取用户昵称 (如果在js中想要使用el表达式获取域对象,js需要写在JSP页面中,无法在js文件中获取)
- 如果用户昵称与session中的昵称一致,则return
-
如果昵称做了修改
- 发送ajax请求后台,验证昵称是否可用
- 如果不可用,提示用户,并禁用按钮
- 如果可用,清空提示信息,按钮可用
-
昵称文本框的聚焦事件 focus
- 清空提示信息
- 按钮可用
-
后台
-
Web层
- 获取参数(昵称)
- 从session作用域获取用户对象,得到用户ID
- 调用Service层的方法,得到返回的结果
- 通过字符输出流将结果响应给前台的ajax的回调函数
- 关闭资源
-
Service层:
- 判断昵称是否为空
- 如果为空,返回"0"
- 调用Dao层,通过用户ID和昵称查询用户对象
- 判断用户对象存在
- 存在,返回"0"
- 不存在,返回"1"
-
Dao层:
- 定义SQL语句,通过用户ID查询除了当前登录用户之外是否有其他用户使用了该昵称
- 指定昵称 nick (前台传递的参数)
- 当前用户 userId (session作用域中的user对象)
-
String sql = "select * from tb_user where nick = ? and userId != ?";
- 设置参数集合
- 调用BaseDao的查询方法
4.1.10 修改用户信息
- 前台
-
文件上传表单
- 表单
- 表单类型:enctype="multipart/form-data"
- 提交方式:method="post"
- 表单元素
- 设置name属性值(昵称、心情、头像)
- 设置隐藏域存放用户行为actionName
-
后台
-
Web层:注:文件上传必须在Servlet类上提那家注解!!! @MultipartConfig
- 调用Service层的方法,传递request对象作为参数,返回resultInfo对象
- 将resultInfo对象存到request作用域中
- 请求转发跳转到个人中心页面 (user?actionName=userCenter)
-
Service层:
- 获取参数(昵称、心情)
- 参数的非空校验(判断必填参数非空)
- 如果昵称为空,将状态码和错误信息设置resultInfo对象中,返回resultInfo对象
- 从session作用域中获取用户对象(获取用户对象中默认的头像)
- 实现上上传文件
- 获取Part对象 request.getPart("name"); name代表的是file文件域的name属性值
- 通过Part对象获取上传文件的文件名
- 判断文件名是否为空
- 获取文件存放的路径 WEB-INF/upload/目录中
- 上传文件到指定目录
- 更新用户头像 (将原本用户对象中的默认头像设置为上传的文件名)
- 调用Dao层的更新方法,返回受影响的行数
- 判断受影响的行数
- 如果大于0,则修改成功;否则修改失败
- 返回resultInfo对象
-
Dao层:
- 通过用户ID修改用户信息
-
定义SQL语句:
String sql = "update tb_user set nick = ?, mood = ?, head = ? where userId = ? ";
- 设置参数集合
- 调用BaseDao的更新方法,返回受影响的行数
- 返回受影响的行数
4.1.11 乱码处理
乱码原因 :服务器默认的解析编码为ISO-8859-1,不支持中文。
乱码情况 : - POST请求 - Tomcat7及以下版本:乱码 - Tomcat8及以上版本:乱码 - GET请求 - Tomcat7及以下版本:乱码 - Tomcat8及以上版本:不乱码
解决方案
:
- POST请求
- 无论是什么版本的服务器,都会出现乱码
- 需要通过request.setCharacterEncoding("UTF-8")设置编码格式(只针对POST请求有效)
- GET请求
- Tomcat8及以上版本,不会乱码,不需要处理
- Tomcat7及以下版本,需要单独处理
-
new String(request.getParameter("xxx").getBytes("ISO-8859- 1"),"UTF-8");
4.2 云记模块功能
4.2.1 发布云记
- 前台
-
表单提交
- 设置表单的提交方式和提交地址
- 在隐藏域中设置用户行为actionName
- 设置表单元素的id与name属性值
-
表单校验
- 获取表单元素的值
- 获取下拉框选中的选项 .val()
- 获取文本框的值 .val()
- 获取富文本编辑器的内容
- ue.getContent():获取富文本编辑器的内容(包含html标签)
- ue.getContentTxt():获取富文本编辑器的纯文本内容(不包含html标签)
- 参数的非空判断
- 如果为空,提示用户,并return fasle
- 如果参数不为空,则return true,提交表单
-
后台
-
Web层:
- 接收参数 (类型ID、标题、内容)
- 调用Service层方法,返回resultInfo对象
- 判断resultInfo的code值
- 如果code=1,表示成功,重定向跳转到首页 index
-
如果code=0,表示失败
- 将resultInfo对象设置到request作用域
- 请求转发跳转到note?actionName=view
-
Service层:
- 设置回显对象 Note对象
- 参数的非空判断
- 如果为空,code=0,msg=xxx,result=note对象,返回resultInfo对象
- 调用Dao层,添加云记记录,返回受影响的行数
- 判断受影响的行数
- 如果大于0,code=1
- 如果不大于0,code=0,msg=xxx,result=note对象
- 返回resultInfo对象
-
Dao层:
- 添加云记,返回受影响的行数
-
加载富文本编辑器
- 下载富文本编辑器的压缩包(UTF-8),解压压缩包
- 将解压后的目录中对应的utf8-jsp文件夹,拷贝到项目中的statics目录下
- 修改utf8-jsp文件夹的名称,设置为ueditor
- 在 pom.xml 中添加依赖(在WEB-INF目录下,新建lib目录,将json与ueditor的jar包拷贝进去 )
4.2.2 查询云记详情
- Web层
- 接收参数 (noteId)
- 调用Service层的查询方法,返回Note对象
- 将Note对象设置到request请求域中
- 设置首页动态包含的页面值
-
请求转发跳转到index.jsp
-
Service层
- 参数的非空判断
- 调用Dao层的查询,通过noteId查询note对象
-
返回note对象
-
Dao层
- 通过noteId查询note对象
4.2.3 删除日记
- 前台 :"删除"按钮绑定点击事件
- 弹出提示框询问用户是否确认删除
-
如果确认删除,则发送ajax请求
- 如果删除失败,提示用户
- 如果删除成功,跳转到首页
-
后台
-
Web层:
- 接收参数 (noteId)
- 调用Service层删除方法,返回状态码 (1=成功,0=失败)
- 通过流将结果响应给ajax的回调函数 (输出字符串)
-
Service层:
- 判断参数
- 调用Dao层的更新方法,返回受影响的行数
- 判断受影响的行数是否大于0
- 如果大于0,返回1
- 否则返回0
5.项目展示
登录页
首页
发布云记
分页查询列表
查询云记详情
删除云记
数据报表
用户分布图
6.总结
该项目是一个基于jsp+servlet实现的简单个人博客,实现了基本的增删查改操作,引入数据报表分析,用户分布图的技术,非常值得我们学习!!
参考文献
- 烟草公司人事管理系统的设计与实现(青岛大学·矫大鹏)
- 基于SSH框架的人力资源管理系统的设计与开发(电子科技大学·唐伟)
- 基于JSP的考试后台管理系统的设计与开发(曲阜师范大学·常洪光)
- 基于SSH框架的博客用户分享平台的设计与实现(河北工业大学·刘磊)
- 人力资源管理系统设计(山东大学·郑重)
- 基于LDAP与Web的银行业务培训系统的集成应用研究(天津大学·戴天)
- 基于SSH框架的人力资源管理系统的设计与开发(电子科技大学·唐伟)
- 基于SSH框架的人力资源管理系统的设计与开发(电子科技大学·唐伟)
- 基于SSH的人事管理系统的分析与设计(北方工业大学·戴青)
- 基于web的信息共享系统的设计与实现(华中科技大学·保罗)
- 部队干部管理系统的设计与实践(吉林大学·侯春山)
- 基于SSH框架的Web网站设计与实现(长春理工大学·冯学军)
- 烟草公司人事管理系统的设计与实现(青岛大学·矫大鹏)
- 基于J2EE的远程网络教育系统研究与实现(电子科技大学·陈南荪)
- 烟草公司人事管理系统的设计与实现(青岛大学·矫大鹏)
本文内容包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主题。发布者:代码港湾 ,原文地址:https://m.bishedaima.com/yuanma/35444.html