jsp日记系统中的知识点
在jsp中取项目路径:
${pageContext.request.contextPath}
1.本地表单验证 加onsubmit 提交表单之前做验证 return true 则提交 return false 不提交;
<form name="myForm" class="form-signin" action="login" method="post" onsubmit="return checkForm()">
2.使用Properties 类 写数据库的参数配置
使用
Properties prop = new Properties();
InputStream in = new PropertiesUtil().getClass().getResourceAsStream("/diary.properties");
prop.load(in);
return (String)prop.getProperty(key);
3.MD5加密
MessageDigest md5 = MessageDigest.getInstance("md5");
BASE64Encoder base64en = new BASE64Encoder();//base64编码格式
return base64en.encode(md5.digest(str.getBytes("utf-8")));
4.设置用户cookie
实现记住密码和用户
public void rememberMe(String userName,String password,HttpServletResponse response){
Cookie user = new Cookie("user", userName+"-"+password);
user.setMaxAge(1*60*60*24*7);//设置存活时间为7天
response.addCookie(user);//通过response对象 返回给view层
}
前台获取cookie
if(request.getAttribute("user") == null){
String userName = null;
String password = null;
Cookie[] cookies = request.getCookies();
for(int i=0;cookies!=null && i<cookies.length;i++){
if(cookies[i].getName().equals("user")){
userName = cookies[i].getValue().split("-")[0];
password = cookies[i].getValue().split("-")[1];
}
}
if(userName == null){
userName = "";
}
if(password == null){
password = "";
}
pageContext.setAttribute("user", new User(userName,password));
}
5.写前台页面
布局采用bootstrap 的流式布局
<div class="container">//容器
<div class="row-fluid">//一行
<div class="span9">//9列
<div class="span3">//3列
解决图片文字不居中
.data_list .data_list_title img{
vertical-align: top;
}
6.用户请求过滤器Filter
创建HttpFilter 类 重写 doFilter方法
@Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpSession session = request.getSession();
Object obj = session.getAttribute("currentUser");
String path = request.getServletPath();
System.out.println(path);
if(obj == null && path.indexOf("login")<0 && path.indexOf("bootstrap")<0 && path.indexOf("images")<0){
response.sendRedirect("login.jsp");
}else{
chain.doFilter(request, response);
}
}
**request.getServletPath();**获取请求路径
str.indexOf(“字符串”) 返回指定字符串在当前字符串中的位置,未找到返回-1 找到
path.indexOf(“login”)<0 作用是设置过滤条件 当请求路径包含"login"时候放行,
配置web.xml文件 映射Filter 类
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>com.java1234.filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
7.日記列表显示
编写Diarydao的方法;
public List<Diary> diaryList(Connection con)throws Exception{
List<Diary> diaryList = new ArrayList<>();
StringBuffer sb = new StringBuffer("select * from t_diary t1,t_diaryType t2 where t1.typeId = t2.diaryTypeId");
sb.append( " order by t1.releaseDate desc");
PreparedStatement pstmt = con.prepareStatement(sb.toString());
ResultSet rs = pstmt.executeQuery();
while(rs.next()){
Diary diary = new Diary();
diary.setDiaryId(rs.getInt("diaryId"));
diary.setTitle(rs.getString("title"));
diary.setContent(rs.getString("content"));
diary.setReleaseDate(DateUtil.formatString(rs.getString("releaseDate"), "yyyy-MM-dd HH:mm:ss"));
diaryList.add(diary);
}
return diaryList;
}
查询Diary 表 返回集合List List知识详解参考简书文章
解决string 和Date 格式化问题
data 工具类格式化日期
将Date类型转化为字符串 使用 SimpleDateFormat类的 format()方法 传入指定格式的字符串即可 常见格式(“yyyy-MM-dd h: m:s”)
将字符串转为Date类型 使用SimpleDateFormat类的prase()方法 传入指定格式的字符串即可 常见格式(“yyyy-MM-dd h: m:s”)
public class StringUtil {
//字符串工具类
public static boolean isEmpty(String str){
if("".equals(str)|| str==null){
return true;
}else{
return false;
}
}
public static boolean isNotEmpty(String str){
if(!"".equals(str)&&str!=null){
return true;
}else{
return false;
}
}
}
public class DateUtil {
//data 工具类格式化日期
public static String formatDate(Date date,String format){
String result="";
SimpleDateFormat sdf=new SimpleDateFormat(format);
if(date!=null){
result=sdf.format(date);
}
return result;
}
public static Date formatString(String str,String format) throws Exception{
if(StringUtil.isEmpty(str)){
return null;
}
SimpleDateFormat sdf=new SimpleDateFormat(format);
return sdf.parse(str);
}
view层显示使用c标签遍历diaryList 并使用fmt标签格式化显示日期
<div class="diary_datas">
<ul>
<c:forEach items="${diaryList}" var="diary" >
<li>【<fmt:formatDate value="${diary.releaseDate}" type="date" pattern="yyyy-MM-dd"/>】<span> <a href="#">${diary.title }</a></span></li>
</c:forEach>
</ul>
css样式处理中的一些常用的属性
list-style-type: none;去除ul标签中的小点点
text-align: center;文本居中
8.分页
实现分页需要知道几个数据
1.当前页 page
2.每页的记录数 pageSize
3.起始页 start
创建pageBean
在查询Diary方法上添加参数pageBean
获取起始页的 算法 (page-1)*pageSize
在service 层创建一个pageBean 传入page(当前页)和pageSize(每页记录数)
在dao层根据pageBean的参数进行查询限制
设置sql语句限制查询的记录数使用limit起始记录,每页记录数
" limit "+pageBean.getStart()+","+pageBean.getPageSize()
在service层添加获取总记录数的方法getdiaryCount()
设计生成分页代码的方法genPageAction()传入总页数,当前页 ,每页记录数
分页中固定的两个位置为首页和尾页
<li><a href='main?page=1'>首页</a></li>
<li><a href='main?page="+totalPage+"'>尾页</a></li>
上一页的限制条件:
当当前页=1的时候上一页则不能被点击
当当前页!=1的时候上一页能被点击且 href中传入的page为当前页-1
具体实现:
if(currentPage==1){
pageCode.append("<li class='disabled'><a hred='#'>上一页</a></li>");
}else{
pageCode.append("<li><a href='main?page="+(currentPage-1)+"'>上一页</a></li>");
}
下一页与之相同
当当前页=总页数的时候 下一页不能被点击
当当前页!=总页数的时候 下一页能被点击且 href中传入的page为当前页+1
具体实现:
if(currentPage==totalPage){
pageCode.append("<li class='disabled'><a hred='#'>下一页</a></li>");
}else{
pageCode.append("<li><a href='main?page="+(currentPage+1)+"'>下一页</a></li>");
}
中间显示分页 使用for循环 设置对应的分页数
为什么要-2: 让当前页左边显示的分页数到当前页-2 处开始
为什么要+2:让当前页右边显示的分页数到当前页+2处结束
如当前页为6则显示为
4,5当前页(6),7,8
限制条件为 当i<1时或者i>总页数时 跳过 不执行添加操作
原因:当i<1时 并没有一个页面是负数的
当i>总页面数时 数据库并没有这么多数据 给你显示
并且设置当前页为被选中状态
for(int i=currentPage-2;i<=currentPage+2;i++){
if(i<1||i>totalPage){
continue;
}
if(i==currentPage){
pageCode.append("<li class='active'><a href='#'>"+i+"</a></li>");
}else{
pageCode.append("<li><a href='main?page="+i+"'>"+i+"</a></li>");
}
}
如 数据库有count =40 条记录
每页显示记录数5条
当前为第2页
则40/5 = 8 页 整除情况
若每页记录数为6
则40/6 = 6余4 应该在页面显示7页才能完整显示 所以 算法为
count%pageSize==o?count/pageSize:count/pageSize+1
9.日志归类 按类别
创建DiaryTypeBean
需要查询出日志类别表的所有数据所以需要使用到左/右连接查询
关键字 表1right join 表2 on 条件
sql :SELECT diaryTypeId,typeName,COUNT(diaryId) FROM t_diary RIGHT JOIN t_diaryType ON t_diary.typeId=t_diaryType.diaryTypeId GROUP BY typeName;
将查询到的结果存在Session 中
//View层通过<c:forEach >遍历出所有数据存显示
<ul>
<c:forEach var="diaryType" items="${diaryTypeCountList}">
<li><span><a href="#">${diaryType.typeName}(${diaryType.diaryCount })</a></span></li>
</c:forEach>
</ul>
按日期
数据库 sql
SELECT DATE_FORMAT(releaseDate,'%Y年%m月'),COUNT(*) AS diaryCount FROM t_diary GROUP BY DATE_FORMAT(releaseDate,'%Y年%m月')ORDER BY releaseDate DESC;
使用DATE_FORMAT(DATE,‘FORMAT’)对日期进行格式化
使用ORDER BY * DESC 进行排序
在DiaryBean 中添加 格式化后的日期属性 releaseDatestr 和 diaryCount 记录总数属性
在DiaryDao中编写 diaryCount() 获取根据日期并排序的记录
public List<Diary> diaryCountList(Connection con) throws Exception{
List<Diary> diaryList = new ArrayList<>();
String sql = "SELECT DATE_FORMAT(releaseDate,'%Y年%m月') as releaseDatestr,COUNT(*) AS diaryCount FROM t_diary GROUP BY DATE_FORMAT(releaseDate,'%Y年%m月')ORDER BY releaseDate DESC;";
PreparedStatement pstmt = con.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
while(rs.next()){
Diary diary = new Diary();
diary.setReleaseDatestr(rs.getString("releaseDatestr"));
diary.setDiaryCount(rs.getInt("diaryCount"));
diaryList.add(diary);
}
return diaryList;
}
在service层调用获取值后 同过session 保存
session.setAttribute("diaryCountList", diaryDao.diaryCountList(con));
view 层 通过<c:forEach>遍历出数据
<div class="datas">
<ul>
<c:forEach var="diaryCount" items="${diaryCountList}">
<li>
<span><a href="#">${diaryCount.releaseDatestr}(${diaryCount.diaryCount })</a></span>
</li>
</c:forEach>
</ul>
</div>
9.个人中心实现
功能1.显示用户图片 昵称 和 心情
在userDao中 获取数据库的以上数据 转发到前台 前台通过request获取
功能2.实现点击日志分类 后显示对应的日志信息
实现点击日期分类后显示对应的日志信息
实现:1.在显示日志分类的标签属性href上 添加请求 请求中包含要请求的日志类型id
<a href="main?s_typeId=${diaryType.diaryTypeId}">
在servlet中获取到请求的类型id
在查询所有日志数据dao中 添加日志类型id的限制条件 在diaryList()方法中添加一个diary参数
diaryList(Connection con,PageBean pageBean,Diary s_diary )
判断diary中diaryTypeId是否为空
若不为空 则添加查询限制条件 限制返回的数据
if(s_diary.getTypeId()!=-1){
sb.append(" and t1.typeId="+s_diary.getTypeId());
}
对于还要实现分页 所以必须限制查询到的所有记录数
在diaryCount()方法中添加diary参数
diaryCount(Connection con,Diary s_diary)
在分页时限制查到的数据
判断diary中diaryTypeId是否为空
if(s_diary.getTypeId()!=-1){
sb.append(" and t1.typeId="+s_diary.getTypeId());
}
若不为空 则添加查询限制条件 限制查询到的记录条数
在servlet处理中
判断当diaryTypeId 不为空时
根据从前台获取到的diaryTypeId 添加到Diary 对象中
并把diaryTypeId 的值存放到session中
并把获取到的日期session的值删除 实现当点击按类型分类查询时,按日期分类查询不冲突
session.removeAttribute(“str”)删除指定的session
if(StringUtil.isNotEmpty(s_typeId)){
diary.setTypeId(Integer.parseInt(s_typeId));
session.setAttribute(“s_typeId”, s_typeId);
session.removeAttribute(“s_releaseDatestr”);
}
当diaryTypeId 为空时就去session中查询 是否有diaryTypeId 的值
如果查询不为空值 则将diaryTypeId 添加到Diary 对象中
这样做的目的是当你在前台点击了按类型分类的日志时 当点对应的分页时查询到的内容还是为该类型的数据而不是其他或者查到空数据
if(StringUtil.isEmpty(s_typeId)){
Object o = session.getAttribute("s_typeId");
if(o != null){
diary.setTypeId(Integer.parseInt((String)o));
}
}
然后去调用查询所有数据
调用分页
调用转发到前台页面
按日期显示数据与上面一样
首先 在前台显示页面中 在日期类型列表的href中添加对应的请求日期releaseDatestr
<a href="main?s_releaseDatestr=${diaryCount.releaseDatestr }
在查询所有DiaryList()方法中添加判断。若Diary对象中的releaseDatestr的这个属性值不为空则在sql后面追加限制条件
if(StringUtil.isNotEmpty(s_diary.getReleaseDatestr())){
sb.append(" and DATE_FORMAT(t1.releaseDate,'%Y年%m月')='"+s_diary.getReleaseDatestr()+"'");
}
其中使用到了格式化日期 DATE_FORMAT()方法
返回限制查询后的所有数据
在分页中也添加对应限制条件 限制返回到 的所有数据条数
在servlet中通过request获取到前台传入的releaseDatestr值,
判断若不为空 并将其设置到Diary 对象中 并删除根据类型查询时设置的Session中的值
让当查询所有数据时 限制条件只有一个 这样就不好冲突
在这里如果前台传过来的releaseDatestr值有乱码则使用
String 类的编码格式化 new String(s_releaseDatestr.getBytes(“ISO-8859-1”),“UTF-8”);
if(StringUtil.isNotEmpty(s_releaseDatestr)){
//s_releaseDatestr = new String(s_releaseDatestr.getBytes("ISO-8859-1"),"UTF-8");
diary.setReleaseDatestr(s_releaseDatestr);
session.setAttribute("s_releaseDatestr", s_releaseDatestr);
session.removeAttribute("s_typeId");
}
继续添加当releaseDatestr为空时点击分页不会出现查询数据为查询全部数据
if(StringUtil.isEmpty(s_releaseDatestr)){
Object o = session.getAttribute("s_releaseDatestr");
if(o != null){
diary.setReleaseDatestr((String)o);
}
}
然后去调用查询所有数据
调用分页
调用转发到前台页面
10.实现搜索查询指定数据
在前台页面设置一个查询的表单 设置请求的路径为main?all=true;
并设置一个输入框 ,设置一个name 属性 在servlet中获取到 all 的值 和name 的值
判断是否为空
不为空时将要查询的title 设置在Diary对象中 并将其值设置到session中
将其他限制条件的Session 中的值移除
当为空时在session中查找是否有title属性的值 如果title不为空,则将title设置到Diary对象中
这样做的是当点击下一页时 也是查询的当前指定内容的记录
if("true".equals(all)){
if(StringUtil.isNotEmpty(s_title)){
diary.setTitle(s_title);
}
session.removeAttribute("s_releaseDatestr");
session.removeAttribute("s_typeId");
session.setAttribute("s_title", s_title);
}
在其他查询条件中添加移除session中title属性的值
if(StringUtil.isNotEmpty(s_typeId)){
diary.setTypeId(Integer.parseInt(s_typeId));
session.setAttribute("s_typeId", s_typeId);
session.removeAttribute("s_releaseDatestr");
session.removeAttribute("s_title");
}
在DiaryList中和DiaryCount (分页时查询指定条件的所有记录条数)添加 限制条件 为title 为模糊查询的条件
if(StringUtil.isNotEmpty(s_diary.getTitle())){
sb.append(" and t1.title like '%"+s_diary.getTitle()+"%'");
}
然后去调用查询所有数据
调用分页
调用转发到前台页面
11.日志显示
实现显示指定日志详情,在日志列表中的title 的href中加上指向具体的typeId参数和添加处理类型的属性action=show,
新建一个diarydao的新方法 DiaryShow 返回值为 Diary 类型 其中要返回的内容要包括
标题 发布日期 发布类型 具体内容
新建一个 servlet 用于处理各种Diary 的处理请求 根据传过来的参数 分配不同的方法处理 相当于一个中控 模板开发
在这个中控 的servlet中编写一个用于显示Diary的私有方法 然后将Diarydao中返回的数据使用request.attrbute()转发到前台显示
request.setAttribute("diary",diary);
request.setAttribute("mainPage", "diary/diaryShow.jsp");
request.getRequestDispatcher("mainTemp.jsp").forward(request, response);
新建一个showDiary.jsp 用于显示具体信息
转发过程为先请求到mainTemp模板页面,模板页通过jsp:inculd 将显示具体内容也面内容包括进模板页 然后渲染模板页里的Css和js 最终显示给用户
11.日志信息的添加
在这里将保存和修改操作存放在一个jsp中,通过传递的参数不同区分操作为添加Diary还是修改Diary,
在Diarydao中创建一个用于处理添加日记的addDiary的方法,通过前台页面表单传传入一个Diary类型的数据,添加成功后跳转到首页显示最新记录,添加失败将错误信息和Diary存放在request中转发到diarySave.jsp 显示
在这里用到了CKeditor 在线文本编辑插件
CKeditor 用法
1.引入
2.创建一个编辑器实例
在textere标签中引入CKeditor
表单验证
创建一个function checkForm 在表单 上添加一个οnsubmit="return checkForm()"提交的时候验证 返回flase则不提交
验证表单是否有内容,没有内容则提示添加
js中获取 CKeditor 中的内容使用
var content=CKEDITOR.instances.content.getData();
checkform如下
function checkForm(){
var title=document.getElementById("title").value;
var content=CKEDITOR.instances.content.getData();
var typeId=document.getElementById("typeId").value;
if(title==null||title==""){
document.getElementById("error").innerHTML="标题不能为空!";
return false;
}
if(content==null||content==""){
document.getElementById("error").innerHTML="内容不能为空!";
return false;
}
if(typeId==null||typeId==""){
document.getElementById("error").innerHTML="请选择日志类别!";
return false;
}
return true;
}
12.日志信息的修改
在Diarydao 中添加修改diary 的update方法 update t_diary set *=?..
在前台页面显示时根据请求的diaryId参数 是否为空在servlet中 选择 预处理显示为写日志还是修改日志,如果为修改操作 则还需在数据库中获取该日志的信息 并通过request.setattribute 设置值 并转发到修改页面
修改页面其实与添加日志的界面是一致的 只需要根据 diayId 是否为空切换一下 显示的标题头 在这里通过<c:chooes>标签 和<c:when><c:otherwise>标签选择即可
在内容框中通过jstl表达式获取数据库的内容并显示,
在这里 日志类型是用下拉框显示的在这里需要对下拉框进行判断 提高用户体验 显示当前diary对应的日志类型 使用三目表达式即可
<option value="${diaryTypeCount.diaryTypeId }" ${diaryTypeCount.diaryTypeId==diary.typeId ?'selected':'' } >${diaryTypeCount.typeName}</option>
13.日志分类管理
-
首先是需要显示全部的分类信息到页面上所以编写用于查询所有分类信息的dao
diaryTypeList 在这里 返回类型为diaryType类型的List -
编写用于处理的Servlet方法在这里需要针对前台页面提交的请求地址的不同编写不同的方法处理。在这里痛过请求/diaryType?action=preSave 中action的值的不同进行处理。在Servlet对应的处理方法中首先调用dao层查询出全部的diaryType信息,然后通过request.setAttribute 吧查询到的list给转发到前台页面 ,
-
编写用于显示的diaryTypeshow.jsp显示全部信息,在这里将全部信息放置在一个table表格中 并设置 修改和删除的操作, 通过<c:forEach>标签进行遍历显示全部信息
4.实现修改和添加diaryType的typeName ,首先编写dao层的方法updateDiaryType修改name字段的值通过diaryTypeId
和添加一个diaryType 根据typeName 的值String sql = "insert into t_diaryType values(null,?)"; String sql = "update t_diaryType set typeName=? where diaryTypeId=?";
前台页面请求修改diaryType的地址是
onclick="javascript:window.location='diaryType?action=preSave&diaryTypeId=${diaryType.diaryTypeId}'"
前台页面请求添加diaryType的地址是
onclick="javascript:window.location='diaryType?action=preSave'"
为了复用在Servlet中创建一个preSave方法通过根据diaryTypeId的值是否为空去判断是添加操作还是修改操作,如果是添加操作则直接转发到显示页面 让用户输入要添加的name,如果是修改操作,则还需要查询数据库 ,将要修改的数据放在request中转发到前台显示,
在修改或者添加完过后请求保存地址为action=Save 子在这里也通过根据diaryTypeId的值是否为空调用添加或者修改的方法
如果成功调用则显示到主页列表
request.getRequestDispatcher("diaryType?action=list").forward(request, response);
如果调用失败则将失败信息显示在显示页面
request.setAttribute("error", "添加或保存失败");
request.setAttribute("mainPage", "diaryTypePreSave");
request.getRequestDispatcher("mainTemp.jsp").forward(request, response);
14.个人信息
显示个人信息 ,需要获取在登录时设置在Session中的User的全部信息 然后编写用于处理User信息的Servlet ,这这个Servlet中创建一个用于显示User的方法用于转发到前台页面
编写前台页面userShow.jsp页面获取转发的信息,并显示,在userShow.jsp中包含的信息有:一个头像框,一个文件上传组件,和一个关于User信息的input框和textarea
在Dao层中编写用于修改的userUpdate ,在userShow.jsp中 给表单提交添加表单验证和提交地址提交地址为user?action=save,请求到Servlet层后根据action的参数不同使用Save方法进行处理,首先获取表单中的所有信息,上传的文件处理见下面的 上传图片 在Save方法中使用Dao中的userUpdate 进行处理
处理完毕后根据userUpdate返回的结果转发到显示页面 还是通过request.getRequestDispatcher 的方法转发到主页
15.上传图片
在表单中需要添加属性 enctype=“multipart/form-data” 才能上传文件
在servlet处理中,需要 通过HttpServletRequest对象获取RequestContext对象
才能使用文件上传 HttpservletRequset不行 必须通过new ServletRequestContext(request)获取RequestContext对象。
解决方法:
上传文件时获取文件FileItem,通过new ServletRequestContext(request)获取RequestContext对象。
List items = upload.parseRequest(new ServletRequestContext(request));
//上传文件需要用到
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> items=null;
try {
items=upload.parseRequest(new ServletRequestContext(request));
} catch (FileUploadException e) {
e.printStackTrace();
}
Iterator<FileItem> itr = items.iterator();
boolean imageChange = false;
HttpSession session = request.getSession();
User user = (User)session.getAttribute("currentUser");
while(itr.hasNext()){
FileItem item = itr.next();
if(item.isFormField()){//获取普通表单
String fieldName = item.getFieldName();//获取字段名称
if("nickName".equals(fieldName)){
user.setNickName(item.getString("UTF-8"));
}
if("mood".equals(fieldName)){
user.setMood(item.getString("UTF-8"));
}
}else if(!"".equals(item.getName())){//用于上传文件用的获取表单信息
try {
imageChange = true;
String imageName = DateUtil.getCurrentDateStr();//获取用于生成文件名的方法
user.setImageName(imageName+"."+item.getName().split("\\.")[1]);
String filePath = PropertiesUtil.getValue("imagePath")+imageName+"."+item.getName().split("\\.")[1];
item.write(new File(filePath));//写出文件
} catch (Exception e) {
e.printStackTrace();
}
}
}
eclipse生成war包,部署在tomcat上,报错404
解决上传图片 后必须要刷新项目才能刷新出新的图片
将上传图片的路径不放在项目路径中,放在D盘其他位置 通过添加tomcat server.xml 的映射路径 增加一个用于访问外部路径的请求 如下
1。解决方案,配置tomcat的server.xml在host节点下配置:
<Context path="/upload" docBase="D:\uploadImage" reloadable="true"></Context>
path:项目的请求路径 docBasc:映射到实际硬盘的位置
在上传文件时 上传到 docBase 对应的路径
imagePath=D:\\uploadImage\ \Properties配置文件中 设置文件存放路径
String filePath = PropertiesUtil.getValue("imagePath")+imageName+"."+item.getName().split("\\.")[1];
item.write(new File(filePath));//写出文件
前端页面显示时在src中添加请求路径 src="/upload${currentUser.imageName} "
最重要 要重启eclipse 才能生效