留言,是博客系统的一个模块。当别人访问到自己的博客主页时,别人可选择查阅你的博客,可以给你博客评论,也可以给你发表留言。

要设计这个留言,就必须现有数据库。起初我们的数据库的设计是这样的。

  留言表1: 

  可以看出,这个留言表还是很有问题的:

1、第一个问题。这几个属性中的id、word_title、word_content、word_sdTime都应该没什么问题,都是一个留言表中必须的。有问题的是word_author,留言的作者,留言的作者应该是其它用户,这里采用的是字符,就是说写的是作者名字。这个作者名字,就是用户表的master_name。

 用户表:


我们的留言表就是通过word_author和用户表关联起来的。其实,这个word_author也是可以用word_authorID的。word_authroID就是用户表中的id,我们把这两个表通过这个id联系起来,这也是比较好一点的做法。

2、第二个问题就是,既然说留言了,那么我们的留言表的功能也太简单了,互动性不强,别人光给你留言,但是你却不能对这个留言说点什么。所以,我们这留言应该要有个回复的功能。但是这个表却无法支持我们的回复的功能。

策略1、当初我们考虑的是,在留言表里面加一个pid,这个pid就是表示回复的那一条留言的id。这样我们的结构就像下面这样:


这只是思考的一个策略。

策略2、后来又想想,我也可以加一个新表,用这个新表映射留言和回复的关系,新表有两个字段,一个是id,一个是pid,表示id是给pid的回复。然后原来的留言表就不用变动了。

后面想想,加多一个表,感觉多此一举呀。直接在留言表上加一个pid就简洁多了。

当我们写jsp的时候,把与当前登录的用户的留言表相关的数据查询出来后,我们要怎么组织这个留言,让其一条一条地显示出来,符合上下文逻辑呢。

由于我们的查出来的数据是按照时间排序的,所以应该不符合上下文逻辑。如果直接按照时间的顺序排列,那就看不出这一条回复是回复那一条留言的了。正因为有这个问题,我们就在查询出来之后,对这些数据进行重新排列,把回复放到留言下面。

不过后面还是没有用策略1的做法,到底为什么没用这就要说到第三个问题了。

3、第三个问题,我们有没有发现,这个留言表还缺了一个重要的东西,我们光有留言作者,但是我们没有留言的对象。我们这个留言到底是给谁的呀,因为在这个表中只有author,没有master,即留言的对象。不仅如此,有些其它表也有这个重大的失误。

所以后面我们又大改了一次数据库。

 这个留言表的最终设计是这样子的。

策略3、前面我们也说了,原来的表中只有author,没有master,所以我们把master加了进去。我们加了word_authorID和word_masterID并把这两个设置为外键。


  乍一看,这个表感觉还缺什么,没有pid,我们怎么处理回复问题?

后面想想,其实设计成这样,我们就不用加pid了,我们家pid不就是为了使我们显示留言或回复时,能够上下文衔接吗。

当一个用户登录时,那么它就是当前用户,设当前用户的id是x。当我们查询到与当前用户相关的留言数据后,我们的到的是要么word_authorID等于x的留言数据,要么word_masterID等于x的留言数据。就是说我们得到的数据是谁对x留言的数据,或x对谁留言的数据。

由于word_authorID和word_masterID其中肯定有一个,而且只有一个,是等于x的。我们何不用等于x那个来对这些数据进行分类,把相同的归为一类,然后同一类的按照时间进行排序,这样上下文顺序也就得出来了。最后我们只需要按照类别,按照排好的顺显示出来就ok了。

具体的代码如下实现:

先是replylist的产生

String URL=session.getAttribute("url").toString();
    List< WordBean> wordList=(List<WordBean>)request.getAttribute("wordList");
	//通过获取request里面的wordList参数,即可获得与当前用户相关的留言表的所有结果
    LinkedList<WordBean> replyList=new LinkedList<WordBean>();
	//用了存储把wordlist留言表按照类别,按照排好的顺序的数据排好的list,
	//即我们最终要在网页上显示的是replyList
	MasterBean masterBean = (MasterBean) session.getAttribute("masterBean");
	//获取当前用户的所有信息
	int id = masterBean.getId();
	//获取当前用户的id
	int replyID = 0;
	for (int i = 0; i < wordList.size(); i++) {
		WordBean wordBean = wordList.get(i);
		int masterID = wordBean.getMasterID();//获取这条留言的被留言者的id
		int authorID = wordBean.getAuthorID();//获取这条留言的留言作者的id
		if (masterID == id) {//通过与当前用户的id比较
			replyID = authorID;
			//若是masterID等于当前用户id,
			//那么authorID就是非当前用户的id,即留言作者的id
			//当前用户是被留言者
		} else {
			replyID = masterID;
			//或是authorID等于当前用户id,
			//那么masterID就是非当前用户的id,即被留言者的id
			//当前用户是留言者
		}
		//若这是留言表的第一条信息,就直接加入到回复队列里面
		if (replyList.size() == 0) {
			replyList.add(wordBean);
		} else {
			//对接下来留言进行判断,符合条件的就加入到replyList中
			int temp;
			for (temp = 0; temp < replyList.size(); temp++) {
				WordBean replyBean = replyList.get(temp);
				//找到插入的位置,找到后,退出循环
				if (replyBean.getAuthorID() == replyID || replyBean.getMasterID() == replyID) {
					//比较,找到与replyID相等的,则是同一类的,找到后,退出循环
					break;
				}
			}
			replyList.add(temp, wordBean);
			//找到了第一个replyID与AuthorID或MasterID相等的那
			//一条数据的位置,然后在这个位置上插入这一条数据,
			//这样同一类的都会放在相邻的位置,因为这些数据本来
			//也是按照时间的顺序来排好的,所以不需要再对其按照
			//时间又排一次,我们只需要找到位置,然后不断插入就
			//可以了。
			//而且若是找不到与replyID与AuthorID或MasterID相等的
			//数据,那么直到循环到temp等于replyList.size时,就会
			//退出,然后在这个temp上插入,表示找不到与replyID与
			//AuthorID或MasterID相等的数据,则在最后的位置插入。
		}
	}

然后我们的replylist的排列大概是这样(若m是自己的id,a,b,c是其它人的id):

a对m:####(这表示对话内容)

m对a:###

a对m:#####

b对m:######

m对b:###

c对m:##

m对c:####

c对m:######

我们的replylist按照m和a对话、m和b对话、m和c对话分成了三类,然后同类的放在相邻的位置。

我们看到红色的字,这些就是m和a、m和b、m和c对话的第一句话,我们的第一句话一般就是对话的开端,后面的都是回复,所以我们的代码这样处理。

<table>
		<%if(replyList.size()==0){ %>
			<tr>
				<td>该文章暂时还没有留言!!!</td>
			</tr>
		<%}else{
			int j;
			//显示留言列表
			for(int k=0;k<replyList.size();k=j){ 
			//前面的那个对话已经把其内容输出完后,
			//k直接从j开始,找出下一个对话的第一条留言
		    	WordBean wordBean=replyList.get(k);
			//先找出当前对话的第一条留言,这个就是对话的开端,即k位置
		    %>
				<tr>
					<td>留言作者:</td>	
					<td><input value="<%=wordBean.getAuthorID()%>"></td>
				</tr>
				
				<tr>
					<td>留言标题:</td>
					<td><input value="<%=wordBean.getWordTitle()%>"></td>
				</tr>
				
				<tr>
					<td>留言内容:</td>
					<td>
						<input value="<%=wordBean.getWordContent()%>">
					</td>
				</tr>
				
				<tr>
					<td>留言时间</td>
					<td><input value="<%=wordBean.getWordSdTime()%>"></td>
				</tr>
				<tr>
					<td>留言回复</td>
					<td>
					<%for(j=k+1;j<replyList.size();j++){ 
						WordBean reply=replyList.get(j);
						if((reply.getAuthorID()==wordBean.getMasterID() 
								&& reply.getMasterID()==wordBean.getAuthorID()) 
									||(reply.getAuthorID()==wordBean.getAuthorID() 
										&& reply.getMasterID()==wordBean.getMasterID())){
											//这里做一个判断,判断一下k位置以后的那些数据,
											//是不是也是和k位置那一条数据一样,也是同一类的数据,
											//即同一个对话的数据,若是,那么则输出
						%>
						<table>
							<tr>
								<td>回复标题:</td><td><%=reply.getWordTitle()%></td>
							</tr>
							<tr>
								<td><%=reply.getAuthorID() %>回复:</td><td><%=reply.getMasterID() %></td>
							</tr>
							<tr>
								<td>回复时间:</td><td><%=reply.getWordSdTime() %></td>
							</tr>
							<tr>
								<td>回复内容:</td><td><%=reply.getWordContent() %></td>
							</tr>
							
							<tr>
								<td background="<%=URL %>/images/front/line.jpg" colspan="4"></td>
							</tr>
						</table>
						<%}else{
							break;//若不是,那么退出循环
						}
					} %>
						</td>
					</tr>
				<tr>
					<td colspan="4">
						<a href="<%=URL%>/WordAction.action?action=reply&id=<%=wordBean.getId()%>">回复留言</a>
					</td>
				</tr>
				
				<tr>
					<td colspan="4">
						<a href="<%=URL%>/WordAction.action?action=delete&id=<%=wordBean.getId()%>">删除留言</a>
					</td>
				</tr>
				<tr>
					<td background="<%=URL%>/images/front/line.jpg" colspan="4"></td>
				</tr>
		    
			<%}
		  } %>
	</table>