常用项目总结模板
2022-09-13 10:46:28 14 举报
AI智能生成
本人对云日记项目总结,包含经典项目总结和个人理解,希望对您有所帮助
作者其他创作
大纲/内容
需求分析
项目概述
云R记软件就是用于记录日常生活点滴。一款跨平台的简单快速的个人记事备忘工具,并且能够实现
PC、移动设备和云端之间的信息同步,简洁高效。将会议记录、日程安排、生活备忘,奇思妙想、快乐趣事以及任何突发灵感都可快速记录到系统中。
本系统采用 B/S 架构,使用 BootStrap + Jsp + Servlet + MySQL+ Tomcat 开发,使用 Maven 构建,
采用 Junit 单元测试、Log4j 搭建日志、使用 POI 导入导出报表,操作 DB 使用大名鼎鼎的 DBUtil,同时 V2 版本使用 MongoDB 数据库,底层使用 Redis 做缓存,采用 Git 进行分布式版本控制开发。
本项目包含用户模块、类型模块、云记模块、数据报表、首页模块这几个核心模块,核心主旨是掌握功能的分析以及前后台数据交互。
系统需求分析
系统架构类图
系统详细设计
实现
总结与展望
技术点
主要技术点
Maven搭建,B/S 架构,使用 Mysql+Mybatis(或者JDBC)+web servlet+tomcat(或jetty)+html+css+javaScript+jQuery+BootStrap+jsp+jstl+el开发
其他技术点
Junit+log4j+hutool工具集+commons-io +百度编辑器ueditor支持+lombok插件
面试题
前端
ajax
详解
https://www.jianshu.com/p/5901fa544173
什么是 AJAX
什么是 ajax:
AJAX 是“Asynchronous JavaScript and XML”的缩写。他是指一种创建交互式网页应用的网页开发技术。Ajax可以实现动态不刷新(局部刷新),就是能在不更新整个页面的前提下维护数据。
这使得Web应用程序更为迅捷地回应用户动作,并避免了在网络上发送那些没有改变过的信息。
Ajax 包含下列技术(Ajax的技术体系组成)(了解)
基于web标准(standards-basedpresentation) XHTML+ CSS的表示;
使用 DOM(Document ObjectModel)进行动态显示及交互;
使用 XML 和 XSLT 进行数据交换及相关操作;
使用 XMLHttpRequest 进行异步数据查询、检索;
使用 JavaScript 将所有的东西绑定在一起。
AJAX都有哪些优点和缺点?
优点:
1、最大的一点是页面无刷新,用户的体验非常好。
2、使用异步方式与服务器通信,具有更加迅速的响应能力。
3、可以把以前一些服务器负担的工作转嫁到客户端,利用客户端闲置的能力来处理,减轻服务器和带宽的负担,节约空间和宽带租用成本。并且减轻服务器的负担,ajax的原则是“按需取据”,可以最大程度的减少冗余请求,和响应对服务器造成的负担。
4、基于标准化的并被广泛支持的技术,不需要下载插件或者小程序。
缺点:
1、ajax不支持浏览器back按钮。
2、安全问题 AJAX暴露了与服务器交互的细节。
3、对搜索引擎的支持比较弱。
4、破坏了程序的异常机制。
5、不容易调试。
*GET和POST的区别,何时使用POST?
GET:一般用于信息获取,使用URL传递参数,对所发送信息的数量也有限制,一般在2000个字符,有的浏览器是8000个字符
POST:一般用于修改服务器上的资源,对所发送的信息没有限制
在以下情况中,请使用 POST 请求:
1. 无法使用缓存文件(更新服务器上的文件或数据库)
2. 向服务器发送大量数据(POST 没有数据量限制)
3. 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠
jequry核心选择器
简介
和使用js操作Dom一样,获取文档中的节点对象是很频繁的一个操
作,在jQuery中提供了简便的方式供我们查找|定位元素,称为
jQuery选择器,选择器可以说是最考验一个人 jQuery 功力的地
方,通俗的讲, Selector 选择器就是"一个表示特殊语意的字符串"。
只要把选择器字符串传入上面的方法中就能够选择不同的Dom 对
象并且以 jQuery 包装集的形式返回。
jQuery 选择器按照功能主要分为"选择"和"过滤"。 并且是配合使用
的,具体分类如下。基础选择器掌握即可 ,其他用到再查阅。
基础选择器
分支主题
代码
1 <style type="text/css"> 2 .blue{ 3 background: blue; 4 } 5 </style> 67 <body> 8 <div id="mydiv1">id选择器1<span>span中的内容
</span></div> 9 <div id="mydiv2" class="blue">元素选择器</div>
10 <span class="blue">样式选择器</span>
11 </body>
12
13 <script src="js/jquery-3.4.1.js"
type="text/javascript" charset="utf-8"></script>
14 <script type="text/javascript">
15 // id选择器
16 console.log("======id====");
17 var idSelecter = $('#mydiv1');
18 console.log(idSelecter.html());
19 console.log(idSelecter.text());
20 // 元素选择器
21 console.log("======name====");
22 var nameSe = $('div'); // 有多个div元素
23 nameSe.each(function(){
24 // this是dom对象,$(this)是jquery对象
25 console.log($(this).text());
26 });
27 // 类选择器,class
28 console.log("======class====");
29 var classSe = $('.blue'); // 有多个class=blue
的元素
30 classSe.each(function(){
31 console.log($(this).text());
32 });
33 // 通用选择器:*
34 console.log("======所有元素====");
var all = $("*");
console.log(all.length);
// 组合选择器
console.log("======组合====");
var unionSe = $('span, .blue,div');
unionSe.each(function(){
console.log($(this).text());
});
</script>
层次选择器
分支主题
代码
1 <!DOCTYPE html>
2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>层次选择器</title> 6 <script src="js/jquery-3.4.1.js"
type="text/javascript"></script> 7 <style type="text/css"> 8 .testColor{ 9 background: green;
10 }
11 .gray{
12 background: gray;
13 }
14 </style>
15 </head>
16 <body>
17 <div id="parent">层次择器
18 <div id="child" class="testColor">父
选择器
19 <div class="gray">子选择器</div>
20 <img
src="http://www.baidu.com/img/bd_logo1.png"
21 width="270" height="129"
/>
22 <img
src="http://www.baidu.com/img/bd_logo1.png"
23 width="270" height="129"
/>
24 </div>
25 <div>
26 选择器2<div>选择器2中的div</div>
27 </div>
28 </div>
29 </body>
30 <script type="text/javascript">
console.log("=========后代选择器-选择所有后
代=====");
var ancestorS = $('#parent div');
ancestorS.each(function(){
console.log($(this).text());
});
console.log("=========子代选择器-选择儿子辈
=====");
var child = $('#parent>div');
child.each(function(){
console.log($(this).text());
});
console.log("=========相邻选择器=====");
var pre_next = $(".gray + img");
console.log(pre_next.length);
console.log("=========同辈选择器,其后,(弟
弟)=====");
var pre_siblings = $(".gray ~ img");
console.log(pre_siblings.length);
</script>
</html>
表单选择器
分支主题
分支主题
代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>表单验证</title>
<script src="js/jquery-3.4.1.js"
type="text/javascript"></script>
</head>
<body>
<form id='myform' name="myform"
method="post">
<input type="hidden" name="uno"
value="9999" disabled="disabled"/>
姓名:<input type="text" id="uname"
name="uname" /><br />
密码:<input type="password" id="upwd"
name="upwd" value="123456" /><br />
年龄:<input type="radio" name="uage"
value="0" checked="checked"/>小屁孩
<input type="radio" name="uage"
value="1"/>你懂得 <br />
爱好:<input type="checkbox"
name="ufav" value="篮球"/>篮球
<input type="checkbox"
name="ufav" value="爬床"/>爬床
<input type="checkbox"
name="ufav" value="代码"/>代码<br />
来自:<select id="ufrom" name="ufrom">
<option value="-1"
selected="selected">请选择</option>
<option value="0">北京
</option>
21 <option value="1">上海
</option>
22 </select><br />
23 简介:<textarea rows="10" cols="30"
name="uintro"></textarea><br />
24 头像:<input type="file" /><br />
25 <input type="image"
src="http://www.baidu.com/img/bd_logo1.png"
26 width="20" height="20"/>
27 <button type="submit"
onclick="return checkForm();">提交</button>
28 <button type="reset" >重置</button>
29
30 </form>
31 </body>
32 </html>
33 <script type="text/javascript">
34 function checkForm(){
35 // 获取 所有的表单元素
36 $(":input").each(function(){
37 // console.log($(this)[0]);
38 console.log($(this)[0].tagName);
39 })
40 console.log("------
+++++++++++++++++++++--------")
41 // 获取 text
42 console.log("text-->" + $(":text").length); // 1
43 // 获取 password
44 console.log("password-->" + $(":password").length); // 1
45 // 获取radio
46 console.log("radio-->" + $(":radio").length); // 2
47 // 获取checkbox
console.log("checkbox-->" + $(":checkbox").length); // 3
// 获取file
console.log("file-->" + $(":file").length); // 1
// 获取按钮
console.log("button-->" + $(":button").length); // 2
// 获取submit按钮
console.log("submit-->" + $(":submit").length); // 1
// 获取image按钮
console.log("image-->" + $(":image").length); // 1
// 获取reset按钮
console.log("reset-->" + $(":reset").length); // 1
return false;
}
</script>
cookie与session的区别
cookie的机制
详解
Cookie是浏览器(User Agent)访问一些网站后,这些网站存放在客户端的一组数据,用于使网站等跟踪用户,实现用户自定义功能。
Cookie的Domain和Path属性标识了这个Cookie是哪一个网站发送给浏览器的;Cookie的Expires属性标识了Cookie的有 效时间,当Cookie的有效时间过了之后,这些数据就被自动删除了。
如果不设置过期时间,则表示这个Cookie生命周期为浏览器会话期间,只要关闭浏览器窗口,Cookie就消失了。这种生命期为浏览会话期的 Cookie被称为会话Cookie。会话Cookie一般不保存在硬盘上而是保存在内存里。
如果设置了过期时间,浏览器就会把Cookie保存到硬盘 上,关闭后再次打开浏览器,这些Cookie依然有效直到超过设定的过期时间。存储在硬盘上的Cookie可以在不同的浏览器进程间共享,比如两个IE窗 口。而对于保存在内存的Cookie,不同的浏览器有不同的处理方式。
cookie中文是饼干的意思,他是浏览器存储的数据,访问网站时这些数据会发挥作用,例如实现对用户的跟踪,自定义功能,免登陆等
会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
个性化设置(如用户自定义设置、主题等)
浏览器行为跟踪(如跟踪分析用户行为等)
cookie里面通常有以下属性,名称,值,Domain和path属性标识网站来源,Expire/maxAge属性标识cookie过期时间,大小属性标识cookie的大小,cookie最大不超过4kb
对于cookie的设置,不设置过期时间,会默认为session级别,浏览器关闭会消失,此时cookie存储在内存中
设置过期时间,浏览器会将cookie保存到硬盘上
分支主题
session的机制
详解
Session是存放在服务器端的类似于HashTable结构(每一种Web开发技术的实现可能不一样,下文直接称之为HashTable)来存放用户 数据,当浏览器第一次发送请求时,服务器自动生成了一个HashTable和一个Session ID用来唯一标识这个HashTable,并将其通过响应发送到浏览器。当浏览器第二次发送请求,会将前一次服务器响应中的Session ID放在请求中一并发送到服务器上,服务器从请求中提取出Session ID,并和保存的所有Session ID进行对比,找到这个用户对应的HashTable。
一般情况下,服务器会在一定时间内(默认20分钟)保存这个HashTable,过了时间限制,就会销毁这个HashTable。在销毁之前,程序员可以 将用户的一些数据以Key和Value的形式暂时存放在这个HashTable中。当然,也有使用数据库将这个HashTable序列化后保存起来的,这 样的好处是没了时间的限制,坏处是随着时间的增加,这个数据库会急速膨胀,特别是访问量增加的时候。一般还是采取前一种方式,以减轻服务器压力。
session是存储在服务器里的数据,是键值对的存储形式,是map集合,类似于hashtable,也是存储用户数据,浏览器第一次发送请求给服务器,服务器会自动生成一个hashtable和一个sessionID,id用来标识hashtable,通过响应发送给浏览器,浏览器第二次请求会将第一次的sessionid一并发送,服务器从请求中提取sessionid,并和存储在服务端的sessionid做比对,找到这个用户对应的hashtable
服务器一般默认保存20分钟的hashtable,也可以使用数据库将这个HashTable序列化后保存起来的,这样的好处是没了时间的限制,坏处是随着时间的增加,这个数据库会急速膨胀,特别是访问量增加的时候。一般还是采取前一种方式,以减轻服务器压力。
区别
1,作用范围不同,session 在服务器端,cookie 在客户端(浏览器)
2,存取方式的不同,Cookie 只能保存 ASCII,Session 可以存任意数据类型,一般情况下我们可以在 Session 中保持一些常用变量信息,比如说 UserId 等。
3,session 的运行依赖 session id,而 session id 是存在 cookie 中的,也就是说,如果浏览器禁用了 cookie ,同时 session 也会失效(但是可以通过其它方式实现,比如在 url 中传递 session_id)
4,有效期不同,Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭或者 Session 超时都会失效。
5,隐私策略不同,Cookie 存储在客户端,比较容易遭到不法获取,早期有人将用户的登录名和密码存储在 Cookie 中导致信息被窃取;Session 存储在服务端,安全性相对 Cookie 要好一些。
6,存储大小不同, 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie。
其他
网站的免登陆是如何做到的?(了解)
方式一:首先想到的是使用cookie保存用户登录信息,设置有效期,在用户下次访问时免去登录环节,直接通过cookie获取用户信息。
方式二:直接将session会话保存,用户下次访问时,继续使用这个session。
相比之下session显得更加安全,但是,大家知道,session会随着浏览器的关闭而消失(确切的说,是在客户端消失,服务器端的session存活周期取决于相应配置),当用户下次启动浏览器,访问网站时,又会得到由网站自动分配的新的session。
那么,问题来了:如何做到
关闭浏览器后到下次登录时session仍然有效?
思路:
1、在用户登录成功时,创建session对象,保存用户信息
2、将此session的sessionid保存到cookie中
setcookie('session_id',session_id(),time()+3600*24,"/","https://localhost:8080");
3、同时将sessionid于session对应关系存储到应用域中,以便后面可以根据sessionid来获取到session
4、在用户关闭浏览器,重新打开浏览器访问网站时,读取用户的cookie,得到sessionid
if(isset($_COOKIE['session_id'])){
session_id($_COOKIE['session_id']);
}
5、根据sessionid获取到第3步存储到应用域中的session对象
6、从session中读取用户信息
服务端是根据 Cookie 中的信息判断用户是否登录,如果浏览器中禁止了 Cookie,如何保障整个机制的正常运转。(了解)
第一种方案,每次请求中都携带一个 SessionID 的参数,也可以 Post 的方式提交,也可以在请求的地址后面拼接 xxx?SessionID=123456...。
第二种方案,Token 机制。Token 机制多用于 App 客户端和服务器交互的模式,也可以用于 Web 端做用户状态管理。
Token 的意思是“令牌”,是服务端生成的一串字符串,作为客户端进行请求的一个标识。Token 机制和 Cookie 和 Session 的使用机制比较类似。
当用户第一次登录后,服务器根据提交的用户信息生成一个 Token,响应时将 Token 返回给客户端,以后客户端只需带上这个 Token 前来请求数据即可,无需再次登录验证。
后端
登录界面
用户登录功能实现
用户登录
简略(待完成)
过滤器拦截用户到登录页面,用户输入用户名密码,点击登录,提交表单给后台,后台servlet接收,根据actionName判断是什么行为,进行控制访问,调用登录方法,获取提交的用户名密码数据,再调用service层,service层依次对用户名和密码进行判断,用户名是否为空,查询数据库是否存在该用户,密码是否为空,密码是否正确,然后判断结果设置相应的code和msg信息到resultInfo里,返回给servlet相应内容,servlet根据返回的code码,设置相应信息到request的resultInfo属性里,返回错误信息到登录页面,或登录成功跳转到主页面
过滤器拦截用户到登录页面,用户输入用户名密码,点击登录,提交表单发送post请求给后台,(这里前端不进行任何的业务逻辑判断,全权由后端进行负责)后台servlet接收,根据actionName判断是什么行为,进行控制访问,调用登录方法,获取提交的用户名密码数据,再调用service层,service层依次对用户名和密码进行判断,用户名是否为空,查询数据库是否存在该用户,密码是否为空,查询数据库根据该用户名查询密码,将表单密码加密后进行比对,然后判断结果设置相应的code和msg信息到resultInfo里,返回给servlet相应内容,servlet根据返回的code码,设置相应信息到request的resultInfo属性里,返回错误信息请求转发到登录页面,或登录成功设置user到session里,重定向跳转到主页面
免登录
servlet得到service判断用户和密码均正确的结果后,如果用户勾选了记住我的复选框,则设置cookie的user属性,值为用户名-密码,设置过期时间,添加到response里面,如果没有勾选则,设置cookie立即过期,覆盖原cookie值。勾选记住我后,待用户关闭浏览器,下次再通过该浏览器访问页面时,拦截器会自动识别获取到cookie的值,拼接url字符串,调用response重定向请求之前的用户登录方法,完成免登陆流程
用户退出
销毁session,设置cookie立即过期,并覆盖原cookie,请求重定向到登录页面
用户模块
简介
通过用户行为来区分 actionName
用户登录 actionName="login"
进入个人中心 actionName="userCenter"
加载头像 actionName="userHead"
验证昵称的唯一性 actionName="checkNick"
修改用户信息 actionName="updateUser"
用户退出 actionName="logout"
在UserServlet的service方法中
1.获取用户行为
String actionName = request.getParameter("actionName");
2. 判断用户行为,调用对应的方法
if ("login".equals(actionName)) {
// 用户登录
} else if ("userCenter".equals(actionName)) {
// 进入个人中心
}
一、用户登录
前端实现
1. 添加表单,设置表单属性
action="user" method="post" id="loginForm"
2. 设置表单元素的那么属性值,修改id属性值(首字母改小写)
设置文本框和密码框的那么属性值
设置复选框的那么属性值和value属性值(如果复选框未选中,在获取不到value属性值)
设置隐藏域,传递用户行为 (name="actionName" value="login")
3. 在 config.js 文件中,添加按钮的点击事件绑定的函数 checkLogin()
表单校验
1. 获取表单元素的值(姓名、密码)
2. 判断元素的值是否为空(引入util.js文件)
如果为空,设置提示信息 (通过设置span标签),并return
3. 如果不为空,则提交表单
后端实现
整体思路
1. 获取参数 (姓名、密码)
2. 参数的非空校验 (ResultInfo的封装类,用来封装响应结果 状态码、提示信息、返回的对象)
如果为空
设置ResultInfo对象的状态码和提示信息
将ResultInfo对象设置request作用域中
请求转发跳转到登录页面
return
3. 如果不为空,通过用户名查询用户对象
4. 判断用户对象是否为空
如果为空
设置ResultInfo对象的状态码和提示信息
将ResultInfo对象设置request作用域中
请求转发跳转到登录页面
return
5. 如果用户对象不为空,将数据库中查询到的用户对象的密码与前台传递的密码作比较 (将密码加密后再比较)
如果密码不正确
设置ResultInfo对象的状态码和提示信息
将ResultInfo对象设置request作用域中
请求转发跳转到登录页面
return
6. 如果密码正确
将用户信息存到session作用域中
判断用户是否选择记住密码(rem的值是1)
如果是,将用户姓名与密码存到cookie中,设置失效时间,并响应给客户端
如果否,清空原有的cookie对象
重定向跳转到index页面
分层思想
Web层
(控制层:接收参数、响应数据)
1. 获取参数 (姓名、密码)
2. 调用Service层的方法,返回ResultInfo对象
3. 判断是否登录成功
如果失败
将resultInfo对象设置到request作用域中
请求转发跳转到登录页面
如果成功
将用户信息设置到session作用域中
判断用户是否选择记住密码(rem的值是1)
如果是,将用户姓名与密码存到cookie中,设置失效时间,并响应给客户端
如果否,清空原有的cookie对象
重定向跳转到index页面
Service层
(业务逻辑层:参数判断、业务逻辑处理)
1. 判断参数是否为空
如果为空
设置ResultInfo对象的状态码和提示信息
返回resultInfo对象
2. 如果不为空,通过用户名查询用户对象
3. 判断用户对象是否为空
如果为空
设置ResultInfo对象的状态码和提示信息
返回resultInfo对象
4. 如果用户对象不为空,将数据库中查询到的用户对象的密码与前台传递的密码作比较 (将密码加密后再比较)
如果密码不正确
设置ResultInfo对象的状态码和提示信息
返回resultInfo对象
5. 如果密码正确
设置ResultInfo对象的状态码和提示信息
6. 返回resultInfo对象
Dao层
(数据访问层:数据库中的增删改查操作)
通过用户名查询用户对象, 返回用户对象
1. 获取数据库连接
2. 定义sql语句
3. 预编译
4. 设置参数
5. 执行查询,返回结果集
6. 判断并分析结果集
7. 关闭资源
二、非法访问拦截和免登录
非法访问拦截
拦截的资源:
所有资源 /*
需要被放行的资源:
1、指定页面,放行 (用户无需登录即可访问的页面;例如:登录页面login.jsp、注册页面register.jsp等)
2、静态资源,放行 (存放在statics目录下的资源;例如:js、css、images等)
3、指定行为,放行 (用户无需登录即可执行的操作;例如:登录操作user?actionName=login、注册操作等)
4、登录状态,放行 (判断session作用域中是否存在用户信息;存在就放行,不存在,就拦截跳转到登录页面)
免登录(自动登录)
通过Cookie和Session对象实现
什么时候使用免登录:
当用户处于未登录状态,且去请求需要登录才能访问的资源时,调用自动登录功能
目的:
让用户处于登录状态(自动调用登录方法)
实现:
从Cookie对象中获取用户的姓名与密码,自动执行登录操作
1、获取cookie数组
2、判断cookie数组
3、遍历cookie数组,获取指定的cookie对象 (name为user的cookie对象)
4、得到指定cookie对象的value (姓名与密码:userName-userPwd)
5、通过split()方法将value字符串转换成数组
6、从数组中别得到姓名和密码
7、重定向到登录操作 user?actionName=login&userName=姓名&userPwd=密码
return;
三、用户退出
前端:
设置超链接的请求地址 user?actionName=logout
后台:
1. 销毁Session对象
2. 删除Cookie对象
3. 重定向跳转到登录页面
四、进入个人中心
前台:
设置超链接的访问地址 user?actionName=userCenter
通过el表达式获取session作用域中的用户信息
后台:
1. 设置首页动态包含的页面值
2. 请求转发跳转到index.jsp
五、加载头像
前台:
设置img标签的src属性,请求后台加载头像
src="user?actionName=userHead&imageName=图片名称" (通过el表达式熊session中获取)
后台:
1. 获取参数 (图片名称)
2. 得到图片的存放路径 (request.getServletContext().getealPathR("/"))
3. 通过图片的完整路径,得到file对象
4. 通过截取,得到图片的后缀
5. 通过不同的图片后缀,设置不同的响应的类型
6. 利用FileUtils的copyFile()方法,将图片拷贝给浏览器
六、验证昵称的唯一性
前台:
前台:
昵称文本框的失焦事件 blur
1. 获取昵称文本框的值
2. 判断值是否为空
如果为空,提示用户,禁用按钮,并return
3. 判断昵称是否做了修改
从session作用域中获取用户昵称 (如果在js中想要使用el表达式获取域对象,js需要写在JSP页面中,无法在js文件中获取)
如果用户昵称与session中的昵称一致,则return
4. 如果昵称做了修改
发送ajax请求后台,验证昵称是否可用
如果不可用,提示用户,并禁用按钮
如果可用,清空提示信息,按钮可用
昵称文本框的聚焦事件 focus
1. 清空提示信息
2. 按钮可用
后台:
Web层:
1. 获取参数(昵称)
2. 从session作用域获取用户对象,得到用户ID
3. 调用Service层的方法,得到返回的结果
4. 通过字符输出流将结果响应给前台的ajax的回调函数
5. 关闭资源
Service层:
1. 判断昵称是否为空
如果为空,返回"0"
2. 调用Dao层,通过用户ID和昵称查询用户对象
3. 判断用户对象存在
存在,返回"0"
不存在,返回"1"
Dao层:
1. 定义SQL语句
通过用户ID查询除了当前登录用户之外是否有其他用户使用了该昵称
指定昵称 nick (前台传递的参数)
当前用户 userId (session作用域中的user对象)
String sql = "select * from tb_user where nick = ? and userId != ?";
2. 设置参数集合
3. 调用BaseDao的查询方法
七、修改用户信息
前台:
文件上传表单
1. 表单
表单类型 enctype="multipart/form-data"
提交方式 method="post"
2. 表单元素
设置name属性值(昵称、心情、头像)
设置隐藏域存放用户行为actionName
后台:
Web层:
注:文件上传必须在Servlet类上提那家注解!!! @MultipartConfig
1. 调用Service层的方法,传递request对象作为参数,返回resultInfo对象
2. 将resultInfo对象存到request作用域中
3. 请求转发跳转到个人中心页面 (user?actionName=userCenter)
Service层:
1. 获取参数(昵称、心情)
2. 参数的非空校验(判断必填参数非空)
如果昵称为空,将状态码和错误信息设置resultInfo对象中,返回resultInfo对象
3. 从session作用域中获取用户对象(获取用户对象中默认的头像)
4. 实现上上传文件
1. 获取Part对象 request.getPart("name"); name代表的是file文件域的name属性值
2. 通过Part对象获取上传文件的文件名
3. 判断文件名是否为空
4. 获取文件存放的路径 WEB-INF/upload/目录中
5. 上传文件到指定目录
5. 更新用户头像 (将原本用户对象中的默认头像设置为上传的文件名)
6. 调用Dao层的更新方法,返回受影响的行数
7. 判断受影响的行数
如果大于0,则修改成功;否则修改失败
8. 返回resultInfo对象
Dao层:
通过用户ID修改用户信息
1. 定义SQL语句
String sql = "update tb_user set nick = ?, mood = ?, head = ? where userId = ? ";
2. 设置参数集合
3. 调用BaseDao的更新方法,返回受影响的行数
4. 返回受影响的行数
类别管理增删改查
类型模块
简介
用户行为 actionName
查询类型列表 actionName=list
删除类型 actionName=delete
添加/修改类型 actionName=addOrUpdate
一、查询类型列表
前台:
给导航栏的"类型管理"超链接设置href属性值
href="user?actionName=list"
设置导航栏的高亮值
后台通过请求域设置menu_page
接收响应数据
通过JSTl的if标签,判断类型集合是否存在
如果不存在,则显示对应的提示信息(暂未查询到类型数据!)
如果存在,通过JSTL的forEach标签进行遍历
注:使用JSTL标签需要导入对应的依赖与核心标签库
后台:
Web层:
1. 获取Session作用域设置的user对象
2. 调用Service层的查询方法,查询当前登录用户的类型集合,返回集合
3. 将类型列表设置到request请求域中
4. 设置首页动态包含的页面值
5. 请求转发跳转到index.jsp页面
Service层:
1. 调用Dao层的查询方法,通过用户ID查询类型集合
2. 返回类型集合
Dao层:
通过用户ID查询类型集合
1. 定义SQL语句
String sql = "select typeId,typeName,userId from tb_note_type where userId = ? ";
2. 设置参数列表
3. 调用BaseDao的查询方法,返回集合
4. 返回集合
二、删除类型
前台:
"删除"按钮绑定点击事件(传递参数:类型ID)
1. 弹出提示框,询问用户是否确认删除
2. 如果确认删除,发送ajax请求后台执行删除操作,返回resultInfo对象(类型ID)
如果删除失败,code=0
提示用户删除失败,msg=xxx
如果删除成功,code=1
准备工作:
1. 表格元素,设置id属性值 id="myTable"
2. 表格的父元素div元素,设置id属性值 id="myDiv"
3. 表格的每一个tr元素,设置id属性值 id="tr_类型ID" (循环设置每一个tr的id属性值)
4. 左侧类型分组的导航列表项li元素,设置id属性值 id="tr_类型ID"
1. 移除指定的tr记录
给table元素设置id属性值;给每一个tr添加id属性值
1. 通过id属性值,得到表格对象
2. 得到table元素的子元素tr的数量
3. 判断tr的数量 (判断是否有多条类型记录)
如果tr的数量等于2,表示只有一条类型记录,删除整个表格对象,并设置提示内容
如果tr的数量大于2,表示有多条类型记录,删除指定的tr对象
2. 删除左侧类型分组的导航列表项
1. 给li元素设置id属性值
2. 通过id选择器获取指定的li元素,并移除
后台:
Web层:
1. 接收参数(类型ID)
2. 调用Service的更新操作,返回ResultInfo对象
3. 将ResultInfo对象转换成JSON格式的字符串,响应给ajax的回调函数
Service层:
1. 判断参数是否为空
2. 调用Dao层,通过类型ID查询云记记录的数量
3. 如果云记数量大于0,说明存在子记录,不可删除
code=0,msg="该类型存在子记录,不可删除",返回resultInfo对象
4. 如果不存在子记录,调用Dao层的更新方法,通过类型ID删除指定的类型记录,返回受影响的行数
5. 判断受影响的行数是否大于0
大于0,code=1;否则,code=0,msg="删除失败"
6. 返回ResultInfo对象
Dao层:
通过类型ID查询云记记录的数量,返回云记数量
通过类型ID删除指定的类型记录,返回受影响的行数
三、添加/修改类型
前台:
准备工作:
1. 设置隐藏域,并设置id属性值,用来存放类型ID
2. 设置文本框,并设置id属性值,用来存放类型名称
a、打开添加模态框
绑定"添加类型"按钮的点击事件
1. 设置添加模态框的标题
2. 清空模态框中的文本框与隐藏域的值
3. 打开添加模态框
b、打开修改模态框
1. 设置修改模态框的标题
2. 将当前要修改的类型记录对应的类型id与类型名称,设置到模态框的隐藏域与文本框中
2.1 通过id属性值,得到要修改的tr记录 (id="tr_类型ID")
2.2 得到tr的具体的单元格
类型名称 第二个单元格,下标是1,tr的子元素中的第二个
类型ID 第一个单元格,下标是0,tr的子元素中的第一个
2.3 将类型ID与类型名称赋值给模态框的隐藏域与文本框
3. 利用模态框的ID属性值,调用show方法,打开模态框
c、模态框的"保存"按钮,绑定点击事件
【添加类型 或 修改类型】
1. 获取参数
添加操作:类型名称
修改操作:类型名称、类型ID
2. 判断参数是否为空(类型名称)
如果为空,提示信息,并return
3. 发送ajax请求后台,执行添加或修改功能,返回ResultInfo对象(通过类型ID是否为空来判断,如果为空,则为添加;如果不为空,则为修改)
判断是否更新成功
如果code=0,表示失败,提示用户失败
如果code=1,表示成功,执行DOM操作
1. 关闭模态框
2. 判断类型ID是否为空
如果为空,执行添加的DOM操作
1. 添加tr记录
1.1. 拼接tr标签
1.2. 通过id属性值,获取表格对象
1.3. 判断表格对象是否存在 (长度是否大于0)
1.4. 如果表格存在,将tr标签追加到表格对象中
1.5. 如果表格不存在,则拼接表格及tr标签,将整个表格追加到div中
2. 添加左侧类型分组导航栏的列表项
2.1. 拼接li元素
2.3 设置ul标签的id属性值,将li元素追加到ul中
如果不为空,执行修改的DOM操作
1. 修改指定tr记录
1. 通过id选择器,得到tr对象
2. 修改tr指定单元格的文本值
2. 修改左侧类型分组导航栏的列表项
给左侧类型名称设置span标签,并指定id属性值,修改span元素的文本值
后台:
Web层:
1. 接收参数 (类型名称、类型ID)
2. 获取Session作用域中的user对象,得到用户ID
3. 调用Service层的更新方法,返回ResultInfo对象
4. 将ResultInfo转换成JSON格式的字符串,响应给ajax的回调函数
Service层:
1. 判断参数是否为空 (类型名称)
如果为空,code=0,msg=xxx,返回ResultInfo对象
2. 调用Dao层,查询当前登录用户下,类型名称是否唯一,返回0或1
如果不可用,code=0,msg=xxx,返回ResultInfo对象
3. 判断类型ID是否为空
如果为空,调用Dao层的添加方法,返回主键 (前台页面需要显示添加成功之后的类型ID)
如果不为空,调用Dao层的修改方法,返回受影响的行数
4. 判断 主键/受影响的行数 是否大于0
如果大于0,则更新成功
code=1,result=主键
如果不大于0,则更新失败
code=0,msg=xxx
Dao层:
查询当前登录用户下,类型名称是否唯一
添加方法,返回主键
修改方法,返回受影响的行数
云记增删改查
云记模块
简介
用户行为 actionName
进入发布云记页面 actionName=view
添加或修改云记 actionName=addOrUpdate
查询云记详情 actionName=detail
删除云记 actionName=delete
分页查询云记列表 actionName=list
一、进入发布云记页面
前台:
设置头部导航栏的"发布云记"超链接的href属性
href="note?actionName=view"
后台:
1. 从Session对象中获取用户对象
2. 通过用户ID查询对应的类型列表
3. 将类型列表设置到request请求域中
4. 设置首页动态包含的页面值
5. 请求转发跳转到index.jsp
二、加载富文本编辑器
1. 下载富文本编辑器的压缩包(UTF-8),解压压缩包
2. 将解压后的目录中对应的utf8-jsp文件夹,拷贝到项目中的statics目录下
3. 修改utf8-jsp文件夹的名称,设置为ueditor
4. 将ueditor目录下jsp目录下的lib目录中对应的jar包,在pom.xml中加载进来;(在WEB-INF目录下,新建lib目录,将json与ueditor的jar包拷贝进去 )
5. 修改配置文件
a. ueditor目录下的 ueditor.config.js 文件
设置编辑器资源文件的根路径 (相当于网站根路径的相对路径)
将20行的注释拷贝到22行
window.UEDITOR_HOME_URL = "/xxxx/xxxx/";
改成:
window.UEDITOR_HOME_URL = "/note/statics/ueditor/"; // /站点名/资源所在路径
b. ueditor目录下jsp目录中的 config.json 文件
修改第11行的图片访问前缀
"imageUrlPrefix": "", /* 图片访问路径前缀 */
改成:
"imageUrlPrefix": "/note", /* 图片访问路径前缀 */
修改第12行的图片保存路径
"imagePathFormat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
改成:
"imagePathFormat": "/statics/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
6. 使用富文本编辑器
1. 引入相关的JS文件 (先引入配置文件,再引入源码文件)
<!-- 配置文件 -->
<script type="text/javascript" src="statics/ueditor/ueditor.config.js"></script>
<!-- 编辑器源码文件 -->
<script type="text/javascript" src="statics/ueditor/ueditor.all.js"></script>
2. 准备容器 (准备一个元素,设置id属性值)
<!-- 加载富文本编辑器的容器 -->
<textarea id="content" name="content"></textarea>
3. 加载富文本编辑器
<script type="text/javascript">
$(function (){
// 加载富文本编辑器 UE.getEditor('容器Id');
var ue = UE.getEditor('content');
});
</script>
4. 自定义工具栏
ueditor.config.js 文件的toolbar属性中
三、发布云记
前台:
表单提交
设置表单的提交方式和提交地址
在隐藏域中设置用户行为actionName
设置表单元素的id与name属性值
表单校验
1. 获取表单元素的值
获取下拉框选中的选项 .val()
获取文本框的值 .val()
获取富文本编辑器的内容
ue.getContent() 获取富文本编辑器的内容(包含html标签)
ue.getContentTxt() 获取富文本编辑器的纯文本内容(不包含html标签)
2. 参数的非空判断
如果为空,提示用户,并return fasle
3. 如果参数不为空,则return true,提交表单
后台:
Web层:
1. 接收参数 (类型ID、标题、内容)
2. 调用Service层方法,返回resultInfo对象
3. 判断resultInfo的code值
如果code=1,表示成功
重定向跳转到首页 index
如果code=0,表示失败
将resultInfo对象设置到request作用域
请求转发跳转到note?actionName=view
Service层:
1. 设置回显对象 Note对象
2. 参数的非空判断
如果为空,code=0,msg=xxx,result=note对象,返回resultInfo对象
2. 调用Dao层,添加云记记录,返回受影响的行数
3. 判断受影响的行数
如果大于0,code=1
如果不大于0,code=0,msg=xxx,result=note对象
4. 返回resultInfo对象
Dao层:
添加云记,返回受影响的行数
四、分页查询云记列表
后台:
Web层:
1. 接收参数 (当前页、每页显示的数量)
2. 获取Session作用域中的user对象
3. 调用Service层查询方法,返回Page对象
4. 将page对象设置到request作用域中
5. 设置首页动态包含的页面值
6. 请求转发跳转到index.jsp
Service层:
1. 参数的非空校验
如果分页参数为空,则设置默认值
2. 查询当前登录用户的云记数量,返回总记录数 (long类型)
3. 判断总记录数是否大于0
4. 如果总记录数大于0,调用Page类的带参构造,得到其他分页参数的值,返回Page对象
5. 查询当前登录用户下当前页的数据列表,返回note集合
6. 将note集合设置到page对象中
7. 返回Page对象
Dao层:
查询当前登录用户的云记数量,返回总记录数
查询当前登录用户下当前页的数据列表,返回note集合
五、查询云记详情
后台:
Web层:
1. 接收参数 (noteId)
2. 调用Service层的查询方法,返回Note对象
3. 将Note对象设置到request请求域中
4. 设置首页动态包含的页面值
5. 请求转发跳转到index.jsp
Service层:
1. 参数的非空判断
2. 调用Dao层的查询,通过noteId查询note对象
3. 返回note对象
Dao层:
通过noteId查询note对象
六、删除云记
前台:
"删除"按钮绑定点击事件
1. 弹出提示框询问用户是否确认删除
2. 如果确认删除,则发送ajax请求
如果删除失败,提示用户
如果删除成功,跳转到首页
后台:
Web层:
1. 接收参数 (noteId)
2. 调用Service层删除方法,返回状态码 (1=成功,0=失败)
3. 通过流将结果响应给ajax的回调函数 (输出字符串)
Service层:
1. 判断参数
2. 调用Dao层的更新方法,返回受影响的行数
3. 判断受影响的行数是否大于0
如果大于0,返回1;否则返回0
Dao层:
通过noteId删除云记记录,返回受影响的行数
basedao采用反射的好处是什么
原本我们会通过直接new对象的方式,然后通过set方法设置,这样一条一条的set进去,对应成javabean,如果po类,里面的属性非常多,那么属性就需要依次的对应,会产生造成大量的代码,非常的冗余和繁琐,而如果每个sql对应的javabean都这样处理,则会产生巨量冗余和繁琐的代码,而用了反射以后,会减少非常多的代码量,mybatis框架也是这样,它减少了大约90%的代码量
请求转发与重定向的区别
forward(转发):
是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,因为这个跳转过程实在服务器实现的,并不是在客户端实现的所以客户端并不知道这个跳转动作,所以它的地址栏还是原来的地址.
redirect(重定向):
是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
转发是服务器行为,重定向是客户端行为。
区别:
从地址栏显示来说
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址.
redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
从数据共享来说
forward:转发页面和转发到的页面可以共享request里面的数据.
redirect:不能共享数据.
从运用地方来说
forward:一般用于用户登陆的时候,根据角色转发到相应的模块.
redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等
从效率来说
forward:高.
redirect:低.
拦截器的实现方式
https://blog.csdn.net/xpsharp/article/details/6890938
https://blog.csdn.net/reggergdsg/article/details/52962774
数据库
数据库三张表之间的关系
分支主题
一对多相互关联靠的是主键和外键(具有相同的列名,可以进行等值连接,连表查询)
注意:
1.外键必须是另一个表的主键
2.主键和外键的数据类型必须是一样的
3.主键和外键同时存在的表是多的一方
数据库字段中,哪些是唯一的
JDBC流程
(数据访问层:数据库中的增删改查操作)
通过用户名查询用户对象, 返回用户对象
1. 获取数据库连接
2. 定义sql语句
3. 预编译
4. 设置参数
5. 执行查询,返回结果集
6. 判断并分析结果集
7. 关闭资源
一些重点的SQL语句
云记模块(重点)
几乎包含了另外其他模块的所有类似的操作
@Insert("insert into db_lezijie_note.tb_note(title, content, typeId, pubTime, lon, lat)VALUES (#{title}, #{content}, #{typeId},now(), #{lon}, #{lat})")
int addNote(Note note);
@Delete("delete from db_lezijie_note.tb_note where noteId=#{noteId}")
int deleteNote(int noteId);
@Update("update db_lezijie_note.tb_note " +
"set title=#{title},content=#{content},typeId=#{typeId},lon=#{lon},lat=#{lat} where noteId=#{noteId}")
int updateNote(Note note);
@Select("select count(*) from db_lezijie_note.tb_note where typeId=#{typeId}")
int selectNotesByID(int typeId);
@Select("SELECT count(1) noteCount,DATE_FORMAT(pubTime,'%Y年%m月') groupName FROM db_lezijie_note.tb_note n " +
" INNER JOIN db_lezijie_note.tb_note_type t ON n.typeId = t.typeId WHERE userId = #{userId} " +
" GROUP BY DATE_FORMAT(pubTime,'%Y年%m月')" +
" ORDER BY DATE_FORMAT(pubTime,'%Y年%m月') DESC ")
List<NoteVo> findNoteCountByDate(Integer userId);
动态SQL
<select id="selectNotes" resultType="com.lezijie.note.po.Note">
select * from db_lezijie_note.tb_note n inner join
db_lezijie_note.tb_note_type t on n.typeId = t.typeId where userId = #{userId}
<choose>
<when test="title!=null and title!=''">
and title like concat('%',#{title},'%')
</when>
<when test="date!=null and date!=''">
and date_format(pubTime,'%Y年%m月') = #{date}
</when>
<when test="typeId!=null and typeId!=''">
and n.typeId = #{typeId}
</when>
</choose>
order by pubTime desc limit #{index},#{pageSize}
</select>
<select id="findNoteCount" resultType="int">
select count(*) from db_lezijie_note.tb_note n inner join
db_lezijie_note.tb_note_type t on n.typeId = t.typeId where userId = #{userId}
<choose>
<when test="title!=null and title!=''">
and title like concat('%',#{title},'%')
</when>
<when test="date!=null and date!=''">
and date_format(pubTime,'%Y年%m月') = #{date}
</when>
<when test="typeId!=null and typeId!=''">
and n.typeId = #{typeId}
</when>
</choose>
</select>
接口绑定
int findNoteCount(@Param("userId")Integer userId, @Param("title")String title,
@Param("date")String date,@Param("typeId") Integer typeId);
List<Note> selectNotes(@Param("userId") Integer userId, @Param("title")String title,
@Param("date")String date, @Param("typeId")Integer typeId,
@Param("index")Integer index, @Param("pageSize")Integer pageSize);
动态SQL理解
where if就相当于正常的java中的if 语句,如果有多个条件组合判断的话用 and, or连接
而where choose when otherwise
choose就好像是switch,when相当于case,可以有一种属性的对个判断,但不能同时去判断多个属性. 同时,只要有一个when满足,则break
otherwise就好像是default,如果前面的when都不满足,则进入otherwise
59期面试题(王广政老师)
### 用户登录、密码修改
、退出登录、记住密码模块
> cookie与session如何实现登录功能,cookie与session的区别
cookie的机制
详解
Cookie是浏览器(User Agent)访问一些网站后,这些网站存放在客户端的一组数据,用于使网站等跟踪用户,实现用户自定义功能。
Cookie的Domain和Path属性标识了这个Cookie是哪一个网站发送给浏览器的;Cookie的Expires属性标识了Cookie的有 效时间,当Cookie的有效时间过了之后,这些数据就被自动删除了。
如果不设置过期时间,则表示这个Cookie生命周期为浏览器会话期间,只要关闭浏览器窗口,Cookie就消失了。这种生命期为浏览会话期的 Cookie被称为会话Cookie。会话Cookie一般不保存在硬盘上而是保存在内存里。
如果设置了过期时间,浏览器就会把Cookie保存到硬盘 上,关闭后再次打开浏览器,这些Cookie依然有效直到超过设定的过期时间。存储在硬盘上的Cookie可以在不同的浏览器进程间共享,比如两个IE窗 口。而对于保存在内存的Cookie,不同的浏览器有不同的处理方式。
cookie中文是饼干的意思,他是浏览器存储的数据,访问网站时这些数据会发挥作用,例如实现对用户的跟踪,自定义功能,免登陆等
会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
个性化设置(如用户自定义设置、主题等)
浏览器行为跟踪(如跟踪分析用户行为等)
cookie里面通常有以下属性,名称,值,Domain和path属性标识网站来源,Expire/maxAge属性标识cookie过期时间,大小属性标识cookie的大小,cookie最大不超过4kb
对于cookie的设置,不设置过期时间,会默认为session级别,浏览器关闭会消失,此时cookie存储在内存中
设置过期时间,浏览器会将cookie保存到硬盘上
分支主题
session的机制
详解
Session是存放在服务器端的类似于HashTable结构(每一种Web开发技术的实现可能不一样,下文直接称之为HashTable)来存放用户 数据,当浏览器第一次发送请求时,服务器自动生成了一个HashTable和一个Session ID用来唯一标识这个HashTable,并将其通过响应发送到浏览器。当浏览器第二次发送请求,会将前一次服务器响应中的Session ID放在请求中一并发送到服务器上,服务器从请求中提取出Session ID,并和保存的所有Session ID进行对比,找到这个用户对应的HashTable。
一般情况下,服务器会在一定时间内(默认20分钟)保存这个HashTable,过了时间限制,就会销毁这个HashTable。在销毁之前,程序员可以 将用户的一些数据以Key和Value的形式暂时存放在这个HashTable中。当然,也有使用数据库将这个HashTable序列化后保存起来的,这 样的好处是没了时间的限制,坏处是随着时间的增加,这个数据库会急速膨胀,特别是访问量增加的时候。一般还是采取前一种方式,以减轻服务器压力。
session是存储在服务器里的数据,是键值对的存储形式,是map集合,类似于hashtable,也是存储用户数据,浏览器第一次发送请求给服务器,服务器会自动生成一个hashtable和一个sessionID,id用来标识hashtable,通过响应发送给浏览器,浏览器第二次请求会将第一次的sessionid一并发送,服务器从请求中提取sessionid,并和存储在服务端的sessionid做比对,找到这个用户对应的hashtable
服务器一般默认保存20分钟的hashtable,也可以使用数据库将这个HashTable序列化后保存起来的,这样的好处是没了时间的限制,坏处是随着时间的增加,这个数据库会急速膨胀,特别是访问量增加的时候。一般还是采取前一种方式,以减轻服务器压力。
区别
1,作用范围不同,session 在服务器端,cookie 在客户端(浏览器)
2,存取方式的不同,Cookie 只能保存 ASCII,Session 可以存任意数据类型,一般情况下我们可以在 Session 中保持一些常用变量信息,比如说 UserId 等。
3,session 的运行依赖 session id,而 session id 是存在 cookie 中的,也就是说,如果浏览器禁用了 cookie ,同时 session 也会失效(但是可以通过其它方式实现,比如在 url 中传递 session_id)
4,有效期不同,Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭或者 Session 超时都会失效。
5,隐私策略不同,Cookie 存储在客户端,比较容易遭到不法获取,早期有人将用户的登录名和密码存储在 Cookie 中导致信息被窃取;Session 存储在服务端,安全性相对 Cookie 要好一些。
6,存储大小不同, 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie。
用户登录功能实现
用户登录
简略(待完成)
过滤器拦截用户到登录页面,用户输入用户名密码,点击登录,提交表单给后台,后台servlet接收,根据actionName判断是什么行为,进行控制访问,调用登录方法,获取提交的用户名密码数据,再调用service层,service层依次对用户名和密码进行判断,用户名是否为空,查询数据库是否存在该用户,密码是否为空,密码是否正确,然后判断结果设置相应的code和msg信息到resultInfo里,返回给servlet相应内容,servlet根据返回的code码,设置相应信息到request的resultInfo属性里,返回错误信息到登录页面,或登录成功跳转到主页面
过滤器拦截用户到登录页面,用户输入用户名密码,点击登录,提交表单发送post请求给后台,(这里前端不进行任何的业务逻辑判断,全权由后端进行负责)后台servlet接收,根据actionName判断是什么行为,进行控制访问,调用登录方法,获取提交的用户名密码数据,再调用service层,service层依次对用户名和密码进行判断,用户名是否为空,查询数据库是否存在该用户,密码是否为空,查询数据库根据该用户名查询密码,将表单密码加密后进行比对,然后判断结果设置相应的code和msg信息到resultInfo里,返回给servlet相应内容,servlet根据返回的code码,设置相应信息到request的resultInfo属性里,返回错误信息请求转发到登录页面,或登录成功设置user到session里,重定向跳转到主页面
免登录
servlet得到service判断用户和密码均正确的结果后,如果用户勾选了记住我的复选框,则设置cookie的user属性,值为用户名-密码,设置过期时间,添加到response里面,如果没有勾选则,设置cookie立即过期,覆盖原cookie值。勾选记住我后,待用户关闭浏览器,下次再通过该浏览器访问页面时,拦截器会自动识别获取到cookie的值,拼接url字符串,调用response重定向请求之前的用户登录方法,完成免登陆流程
用户退出
销毁session,设置cookie立即过期,并覆盖原cookie,请求重定向到登录页面
其他
网站的免登陆是如何做到的?(了解)
方式一:首先想到的是使用cookie保存用户登录信息,设置有效期,在用户下次访问时免去登录环节,直接通过cookie获取用户信息。
方式二:直接将session会话保存,用户下次访问时,继续使用这个session。
相比之下session显得更加安全,但是,大家知道,session会随着浏览器的关闭而消失(确切的说,是在客户端消失,服务器端的session存活周期取决于相应配置),当用户下次启动浏览器,访问网站时,又会得到由网站自动分配的新的session。
那么,问题来了:如何做到
关闭浏览器后到下次登录时session仍然有效?
思路:
1、在用户登录成功时,创建session对象,保存用户信息
2、将此session的sessionid保存到cookie中
setcookie('session_id',session_id(),time()+3600*24,"/","https://localhost:8080");
3、同时将sessionid于session对应关系存储到应用域中,以便后面可以根据sessionid来获取到session
4、在用户关闭浏览器,重新打开浏览器访问网站时,读取用户的cookie,得到sessionid
if(isset($_COOKIE['session_id'])){
session_id($_COOKIE['session_id']);
}
5、根据sessionid获取到第3步存储到应用域中的session对象
6、从session中读取用户信息
服务端是根据 Cookie 中的信息判断用户是否登录,如果浏览器中禁止了 Cookie,如何保障整个机制的正常运转。(了解)
第一种方案,每次请求中都携带一个 SessionID 的参数,也可以 Post 的方式提交,也可以在请求的地址后面拼接 xxx?SessionID=123456...。
第二种方案,Token 机制。Token 机制多用于 App 客户端和服务器交互的模式,也可以用于 Web 端做用户状态管理。
Token 的意思是“令牌”,是服务端生成的一串字符串,作为客户端进行请求的一个标识。Token 机制和 Cookie 和 Session 的使用机制比较类似。
当用户第一次登录后,服务器根据提交的用户信息生成一个 Token,响应时将 Token 返回给客户端,以后客户端只需带上这个 Token 前来请求数据即可,无需再次登录验证。
cookie与session(了解)
1.什么是cookie
HTTP Cookie(也叫 Web Cookie或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。
2.cookie主要用于以下三个方面
会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
个性化设置(如用户自定义设置、主题等)
浏览器行为跟踪(如跟踪分析用户行为等)
3.什么是 Session
Session 代表着服务器和客户端一次会话的过程。Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当客户端关闭会话,或者 Session 超时失效时会话结束。
4.Cookie 和 Session 有什么差别?
作用范围不同,Cookie 保存在客户端(浏览器),Session 保存在服务器端。
存取方式的不同,Cookie 只能保存 ASCII,Session 可以存任意数据类型,一般情况下我们可以在 Session 中保持一些常用变量信息,比如说 UserId 等。
有效期不同,Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭或者 Session 超时都会失效。
隐私策略不同,Cookie 存储在客户端,比较容易遭到不法获取,早期有人将用户的登录名和密码存储在 Cookie 中导致信息被窃取;Session 存储在服务端,安全性相对 Cookie 要好一些。
存储大小不同, 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie。
5.服务端是根据 Cookie 中的信息判断用户是否登录,如果浏览器中禁止了 Cookie,如何保障整个机制的正常运转。
第一种方案,每次请求中都携带一个 SessionID 的参数,也可以 Post 的方式提交,也可以在请求的地址后面拼接 xxx?SessionID=123456...。
第二种方案,Token 机制。Token 机制多用于 App 客户端和服务器交互的模式,也可以用于 Web 端做用户状态管理。
Token 的意思是“令牌”,是服务端生成的一串字符串,作为客户端进行请求的一个标识。Token 机制和 Cookie 和 Session 的使用机制比较类似。
当用户第一次登录后,服务器根据提交的用户信息生成一个 Token,响应时将 Token 返回给客户端,以后客户端只需带上这个 Token 前来请求数据即可,无需再次登录验证。
详解地址:
https://blog.csdn.net/qq_42596046/article/details/123561142
### 信息管理模块
> 编辑按钮回显的具体流程
> 请求转发与重定向的区别
编辑按钮回显的具体流程
计网底层了解
https://zhuanlan.zhihu.com/p/452601285
请求转发与重定向的区别
https://www.cnblogs.com/haozihao/p/15388899.html
### 书籍管理模块
> 权限与授权如何实现的
> 分页展示如何实现的
权限与授权如何实现的
http://t.zoukankan.com/lilongsheng1125-p-4978552.html
https://www.bilibili.com/read/cv8718021/
分页展示如何实现的
https://blog.csdn.net/qq_60281421/article/details/124649237
详解
https://blog.csdn.net/qq_44985812/article/details/124191939
### 系统公告模块
> 菜单级别访问控制如何实现
> 编辑修改功能如何实现
菜单级别访问控制如何实现
编辑修改功能如何实现
https://blog.csdn.net/m0_52162006/article/details/124182235
如何实现批量删除与行工具栏删除
SpringAop的相关知识(要说Aop的所有知识)
### 借阅信息模块
> 编辑修改按钮如何实现回显
> 查询按钮的实现方式
编辑修改按钮如何实现回显
查询按钮的实现方式
子主题 1
了解
https://zhuanlan.zhihu.com/p/344650026
### 用户管理模块
> 编辑修改按钮如何实现回显
> 批量删除如何实现
> 全局异常处理如何实现
> 拦截器的实现方式
编辑修改按钮如何实现回显
批量删除如何实现
全局异常处理如何实现
拦截器的实现方式
https://blog.csdn.net/xpsharp/article/details/6890938
https://blog.csdn.net/reggergdsg/article/details/52962774
### 其他事项模块
> 这些字段中,哪些是唯一的
> 聊一聊对ajax的理解
这些字段中,哪些是唯一的
聊一聊对ajax的理解
https://blog.csdn.net/baidu_33438652/article/details/124652216
https://blog.csdn.net/zhi_Miss/article/details/50932018
https://blog.csdn.net/sisi_chen/article/details/92118350
SpringMVC请求的流程
基础
复习之前的内容
项目注意事项
前端
webapp下的WEB-INFO是默认无法被访问到的,所以要将资源文件转移出去
前端一些js文件里的方法可能会出现重名等冲突,此时要通过改名来处理,或者直接删掉静态资源然后重新copy
后端
maven资源过滤问题
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
或者项目的过滤器也可以
项目编码,编译环境问题
maven的pom里
项目结构里
项目的java编译器里
设置(setting)->java compile
设置里的file encode里面
过滤器里面设置编码过滤
总之要通篇的看一遍下来,这样才没有问题
target或out 通过构建生成的包
偶尔因为资源过滤或缓存的问题会导致无法识别,此时要先删除此文件夹然后通过小锤子重新构建
一些注解千万不能忘记,所以说有时候眼神很重要,一定要擦亮眼睛
项目路径问题,推荐使用相对路径,但如果相对路径发生冲突,则迫不得已使用绝对路径
图片过长,过大,或出现一些非常特殊的字符等等问题,待解决
TODO 日记类型主键自动增长,需要调整业务
mybatis+数据库
mybatis缓存问题会导致前端更新后查到的还是之前的内容,解决方案:
<!-- 关闭二级缓存-->
<setting name="cacheEnabled" value="false"/>
<!-- 设置一级缓存的作用范围是一条sql语句执行期间,执行完毕清空一级缓存-->
<setting name="localCacheScope" value="STATEMENT"/>
mybatis连接池数量问题,待解决
mybatis连接超过最大使用时间解决:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db_lezijie_note?useUnicode=true&characterEncoding=utf8&autoReconnect=true&serverTimezone=GMT%2B8&useSSL=false
mapper推荐使用单个绑定
<mapper resource="com/lezijie/note/mapper/NoteTypeMapper.xml"/>
<mapper resource="com/lezijie/note/mapper/UserMapper.xml"/>
<mapper resource="com/lezijie/note/mapper/NoteMapper.xml"/>
数据库
数据库各种配置要弄好
基础问题
对一些概念一定要熟知,基础非常的重要,例如某些类po,vo类是干啥的,一些逻辑是干啥用的等等
项目重难点:主要逻辑
关键逻辑
用户模块
简介
通过用户行为来区分 actionName
用户登录 actionName="login"
进入个人中心 actionName="userCenter"
加载头像 actionName="userHead"
验证昵称的唯一性 actionName="checkNick"
修改用户信息 actionName="updateUser"
用户退出 actionName="logout"
在UserServlet的service方法中
1.获取用户行为
String actionName = request.getParameter("actionName");
2. 判断用户行为,调用对应的方法
if ("login".equals(actionName)) {
// 用户登录
} else if ("userCenter".equals(actionName)) {
// 进入个人中心
}
一、用户登录
前端实现
1. 添加表单,设置表单属性
action="user" method="post" id="loginForm"
2. 设置表单元素的那么属性值,修改id属性值(首字母改小写)
设置文本框和密码框的那么属性值
设置复选框的那么属性值和value属性值(如果复选框未选中,在获取不到value属性值)
设置隐藏域,传递用户行为 (name="actionName" value="login")
3. 在 config.js 文件中,添加按钮的点击事件绑定的函数 checkLogin()
表单校验
1. 获取表单元素的值(姓名、密码)
2. 判断元素的值是否为空(引入util.js文件)
如果为空,设置提示信息 (通过设置span标签),并return
3. 如果不为空,则提交表单
后端实现
整体思路
1. 获取参数 (姓名、密码)
2. 参数的非空校验 (ResultInfo的封装类,用来封装响应结果 状态码、提示信息、返回的对象)
如果为空
设置ResultInfo对象的状态码和提示信息
将ResultInfo对象设置request作用域中
请求转发跳转到登录页面
return
3. 如果不为空,通过用户名查询用户对象
4. 判断用户对象是否为空
如果为空
设置ResultInfo对象的状态码和提示信息
将ResultInfo对象设置request作用域中
请求转发跳转到登录页面
return
5. 如果用户对象不为空,将数据库中查询到的用户对象的密码与前台传递的密码作比较 (将密码加密后再比较)
如果密码不正确
设置ResultInfo对象的状态码和提示信息
将ResultInfo对象设置request作用域中
请求转发跳转到登录页面
return
6. 如果密码正确
将用户信息存到session作用域中
判断用户是否选择记住密码(rem的值是1)
如果是,将用户姓名与密码存到cookie中,设置失效时间,并响应给客户端
如果否,清空原有的cookie对象
重定向跳转到index页面
分层思想
Web层
(控制层:接收参数、响应数据)
1. 获取参数 (姓名、密码)
2. 调用Service层的方法,返回ResultInfo对象
3. 判断是否登录成功
如果失败
将resultInfo对象设置到request作用域中
请求转发跳转到登录页面
如果成功
将用户信息设置到session作用域中
判断用户是否选择记住密码(rem的值是1)
如果是,将用户姓名与密码存到cookie中,设置失效时间,并响应给客户端
如果否,清空原有的cookie对象
重定向跳转到index页面
Service层
(业务逻辑层:参数判断、业务逻辑处理)
1. 判断参数是否为空
如果为空
设置ResultInfo对象的状态码和提示信息
返回resultInfo对象
2. 如果不为空,通过用户名查询用户对象
3. 判断用户对象是否为空
如果为空
设置ResultInfo对象的状态码和提示信息
返回resultInfo对象
4. 如果用户对象不为空,将数据库中查询到的用户对象的密码与前台传递的密码作比较 (将密码加密后再比较)
如果密码不正确
设置ResultInfo对象的状态码和提示信息
返回resultInfo对象
5. 如果密码正确
设置ResultInfo对象的状态码和提示信息
6. 返回resultInfo对象
Dao层
(数据访问层:数据库中的增删改查操作)
通过用户名查询用户对象, 返回用户对象
1. 获取数据库连接
2. 定义sql语句
3. 预编译
4. 设置参数
5. 执行查询,返回结果集
6. 判断并分析结果集
7. 关闭资源
二、非法访问拦截和免登录
非法访问拦截
拦截的资源:
所有资源 /*
需要被放行的资源:
1、指定页面,放行 (用户无需登录即可访问的页面;例如:登录页面login.jsp、注册页面register.jsp等)
2、静态资源,放行 (存放在statics目录下的资源;例如:js、css、images等)
3、指定行为,放行 (用户无需登录即可执行的操作;例如:登录操作user?actionName=login、注册操作等)
4、登录状态,放行 (判断session作用域中是否存在用户信息;存在就放行,不存在,就拦截跳转到登录页面)
免登录(自动登录)
通过Cookie和Session对象实现
什么时候使用免登录:
当用户处于未登录状态,且去请求需要登录才能访问的资源时,调用自动登录功能
目的:
让用户处于登录状态(自动调用登录方法)
实现:
从Cookie对象中获取用户的姓名与密码,自动执行登录操作
1、获取cookie数组
2、判断cookie数组
3、遍历cookie数组,获取指定的cookie对象 (name为user的cookie对象)
4、得到指定cookie对象的value (姓名与密码:userName-userPwd)
5、通过split()方法将value字符串转换成数组
6、从数组中别得到姓名和密码
7、重定向到登录操作 user?actionName=login&userName=姓名&userPwd=密码
return;
三、用户退出
前端:
设置超链接的请求地址 user?actionName=logout
后台:
1. 销毁Session对象
2. 删除Cookie对象
3. 重定向跳转到登录页面
四、进入个人中心
前台:
设置超链接的访问地址 user?actionName=userCenter
通过el表达式获取session作用域中的用户信息
后台:
1. 设置首页动态包含的页面值
2. 请求转发跳转到index.jsp
五、加载头像
前台:
设置img标签的src属性,请求后台加载头像
src="user?actionName=userHead&imageName=图片名称" (通过el表达式熊session中获取)
后台:
1. 获取参数 (图片名称)
2. 得到图片的存放路径 (request.getServletContext().getealPathR("/"))
3. 通过图片的完整路径,得到file对象
4. 通过截取,得到图片的后缀
5. 通过不同的图片后缀,设置不同的响应的类型
6. 利用FileUtils的copyFile()方法,将图片拷贝给浏览器
六、验证昵称的唯一性
前台:
前台:
昵称文本框的失焦事件 blur
1. 获取昵称文本框的值
2. 判断值是否为空
如果为空,提示用户,禁用按钮,并return
3. 判断昵称是否做了修改
从session作用域中获取用户昵称 (如果在js中想要使用el表达式获取域对象,js需要写在JSP页面中,无法在js文件中获取)
如果用户昵称与session中的昵称一致,则return
4. 如果昵称做了修改
发送ajax请求后台,验证昵称是否可用
如果不可用,提示用户,并禁用按钮
如果可用,清空提示信息,按钮可用
昵称文本框的聚焦事件 focus
1. 清空提示信息
2. 按钮可用
后台:
Web层:
1. 获取参数(昵称)
2. 从session作用域获取用户对象,得到用户ID
3. 调用Service层的方法,得到返回的结果
4. 通过字符输出流将结果响应给前台的ajax的回调函数
5. 关闭资源
Service层:
1. 判断昵称是否为空
如果为空,返回"0"
2. 调用Dao层,通过用户ID和昵称查询用户对象
3. 判断用户对象存在
存在,返回"0"
不存在,返回"1"
Dao层:
1. 定义SQL语句
通过用户ID查询除了当前登录用户之外是否有其他用户使用了该昵称
指定昵称 nick (前台传递的参数)
当前用户 userId (session作用域中的user对象)
String sql = "select * from tb_user where nick = ? and userId != ?";
2. 设置参数集合
3. 调用BaseDao的查询方法
七、修改用户信息
前台:
文件上传表单
1. 表单
表单类型 enctype="multipart/form-data"
提交方式 method="post"
2. 表单元素
设置name属性值(昵称、心情、头像)
设置隐藏域存放用户行为actionName
后台:
Web层:
注:文件上传必须在Servlet类上提那家注解!!! @MultipartConfig
1. 调用Service层的方法,传递request对象作为参数,返回resultInfo对象
2. 将resultInfo对象存到request作用域中
3. 请求转发跳转到个人中心页面 (user?actionName=userCenter)
Service层:
1. 获取参数(昵称、心情)
2. 参数的非空校验(判断必填参数非空)
如果昵称为空,将状态码和错误信息设置resultInfo对象中,返回resultInfo对象
3. 从session作用域中获取用户对象(获取用户对象中默认的头像)
4. 实现上上传文件
1. 获取Part对象 request.getPart("name"); name代表的是file文件域的name属性值
2. 通过Part对象获取上传文件的文件名
3. 判断文件名是否为空
4. 获取文件存放的路径 WEB-INF/upload/目录中
5. 上传文件到指定目录
5. 更新用户头像 (将原本用户对象中的默认头像设置为上传的文件名)
6. 调用Dao层的更新方法,返回受影响的行数
7. 判断受影响的行数
如果大于0,则修改成功;否则修改失败
8. 返回resultInfo对象
Dao层:
通过用户ID修改用户信息
1. 定义SQL语句
String sql = "update tb_user set nick = ?, mood = ?, head = ? where userId = ? ";
2. 设置参数集合
3. 调用BaseDao的更新方法,返回受影响的行数
4. 返回受影响的行数
类型模块
简介
用户行为 actionName
查询类型列表 actionName=list
删除类型 actionName=delete
添加/修改类型 actionName=addOrUpdate
一、查询类型列表
前台:
给导航栏的"类型管理"超链接设置href属性值
href="user?actionName=list"
设置导航栏的高亮值
后台通过请求域设置menu_page
接收响应数据
通过JSTl的if标签,判断类型集合是否存在
如果不存在,则显示对应的提示信息(暂未查询到类型数据!)
如果存在,通过JSTL的forEach标签进行遍历
注:使用JSTL标签需要导入对应的依赖与核心标签库
后台:
Web层:
1. 获取Session作用域设置的user对象
2. 调用Service层的查询方法,查询当前登录用户的类型集合,返回集合
3. 将类型列表设置到request请求域中
4. 设置首页动态包含的页面值
5. 请求转发跳转到index.jsp页面
Service层:
1. 调用Dao层的查询方法,通过用户ID查询类型集合
2. 返回类型集合
Dao层:
通过用户ID查询类型集合
1. 定义SQL语句
String sql = "select typeId,typeName,userId from tb_note_type where userId = ? ";
2. 设置参数列表
3. 调用BaseDao的查询方法,返回集合
4. 返回集合
二、删除类型
前台:
"删除"按钮绑定点击事件(传递参数:类型ID)
1. 弹出提示框,询问用户是否确认删除
2. 如果确认删除,发送ajax请求后台执行删除操作,返回resultInfo对象(类型ID)
如果删除失败,code=0
提示用户删除失败,msg=xxx
如果删除成功,code=1
准备工作:
1. 表格元素,设置id属性值 id="myTable"
2. 表格的父元素div元素,设置id属性值 id="myDiv"
3. 表格的每一个tr元素,设置id属性值 id="tr_类型ID" (循环设置每一个tr的id属性值)
4. 左侧类型分组的导航列表项li元素,设置id属性值 id="tr_类型ID"
1. 移除指定的tr记录
给table元素设置id属性值;给每一个tr添加id属性值
1. 通过id属性值,得到表格对象
2. 得到table元素的子元素tr的数量
3. 判断tr的数量 (判断是否有多条类型记录)
如果tr的数量等于2,表示只有一条类型记录,删除整个表格对象,并设置提示内容
如果tr的数量大于2,表示有多条类型记录,删除指定的tr对象
2. 删除左侧类型分组的导航列表项
1. 给li元素设置id属性值
2. 通过id选择器获取指定的li元素,并移除
后台:
Web层:
1. 接收参数(类型ID)
2. 调用Service的更新操作,返回ResultInfo对象
3. 将ResultInfo对象转换成JSON格式的字符串,响应给ajax的回调函数
Service层:
1. 判断参数是否为空
2. 调用Dao层,通过类型ID查询云记记录的数量
3. 如果云记数量大于0,说明存在子记录,不可删除
code=0,msg="该类型存在子记录,不可删除",返回resultInfo对象
4. 如果不存在子记录,调用Dao层的更新方法,通过类型ID删除指定的类型记录,返回受影响的行数
5. 判断受影响的行数是否大于0
大于0,code=1;否则,code=0,msg="删除失败"
6. 返回ResultInfo对象
Dao层:
通过类型ID查询云记记录的数量,返回云记数量
通过类型ID删除指定的类型记录,返回受影响的行数
三、添加/修改类型
前台:
准备工作:
1. 设置隐藏域,并设置id属性值,用来存放类型ID
2. 设置文本框,并设置id属性值,用来存放类型名称
a、打开添加模态框
绑定"添加类型"按钮的点击事件
1. 设置添加模态框的标题
2. 清空模态框中的文本框与隐藏域的值
3. 打开添加模态框
b、打开修改模态框
1. 设置修改模态框的标题
2. 将当前要修改的类型记录对应的类型id与类型名称,设置到模态框的隐藏域与文本框中
2.1 通过id属性值,得到要修改的tr记录 (id="tr_类型ID")
2.2 得到tr的具体的单元格
类型名称 第二个单元格,下标是1,tr的子元素中的第二个
类型ID 第一个单元格,下标是0,tr的子元素中的第一个
2.3 将类型ID与类型名称赋值给模态框的隐藏域与文本框
3. 利用模态框的ID属性值,调用show方法,打开模态框
c、模态框的"保存"按钮,绑定点击事件
【添加类型 或 修改类型】
1. 获取参数
添加操作:类型名称
修改操作:类型名称、类型ID
2. 判断参数是否为空(类型名称)
如果为空,提示信息,并return
3. 发送ajax请求后台,执行添加或修改功能,返回ResultInfo对象(通过类型ID是否为空来判断,如果为空,则为添加;如果不为空,则为修改)
判断是否更新成功
如果code=0,表示失败,提示用户失败
如果code=1,表示成功,执行DOM操作
1. 关闭模态框
2. 判断类型ID是否为空
如果为空,执行添加的DOM操作
1. 添加tr记录
1.1. 拼接tr标签
1.2. 通过id属性值,获取表格对象
1.3. 判断表格对象是否存在 (长度是否大于0)
1.4. 如果表格存在,将tr标签追加到表格对象中
1.5. 如果表格不存在,则拼接表格及tr标签,将整个表格追加到div中
2. 添加左侧类型分组导航栏的列表项
2.1. 拼接li元素
2.3 设置ul标签的id属性值,将li元素追加到ul中
如果不为空,执行修改的DOM操作
1. 修改指定tr记录
1. 通过id选择器,得到tr对象
2. 修改tr指定单元格的文本值
2. 修改左侧类型分组导航栏的列表项
给左侧类型名称设置span标签,并指定id属性值,修改span元素的文本值
后台:
Web层:
1. 接收参数 (类型名称、类型ID)
2. 获取Session作用域中的user对象,得到用户ID
3. 调用Service层的更新方法,返回ResultInfo对象
4. 将ResultInfo转换成JSON格式的字符串,响应给ajax的回调函数
Service层:
1. 判断参数是否为空 (类型名称)
如果为空,code=0,msg=xxx,返回ResultInfo对象
2. 调用Dao层,查询当前登录用户下,类型名称是否唯一,返回0或1
如果不可用,code=0,msg=xxx,返回ResultInfo对象
3. 判断类型ID是否为空
如果为空,调用Dao层的添加方法,返回主键 (前台页面需要显示添加成功之后的类型ID)
如果不为空,调用Dao层的修改方法,返回受影响的行数
4. 判断 主键/受影响的行数 是否大于0
如果大于0,则更新成功
code=1,result=主键
如果不大于0,则更新失败
code=0,msg=xxx
Dao层:
查询当前登录用户下,类型名称是否唯一
添加方法,返回主键
修改方法,返回受影响的行数
云记模块
简介
用户行为 actionName
进入发布云记页面 actionName=view
添加或修改云记 actionName=addOrUpdate
查询云记详情 actionName=detail
删除云记 actionName=delete
分页查询云记列表 actionName=list
一、进入发布云记页面
前台:
设置头部导航栏的"发布云记"超链接的href属性
href="note?actionName=view"
后台:
1. 从Session对象中获取用户对象
2. 通过用户ID查询对应的类型列表
3. 将类型列表设置到request请求域中
4. 设置首页动态包含的页面值
5. 请求转发跳转到index.jsp
二、加载富文本编辑器
1. 下载富文本编辑器的压缩包(UTF-8),解压压缩包
2. 将解压后的目录中对应的utf8-jsp文件夹,拷贝到项目中的statics目录下
3. 修改utf8-jsp文件夹的名称,设置为ueditor
4. 将ueditor目录下jsp目录下的lib目录中对应的jar包,在pom.xml中加载进来;(在WEB-INF目录下,新建lib目录,将json与ueditor的jar包拷贝进去 )
5. 修改配置文件
a. ueditor目录下的 ueditor.config.js 文件
设置编辑器资源文件的根路径 (相当于网站根路径的相对路径)
将20行的注释拷贝到22行
window.UEDITOR_HOME_URL = "/xxxx/xxxx/";
改成:
window.UEDITOR_HOME_URL = "/note/statics/ueditor/"; // /站点名/资源所在路径
b. ueditor目录下jsp目录中的 config.json 文件
修改第11行的图片访问前缀
"imageUrlPrefix": "", /* 图片访问路径前缀 */
改成:
"imageUrlPrefix": "/note", /* 图片访问路径前缀 */
修改第12行的图片保存路径
"imagePathFormat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
改成:
"imagePathFormat": "/statics/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
6. 使用富文本编辑器
1. 引入相关的JS文件 (先引入配置文件,再引入源码文件)
<!-- 配置文件 -->
<script type="text/javascript" src="statics/ueditor/ueditor.config.js"></script>
<!-- 编辑器源码文件 -->
<script type="text/javascript" src="statics/ueditor/ueditor.all.js"></script>
2. 准备容器 (准备一个元素,设置id属性值)
<!-- 加载富文本编辑器的容器 -->
<textarea id="content" name="content"></textarea>
3. 加载富文本编辑器
<script type="text/javascript">
$(function (){
// 加载富文本编辑器 UE.getEditor('容器Id');
var ue = UE.getEditor('content');
});
</script>
4. 自定义工具栏
ueditor.config.js 文件的toolbar属性中
三、发布云记
前台:
表单提交
设置表单的提交方式和提交地址
在隐藏域中设置用户行为actionName
设置表单元素的id与name属性值
表单校验
1. 获取表单元素的值
获取下拉框选中的选项 .val()
获取文本框的值 .val()
获取富文本编辑器的内容
ue.getContent() 获取富文本编辑器的内容(包含html标签)
ue.getContentTxt() 获取富文本编辑器的纯文本内容(不包含html标签)
2. 参数的非空判断
如果为空,提示用户,并return fasle
3. 如果参数不为空,则return true,提交表单
后台:
Web层:
1. 接收参数 (类型ID、标题、内容)
2. 调用Service层方法,返回resultInfo对象
3. 判断resultInfo的code值
如果code=1,表示成功
重定向跳转到首页 index
如果code=0,表示失败
将resultInfo对象设置到request作用域
请求转发跳转到note?actionName=view
Service层:
1. 设置回显对象 Note对象
2. 参数的非空判断
如果为空,code=0,msg=xxx,result=note对象,返回resultInfo对象
2. 调用Dao层,添加云记记录,返回受影响的行数
3. 判断受影响的行数
如果大于0,code=1
如果不大于0,code=0,msg=xxx,result=note对象
4. 返回resultInfo对象
Dao层:
添加云记,返回受影响的行数
四、分页查询云记列表
后台:
Web层:
1. 接收参数 (当前页、每页显示的数量)
2. 获取Session作用域中的user对象
3. 调用Service层查询方法,返回Page对象
4. 将page对象设置到request作用域中
5. 设置首页动态包含的页面值
6. 请求转发跳转到index.jsp
Service层:
1. 参数的非空校验
如果分页参数为空,则设置默认值
2. 查询当前登录用户的云记数量,返回总记录数 (long类型)
3. 判断总记录数是否大于0
4. 如果总记录数大于0,调用Page类的带参构造,得到其他分页参数的值,返回Page对象
5. 查询当前登录用户下当前页的数据列表,返回note集合
6. 将note集合设置到page对象中
7. 返回Page对象
Dao层:
查询当前登录用户的云记数量,返回总记录数
查询当前登录用户下当前页的数据列表,返回note集合
五、查询云记详情
后台:
Web层:
1. 接收参数 (noteId)
2. 调用Service层的查询方法,返回Note对象
3. 将Note对象设置到request请求域中
4. 设置首页动态包含的页面值
5. 请求转发跳转到index.jsp
Service层:
1. 参数的非空判断
2. 调用Dao层的查询,通过noteId查询note对象
3. 返回note对象
Dao层:
通过noteId查询note对象
六、删除云记
前台:
"删除"按钮绑定点击事件
1. 弹出提示框询问用户是否确认删除
2. 如果确认删除,则发送ajax请求
如果删除失败,提示用户
如果删除成功,跳转到首页
后台:
Web层:
1. 接收参数 (noteId)
2. 调用Service层删除方法,返回状态码 (1=成功,0=失败)
3. 通过流将结果响应给ajax的回调函数 (输出字符串)
Service层:
1. 判断参数
2. 调用Dao层的更新方法,返回受影响的行数
3. 判断受影响的行数是否大于0
如果大于0,返回1;否则返回0
Dao层:
通过noteId删除云记记录,返回受影响的行数
项目收获
BUG集锦
1.环境问题
(环境统一 jdk1.8 idea2020 maven3.6.2 xxxx)
Maven 环境集成
Mysql navicat idea c盘满了 d|e|f
2.非技术问题(绣花功夫)
前端js
var userName= $("#userName").val()
alret("123")
consoel.lgo("234234")
if("userName"==null || "userName"=="")
缓存(浏览器缓存)--->清除浏览器数据
tomcat插件|jetty插件(beyound compare)
3.技术问题-项目
I.思路清晰(先有思路)
1.准备表单
jsp--->{
userName
userPwd
button
}
2.提供登录servlet
1.接收参数
2.校验参数(用户名非空 密码非空)
3.根据用户名查找用户记录
4.校验用户记录是否存在
存在
不存在
5.存在 校验密码
正确
错误
6.密码正确
用户信息放入session
重定向到主页面
3.提供dao 实现用户查找
jdbc(6步)
II.参考课堂代码(遗忘的方法、遗忘的步骤)
III.debug 调试打断点
对Bug解决的能力
先定位Bug的位置,可以先看错误,再细想(非常关键),想通逻辑,再通过debug调试,main方法,test类测试,sout输出等进行详细定位,定位到一行或某几行代码,然后解决问题(常见,少写字,多字,或逻辑问题,未使用工具类,等等)
其他
主要功能
1. 用户模块
2. 类型模块
3. 云记模块
4. 云记主页
5. 报表统计
核心功能
3. 云记模块
项目内容
云R记
1. 用户模块
用户登录
退出登录
显示用户信息
加载头像
验证昵称的唯一性
上传头像
2. 类型模块
查询类型列表
验证类型名的唯一性
添加类型
修改类型
删除类型
3. 云记模块
分页查询云记列表
添加云记
修改云记
删除云记
4. 云记主页
标题查询云记列表
日期分组查询云记列表
类型分组查询云记列表
5. 报表统计
用户发布位置的散点图
云记发布月份的柱状图
后期发展与维护
0 条评论
下一页