自主练习:BBS电子布告栏

自主练习:BBS电子布告栏

工程地址:D:\BbsDemo;

技术参考:JSP+Servlet+MySQL;

开发工具:DBeaver客户端+Eclipse 2021

需求描述

编写一个留言界面,留言信息有标题和内容,标题不能为空。当往数据库中插入留言信息时,自动添加时间和留言者,时间为系统时间,留言者为当前登录用户,没有登录默认为游客

出处:《Java web程序设计》电子工业出版社 张磊丁香乾//第251页 练习3.E.2

准备工作

第一步:修改.classpath文件

修改正常情况下第三行为下图所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UsF4pEiQ-1655454307304)(C:\Users\Friedrich Hsing\AppData\Roaming\Typora\typora-user-images\image-20220419215218715.png)]

第二步:导入数据库连接jar包

image-20220419215610958

总体设计

数据库

userdetail记录用户信息,包含下列数据:id,name,pwd;

messdetail记录留言界面:tital,mainmess,username,time。key分别是id,tital。设计时注意mainmess可以为空,但是其他不能为空。

实体类

User和Mess类,分别对应数据库内容设计数据域和setting和getting方法。

数据库操作类

UserDAO:实现id与pwd写入数据库;判定id是否存在,pwd是否输入正确。

MessDAO:实现将信息写入数据库;在页面中输出信息。

具体界面和功能(附图是1.0软件结束后的效果图)

登录:logon.jsp+LogonServlet.java

​ 从表userdetail获取信息,进行比对。

登录界面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YwhU2RE3-1655454307306)(C:\Users\Friedrich Hsing\AppData\Roaming\Typora\typora-user-images\image-20220423232049208.png)]

注册:regist.jsp+RegistServlet.java

​ 将完整的user信息写入表userdetail中。

注册界面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BrQCAdPM-1655454307307)(C:\Users\Friedrich Hsing\AppData\Roaming\Typora\typora-user-images\image-20220423232117569.png)]

显示所有信息:main.jsp+MainServlet.java

​ 将表messdetail信息以一定格式全部输出。

显示页面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XLbVZmx9-1655454307308)(C:\Users\Friedrich Hsing\AppData\Roaming\Typora\typora-user-images\image-20220423232221124.png)]

用户输入信息:input.jsp+InputServlet.java

​ 将信息处理好后(加上user.getName()和time的信息)写入表messdetail中。

发布界面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pVAMFp2o-1655454307308)(C:\Users\Friedrich Hsing\AppData\Roaming\Typora\typora-user-images\image-20220424000510410.png)]


界面之间的镶嵌关系

web.xml设计中强调起点为登录界面logon.jsp,存在提交(button)------->跳转main.jsp

​ 注册(超链接)--------->跳转regist.jsp

注册成功之后跳转main.jsp。

main.jsp存在发布(超链接),----------->跳转input.jsp。

文件地址:

“D:\Friedrich Hsing’s Documents\Typora文档\自主练习:BBS电子布告栏设计阶段.pdf”

实现必要信息的传递

前文提出了一个必要的需求,实现信息定位到是哪个用户写的,这意味着在登陆界面logon.jsp实现将注册用户的信息写入request表头中,注册界面也要实现相应的功能。然后利用

request.getRequestDispatcher(“/a.jsp”).forward(request.response);

url地址不变,只能跳转到本web应用中的页面上。可以用request.setAttibute方法

实现重定位功能跳转main.jsp

更改:尝试直接写入Context中,发现这样子就不需要像上一个方案一样,需要重复的将信息写入表头。

正式开发第一阶段:围绕userdetail设计的功能

第一步,建包和建立文件,将已有储备的数据库操作类移植过来,实现过滤器,完成xml设置

其中**EncodeFilter.java实现中文字符编码设置功能。**在xml代码如下:

<filter>
  	<filter-name>EncodeFilter</filter-name>
  	<filter-class>com.filter.EncodeFilter</filter-class>
  </filter>

实现核心代码的逻辑如下,在doFilter()中实现设置请求和响应对应字符的代码:

request.setCharacterEncoding("utf-8");
response.setContentType("text/html; charset=utf-8");

注意设置初始页面为登陆界面:

  <welcome-file-list>
    <welcome-file>logon.jsp</welcome-file>
  </welcome-file-list>

注意在建立新的数据库bbs后,在web.xml设置关于数据库的信息在ServletConfig上,时候使用ServletContext.getInitParameter()实现数据库连接信息必要的获取。

<context-param>
  	<param-name>server</param-name>
  	<param-value>localhost</param-value>
  </context-param>
  <context-param>
  	<param-name>dbname</param-name>
  	<param-value>bbs</param-value>
  </context-param>
  <context-param>
  	<param-name>user</param-name>
  	<param-value>root</param-value>
  </context-param>
  <context-param>
  	<param-name>pwd</param-name>
  	<param-value>**********</param-value>
  </context-param>
第二步,实现两个简单的前端JSP文件,登录界面和注册界面,并基于AJAX技术,实现相应简单功能

分别实现logon.jsp&&regist.jsp两个文件,AJAX实现检验提交前是否存在信息没有输入。

注册页面regist.jsp文件在判断是否有信息没输入外,使用AJAX实现判断前后两次密码输入是否一致。

开发完成这两个简单的前端文件之后,检验,正常运行。

第三步,实现数据库表userdetail开发。

再dbeaver数据库可视化开发客户端中,建立新的数据库bbs,库中建立新的表userdetail。

userdetail含有三个属性:id,name,password//账号,用户名,密码;

实现并保存之后,可以开始实现数据库连接类的个性化开发。

第四步,基于数据库数据,实现实体类User,并根据User实现数据库连接类。

User类很容易实现,主要需要注意的是UserDao类

继承dboperate类(我自己开发的总数据库操作类)之后,依据这个类的方法实现下述两个功能:

添加用户和查询用户,实现这些功能具体信息如下:

AddUser()

INSERT INTO userdetail(id,pwd,name)value(?,?,?)

new String[] {user.getId(),user.getPwd(),user.getName()}

getUserById()

SELECT * FROM userdetail WHERE id=?

new String[] {id}

第五步,实现登录和注册的Servlet

先分析情景,登录过程中需要实现那些功能?

  1. 通过输入ID查找用户

  2. 用户不存在,返回提示

  3. 用户存在密码错误,返回提示

  4. 用户存在,密码正确,跳转main.jsp

注册没有什么特别注意的,单纯的将信息写入数据库中就行了

PS: 如果不使用try{}结构,则无法连接数据

具体逻辑(注册登录相同逻辑):

  1. 从前端JSP获取信息
  2. 连接数据库
  3. 操作数据库(写入信息或者搜索信息)
  4. 将User的信息写入ServletContext中
  5. 跳转main.jsp页面

下图是查阅的资料:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0fiFLfYi-1655454307309)(C:\Users\Friedrich Hsing\AppData\Roaming\Typora\typora-user-images\image-20220422104252971.png)]

写到一半追加增加设计:

regist.jsp实现相同邮箱不能重复注册,发生错误提示弹窗;

在实现注册功能的具体连接之前,使用自定义UserDao中getUserById方法获取数据库信息,如果用户存在,不予注册资格

原本使用方法:

		try {
			dao.getConn(server, dbname, owner, password);
			User tempuser=dao.getUserById("id");
			if(tempuser!=null) {//用户不存在
				boolean flag=dao.addUser(user);//添加用户
				if(flag) {
					System.out.println("AddUser Success!");
					request.setAttribute("userinformation", user);
					request.getRequestDispatcher("/main.jsp").forward(request,response);//跳转主页面
				}
			}else {//如果用户ID已经存在
				System.out.println("this id is in the System, AddUser failed!");//控制台输出bug
			}

后来查阅资料:发现主键数据可以自主实现对重复数据的判断,不需要格外代码实现:

主键是一列或一组列,用于唯一标识表中的每一行。为表定义主键时,必须遵循以下规则:

  • 主键必须包含唯一值。如果主键由多列组成,则这些列中的值组合必须是唯一的
  • 主键列不能包含NULL值。
  • 一表只有一个主键。

报出异常:java.sql.SQLIntegrityConstraintViolationException

能否将这种异常应用到程序中?使用代码:catch(SQLIntegrityConstraintViolationException e)

错误:Unreachable catch block for IOException. This exception is never thrown from the try statement body

意义:永远无法发生这类异常

这一段或许可以加上,不过一切都要等待一个工作的完成:servlet实现弹窗

正式开发第二阶段:messdetail实现信息发布和信息浏览

第一步,实现数据库表messdetail

由需求描述,表messdetail有tital,mainmess,username,sendtime

补充:sendtime有多种数据类型,最终选用timestamp数据类型。格外注意后面数据库中时间类型和代码中数据类型的一一对应。

第二步,实现数据库对应的实体类Mess,和数据库连接类MessDao

如同上文一样,Mess.java的设计没有什么需要额外注意的地方,但是MessDao的要求有所升级;

再将一条Mess写入数据库中,除了前端提供的tital和mainmess信息之外,获取系统的时间,登录者的名称名写入数据库。

不过这几步操作并没有打算再数据库连接类中实现,数据库连接类还是只实现AddMess()功能。

getAllMess()的方法实现获取所有信息,返回一个List<Mess>数据结构的数据。

注意:在应用executeUpdate等方法时,String[]中的数据数据类型不同的需要使用,“ ”+隔开

将数据库操作后返回结果封装入一个实体对象,并将实体对象返回至代码

Date sendtime属性我暂时使用getDate()方法,没有报错,看情况调整。

更改:Mess实体类对应的数据结构更改为:java.sql.Timestamp,来替换原来的java.util.Date

修改使用:getTimestamp()方法,可以正常运行。

第三步,先实现写入mess的操作,input.jsp及其对应的InputServlet.java

在设计input.jsp前,先恶补一下之前嫌麻烦没有学习的html设计文字框大小。

<input type="text" style="width:800px; height:20px;">

使用AJAX,第一次判断tital是否为空,弹窗提示。

用户信息在最顶栏显示。

//注意用户信息从登录界面logon.jsp之后,要再一次传送转送到main.jsp中

更改:改用context方案不需要这么麻烦,注意jsp镶嵌java代码<%%>中context对应application的JSP内置对象

在这里出现了bug

最初设计时犯了两个严重的错误。

第一个错误是:直接将User实体类的对象存放到了request中,其实并不需要那么多数据。但是长远来看,使用User实体类传输数据无疑更为安全可靠。之后的2.0版本可以将这个功能加上去。

第二个错误是不应该使用request,相比于request,context更为稳妥。request需要每一个页面都实现一次将信息写入request表头。

不过这也只是暂时的规划,之后参考一些已经投入应用的大型项目,看看他们用户信息都写在哪里。
最大的bug:时间数据结构bug

bug报错描述:Data truncation: Incorrect datetime value: ‘Fri Apr 22 21:16:46 CST 2022’ for column ‘sendtime’ at row 1

其实心里有数,指导是从数据库的设计起始,关于时间的存储标准一直很混乱。导致这种混乱一开始是源于java.util.Date和java.sql.Date两种数据结构傻傻分不清,原本打算是等到最后再统一标准,但现在看来要是不早准备好会导致连测试都做不了。

第一,确认数据库的数据类型,最后我选择了相对来说占存储空间小一点的timestamp数据结构。对应在java代码中的数据结构是:java.sql.Timestamp

第二,在主要实现功能的Servlet上,要实现最最最关键的步骤。

因为java代码只能(又或者是我技术不强?)通过java.util.Date实现获取服务器的系统时间。所以将系统时间插入数据库需要经过两个步骤:1.将系统时间通过java.util.Date获取 2.将这个时间转化成为数据库接受的对应的数据结构java.sql.Timestamp

具体代码如下:

Date utilDate = new Date();
Timestamp sqlDate = new Timestamp(utilDate.getTime());
第四步,实现展现所有数据库中所有信息,main.jsp与MainServlet.java的开发

小插曲:忽然tomcat报错404,清理tomcat工作日志无效之后,根据bug404出现时间初步判断是webapp与tomcat连接出错,将工程移除,重新添加,成功运行。

在main.jsp实现前端展现数据库所有信息时,我忽然想到在Mess.java中直接规定好Mess数据的信息输出模式,岂不是更加稳妥?

在Mess实体类中实现了一个返回String数据类型的toString()方法,下面是核心代码,因为有需要注意的地方单列如下:

result="<br>_______________________________________________________________________________________________-<br>发布人:"+getUsername()+"<br>发布时间:"+getSendtime()+"<br>-----------------------------------------------------------<br>发布主题:"+getTital()+"<br>-----------------------------------------------------------<br>发布内容:"+getMainmess();

可以正常运行,不过要注意使用<br>添加在字符串中,网页端才能显示换行,不要使用习惯上在控制台中的\n,达不到理想效果

问题1:Timestamp能否正常输出?可以,格式正常

一个注意点:

main.jsp在开始循环查找数据库之前一定要运行有下述代码:

<jsp:forward page="MainServlet"></jsp:forward>

实现main.jsp与MainServlet.java之间的连接。

<%
	List<Mess> messes=(List<Mess>)request.getAttribute("messes");
	if(messes==null){
%>
	<jsp:forward page="MainServlet"></jsp:forward>
<%} 
	for(int i=0;i<messes.size();i++)
	{
		Mess mess=messes.get(i);
		//输出这个mess
		%>
		<%=mess%>
		
		<%
	}
%>

至此,我们可以说是完成了主要的结构主干了。接下来就是优化系统了。

现阶段存在问题和2.0版本预计更新内容

问题1:

main.jsp中Mess的顺序是按照时间顺序来的,就是说最上面的信息是最初始的那一条,这样使用很不方便。

问题2:

需求中提到未登录用户(现在看来只有可能是调试阶段,检验后重新提交表单后不能存在登录失败的情况)游客显示,数据库中Null转化成游客的功能有所欠缺

问题3:

用户信息现阶段只有name,这存在一个原因是数据库设计时仅考虑了需求书上的内容,要封装整个User类进去,现在看来应该是可行的。

调试阶段出现bug,不过后面就没有出现这个bug了。信息重复输入数据库,第二条测试代码输入数据库操作两次。且第二次登录失败。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JIN8NFPY-1655454307310)(C:\Users\Friedrich Hsing\AppData\Roaming\Typora\typora-user-images\image-20220423230949377.png)]

总结之前的临时思考所写出的一些想法,2.0版本主要升级方面如下:

  1. AJAX实现main.jsp自主刷新;
  2. logon.jsp登录失败弹窗提示密码错误;
  3. regist.jsp实现相同邮箱不能重复注册,发生错误提示弹窗;
  4. ServletContext传递User本体,目的是增强系统整体的安全稳定性;//参考大型网站软件的代码
  5. main.jsp实现用户名超链接,实现进入个人主页;//构造全新的JSP和servlet
  6. 信息Mess实体类和数据库更新,实现封装id(方便确认发送对象),如果有可能实现封装User全部信息(数据库如何引入自定义类?)
  7. Messdetail数据库表,数据输出倒序输出
预计版本3.0更新对应功能:

User数据库及对应实体类升级,增加个人简介和头像功能;

Mess数据库实现Userinformation项目封装可以确认账户对象的具体方法

出现的未知知识,需要及时的补充:

使用Servlet实现弹窗;

数据库自定义数据类型;

AJAX,js与html之间互相传值

系统优化

1.实现数据库中的信息,新的信息排在上面,旧的信息排在后面

如何实现?一开始也是考虑从数据库角度出发。由于我起始没有系统的学过MySQL,所以我的第一反应是使用新的指令实现这一功能。

但是后来我考虑了一下,直接从List循环输出的角度考虑不久行了?

循环方式从:

for(int i=0;i<messes.size();i++)

修改为

for(int i=messes.size()-1;i>=0;i--)
2.实现User完整封装到ServletContext中

修改LogonServlet.java 48rank

​ RegistServlet.java 53rank中的java代码

修改input.jsp&main.jsp的application的JSP内置对象获得数据。

经检验成功运行。

3.未登录游客发言显示为游客

计划直接从显示端入手,即直接在Mess中的toString()方法中实现检验,如果没有name的信息则将Guest填进去,但是失败了。

启动第二个方案

在main.jsp&input.jsp先检验context中信息是否为null,如果是,填入预备好的Guest信息;如果不是,则正常运行。

正常运行。

升级2.0版本

利用AJAX异位同步实时刷新main.jsp界面

​ 主要应用到的技术是AJAX的异位同步

复习了相应的知识点,值得注意的地方是:

  1. 一个完整的AJAX同步异位系统包括下列三个核心JavaScript函数:

    1. 创建xmlHttpRequest对象的函数
      1. function createXMLHttpRequest()
      2. new ActiveXObject(“Microsoft.xmlHttp”)
      3. new XMLHttpRequest()
    2. 绑定状态触发器的函数,主要用于处理服务器返回的信息
      1. function processor()
      2. 判断正常条件下运行:xmlHttp.readyState4&&xmlHttp.status200
      3. var x=xmlHttp.responseText//获得服务器返回的响应文本信息
      4. document.getElementById(“id”).innerHTML=x//前端html显示内容
    3. 具体操作的函数
      1. function getX()
      2. 先调用创造createXMLHttpRequest()方法
      3. i++//区别化请求内容
      4. xmlHttp.open(“GET”,“Servlet?count=”+i);//请求发送至服务器
      5. xmlHttp.send(null)
  2. 除了三个主要的函数,还包括其他主体部分

    1. var xmlHttp;
    2. var i=0;
    3. setInterval(“function3();” ms);
  3. html区域需要注意:

    1. <div id=""></div>中展示AJAX中的内容
  4. Servlet注意

    1. out.println(“”);里面放着需要输出刷新的内容
    2. out.flush();
    3. out.close();
升级input.jsp,使之可以查看往期自己发布的信息

第一步,实现数据库操作类MessDao升级,实现方法getMessByUserName||getMessByUserId;

返回一个List。

第二步,实现一个新的Servlet实现这个功能

命名GetPersonalMessServlet.java,接入input.jsp

出现了bug

bug 1:发送新的Mess永远无法确认发送人

bug已修复,原因是因为没有即使的更新inputServlet中获取Context信息的代码。

bug2:input.jsp只显示最初是那一条信息

bug解决,因为把上文中一个循环while写成if了。

3.升级main.jsp,使用JSTL技术加持,增强代码的可维护性

在源程序中,大量使用了JSP镶嵌java代码的形式,为了保证效果,决定大量使用JSTL技术

导入包rmpl&&spec之后

修改核心代码为:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>	
<h1>欢迎!
	<%User user=(User)application.getAttribute("userinformation");%>
	<c:choose>
		<c:when test="${user==null}">GUEST</c:when>
		<c:when test="${user!=null}"><%=user.getName() %></c:when>
<a href="input.jsp">发布</a>
<br>
<h1>Crystal Palace社区主页</h1>
<br>
<%List<Mess> messes=(List<Mess>)request.getAttribute("messes");%>
<c:if test="${messes==null}">
	<jsp:forward page="MainServlet"></jsp:forward>
</c:if>
<c:forEach var="mess" items="${messes}" varStatus="status">
${mess}
</c:forEach>

查阅资料发现,<c:forEach>无法实现逆序查找,所以这一部分放弃现方案,还是应用原方案。

3.0版本预计更新内容

在2.0版本中原计划实现的功能只实现了ServletContext封装User,input.jsp页面实现查看之前发送的信息(类似个人界面),数据库自定义排列方式三个需求。

除了2.0版本原定要实现但是没有实现的功能外,3.0版本预计升级:

  1. 头像自定义//涉及到userdetail数据库层面的大规模工程升级
  2. 自主选择消息展现在main.jsp或者input.jsp的模式,支持顺序展示和逆序展示。//实现难度较小,尽可能不涉及servlet层面的结构。
  3. Messdetail数据库升级,实现id封装,通过id提升效率。
  4. Guest状态下,没有发布按钮
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值