OA的学习--第七天的内容--论坛模块

来源:互联网 发布:tl wn725n mac驱动 编辑:程序博客网 时间:2024/05/01 09:05
    OA第七天的学习,是从第64集开始到81集,共18集.终于快结束了,真真是不容易啊.废话不多说,还是早弄完早了吧.第七天的主要内容就是完成论坛模块,其中比较重要的几个点就是,查询和发布帖子/主题/版块,以及排序等的问题,但是这些和基本的增删改查还是比较接近的,所以除了排序基本不介绍了.然后就是用FCKeditor网页编辑器发帖,还有就是分页了,分页有两种,一种是最简单的分页,还有一种是带条件的分页,并且最后按照惯例,都进行了封装,达到代码复用的效果.

排序

    排序的要求是这样子的,首先有3种帖子,一种置顶,一种精华,一种普通.置顶贴一定是放在最前面,但是多个置顶贴的先后顺序是按更新时间来排序的;精华帖是不影响顺序的,和普通帖一样,按更新时间来.
/** * 查询主题列表 *  */@Deprecatedpublic List<Topic> findByForum(Forum forum) {//排序:所有置顶贴在最上面,并按最后更新时间排序,让新状态的在最上面;但是精华帖不影响顺序//1.查询2次,先查置顶再查精华//2.将置顶帖作为字段,来解决//3.如何让他任务0和1是一样的,而2是不一样的,return getSession().createQuery(// //TODO:怎么排序"FROM Topic t WHERE t.forum=? ORDER BY (CASE t.type WHEN 2 THEN 2 ELSE 0 END) DESC,t.lastUpdateTime DESC")//.setParameter(0,forum).list();}
    核心就是写这么一句查询语句:FROM Topic t WHERE t.forum=? ORDER BY (CASE t.type WHEN 2 THEN 2 ELSE 0 END) DESC,t.lastUpdateTime DESC").其中整体是按更新时间倒序排序,所以最新的在最上面;然后还有根据 (CASE t.type WHEN 2 THEN 2 ELSE 0 END) DESC 排序排序,其中t.type中,普通贴的type为0,精华为1,而置顶为2.所以只有置顶贴为2,而普通帖和精华帖为0,然后按照2和0倒序排序,则置顶贴放在最前面.

使用FCKeditor发帖

    首先看下效果,就是这样的一个工具.可以改变输入文字的样式,插入图片.若是没有这个我们可以自己写HTML代码加上样式.

    使用网页编辑器FCKeditor发帖,就是要准备FCKeditor相关包,放到项目中.做法就是,导入js文件,然后准备一个地方放FCKeditor,最后就是写js代码显示FCKeditor.FCKeditor的创建有两种,一种直接create创建,一种replaceTextarea.

    只是现在用的FCKeditor,工具栏有很多不需要的,以及表情太少了.如何自定义自己的FCKeditor工具栏.在FCKeditor的配置文件fckconfig.js中,添加自定义的一种工具栏.然后设置ToolbarSet为bbs就可以了.

    但是直接修改fckconfig.js文件不是很好,可以再写一个自定义的配置文件,然后先加载默认的,然后加载自定义的,自定义的就可以覆盖默认的.所以在fckconfig.js中加上自定义配置文件myconfig.js.

    然后在myconfig.js,添加ToolBarSet,以及对于图片表情,给一个路径,以及图片名.

简单分页

    简单分页的效果就是在这样,基本上加上jquery.pagination.js分页插件,然后再写些真分页的查询语句就可以了,对于这里还做了转到页码,以及显示最多显示前后共10个页码.这里的问题是,pageSize没有地方设置,所以写死了.

    先分析分页需要做的事,以及写好大概的代码,并且列出需要的参数.其中分页的记录是recordList,而控制显示最多显示前后共10个页码,由于需要计算,所以直接让传递算好的值beginPageIndex和endPageIndex。最后转到的下拉列表中的值,根据总页数从1开始遍历生成,而gotoPage的功能实现需要写js代码控制。
   
    然后封装一个PageBean实体,封装分页需要的所有数据.并将这些数据进行分类,一种是前台指定,接收前台的值就可以;一种是查询数据库,得出确切的值;还有就是根据上面的两种数据,可以计算得到的数据.而且在构造函数中,通过接收前4个属性,来自动计算后3个属性.
/** * 分页功能中一页的信息 * @author liu * */public class PageBean {//指定或是页面参数private intcurrentPage;  // 当前页;private intpageSize;         // 每页显示多少条//查询数据库private intrecordCount;      // 总记录数private List recordList;   // 本页的数据列表;//计算private intpageCount;        // 总页数;private intbeginPageIndex;   // 页码列表的开始索引private int endPageIndex;     // 页码列表的结束索引 public PageBean() {};/** * 只接收前4个必要的shuxing,会自动计算其他3个属性的值 * @param currentPage * @param pageSize * @param recordCount * @param recordList */public PageBean(int currentPage, int pageSize, int recordCount,List recordList) {this.currentPage = currentPage;this.pageSize = pageSize;this.recordCount = recordCount;this.recordList = recordList;// 计算总页码 (若有余数,加一页)pageCount = (recordCount + pageSize -1)/pageSize;// 计算beginPageIndex 和end PageIndex// 总页数不多于10页,则全部显示if (pageCount <= 10) {beginPageIndex = 1;endPageIndex = pageCount;}// 总页数多于10页,则显示当前页附近的共10个页面else {   // 当前页附近的共10个页码(前4个 + 当前页 + 后5个)   beginPageIndex = currentPage - 4;   endPageIndex = currentPage + 5;   // 当前面的页码不足4个时,则显示最前面10个页码,1-10   if (beginPageIndex < 1) {   beginPageIndex = 1;   endPageIndex = 10;   }   // 当后面的页码不足5个时,则显示最后10个页码,倒数第10个-倒数第1个   if (endPageIndex > pageCount) {   endPageIndex = pageCount;   beginPageIndex = pageCount - 10 + 1;   }}}           …}
    最后形成的结构就是这样,对于JSP,Action和Service分析各做了什么?
    对于jsp就是在页面上显示这些数据,而Action,需要接收jsp传递的参数,然后调用service的方法获取到pageBean,最后回显到jsp中,(实现时为了显示方便,所以pageBean放到了对象栈中).而service提供getPageBean的方法,先是查询出符合条件的记录,然后与查询总记录数,最后调用pageBean的构造函数,传递4个属性,计算3个属性,最后返回的pageBean中7个属性就会填充好.


    首先可以看下JSP的代码,最开始的时候,是每个页面都写一份这样的代码,后来由于有很多分页功能的页面,所以优化,提取和封装了这样一段jsp片段pageView.jspf.
    那么,在JSP页面中需要写什么,才能引入这个分页片段.需要include分页文件,然后要设置一个form用于提交,因为转到的封装需要用到这个.

<!--分页信息--><%@ include file="/WEB-INF/jsp/public/pageView.jspf" %><s:form action="topic_show?id=%{id}"></s:form>
    然后再看下pageView.jspf片段文件的内容,首先需要struts的标签和编码utf-8.然后就可是开始建立自定义的分页控件了,里面的样式由pageCommon.css提供.
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%><%@ taglib prefix="s" uri="/struts-tags" %><div id=PageSelectorBar><div id=PageSelectorMemo>页次:${currentPage}/${pageCount }页  每页显示:${pageSize }条  总记录数:${recordCount }条</div><div id=PageSelectorSelectorArea><a href="javascript: gotoPage(1)" title="首页" style="cursor: hand;"><img src="${pageContext.request.contextPath}/style/blue/images/pageSelector/firstPage.png"/></a><s:iterator begin="%{beginPageIndex}" end="%{endPageIndex}" var="num"><s:if test="#num == currentPage"> <%-- 当前页 --%><span class="PageSelectorNum PageSelectorSelected">${num}</span></s:if><s:else> <%-- 非当前页 --%><span class="PageSelectorNum" style="cursor: hand;" onClick="gotoPage(${num});">${num}</span></s:else></s:iterator><a href="javascript: gotoPage(${pageCount})" title="尾页" style="cursor: hand;"><img src="${pageContext.request.contextPath}/style/blue/images/pageSelector/lastPage.png"/></a> 转到:<select onchange="gotoPage(this.value)" id="_pn"><s:iterator begin="1" end="%{pageCount}" var="num"><option value="${num}">${num}</option></s:iterator></select><script type="text/javascript">$("#_pn").val("${currentPage}");</script></div></div>

<script type="text/javascript">function gotoPage( pageNum ){            //方法1:路径写死,不能复用// window.location.href = "forum_show.action?id=${id}&pageNum=" + pageNum;//方法2:在form中中添加隐藏域,记录pageNum值,然后提交$(document.forms[0]).append("<input type='hidden' name='pageNum' value='" + pageNum +"'>");document.forms[0].submit();}</script>

    其中,当前页码的样式有PageSelectorSelected,选中样式.首页和尾页是<<和>>图片显示,并且所有都是调用gotoPage,转到页面的js方法.

    而gotoPage的js跳转代码,之前用window.location.href的方式,写死路径,不能复用;所以改成在页面的form中加隐藏域,并提交.所以js页面一定要有为分页写一个form,而且若页面中有多个form,则该form必须放在最上面.
    接着Action的代码,由于多个页面都有分页功能,所以优化,所以将pageNum和pageSize放在BaseAction中了,但是实际这应该是页面接收过来的.
    然后分页最开始是在replyService中写了一个getPageBeanByTopic方法.后来由于带参数的分页中也有这么一段,于是优化写了一个getPageBean的传hql语句的方法(在下面的带参数分页中会介绍).

public abstract class BaseAction<T> extends ActionSupport implements ModelDriven<T> {            …protected int pageNum = 1; // 当前页protected int pageSize = 10; //每页显示多少条记录…}
public class TopicAction extends BaseAction<Topic> {/** * 显示单个主题(主贴+回帖列表) * @return * @throws Exception */public String show() throws Exception {//准备TopicTopic topic = topicService.getById(model.getId());ActionContext.getContext().put("topic",topic);//准备分页信息v1PageBean pageBean = replyService.getPageBeanByTopic(pageNum,pageSize,topic);ActionContext.getContext().getValueStack().push(pageBean);//准备分页信息v2//String hql = "FROM Reply r WHERE r.topic = ? ORDER BY r.postTime ASC";//List<Object> parameters = new ArrayList<Object>();//parameters.add(topic);////PageBean pageBean = replyService.getPageBean(pageNum,pageSize,hql,parameters);// ActionContext.getContext().getValueStack().push(pageBean);        return "show";}}
    Service层的代码
public class ReplyServiceImpl extends DaoSupportImpl<Reply> implements ReplyService {           /** * 查询分页信息 * @param pageNum * @param pageSize * @param topic * @return */public PageBean getPageBeanByTopic(int pageNum, int pageSize, Topic topic) {//查询所有回复记录List list = getSession().createQuery(//"FROM Reply r WHERE r.topic = ? ORDER BY r.postTime ASC")//.setParameter(0,topic)//.setFirstResult((pageNum -1) * pageSize)//.setMaxResults(pageSize).list();//查询总记录数Long count = (Long) getSession().createQuery(//"SELECT COUNT(*) FROM Reply r WHERE r.topic = ? ")// .setParameter(0,topic)//.uniqueResult();return new PageBean(pageNum,pageSize,count.intValue(),list);}       }     
    这样简单分页就做好了.然后看看带条件的分页

带条件分页

    先看下带条件分页的效果.就是这样,在简单的分页插件上面,有3个下拉框和一个提交按钮.其中第一个下拉框,可以选择主题(帖子)的类型,是全部显示,还是只显示精华.第二个是控制排序的条件,其中默认排序的话,置顶贴在最前面;还有只按最后更新时间,以及只按回复数量,还有只按主题发表时间;最后一个下拉框就是选择降序还是升序.最后只有点击提交才有效果.

    首页,做法是和上面其实差不多,都要引入分页插件,但是这里还要多写些下拉框和按钮,然后对于后台,之前只要papeSize和当前页,现在还需要接收前台的参数,最后根据参数拼凑sql语句,查询出结果,返回.
    先看下JSP的代码,主要就是用s:select标签的list属性,写好各个下拉框中的value和text,还有提交按钮.以及引入pageView.jspf文件.而提交会提交到forum_show,并且会传递版块的id.
<s:form action="forum_show?id=%{id}"><div id="MainArea"><div id="PageHead"></div><center> ...<div class="ForumPageTableBorder"> ...<!--其他操作--><div id="TableTail"><div id="TableTail_inside"><table border="0" cellspacing="0" cellpadding="0" height="100%" align="left"><tr valign=bottom><td></td><td><s:select name="viewType" list="#{0:'全部主题', 1:'全部精华贴'}"/><s:select name="orderBy" onchange="onSortByChange(this.value)"list="#{0:'默认排序(所有置顶帖在前面,并按最后更新时间降序排列)', 1:'只按最后更新时间排序', 2:'只按主题发表时间排序', 3:'只按回复数量排序'}"/><s:select name="asc" list="#{false:'降序', true:'升序'}"/><input type="IMAGE" src="${pageContext.request.contextPath}/style/blue/images/button/submit.PNG" align="ABSMIDDLE"/></td></tr></table></div></div></div></center></div></s:form><!--分页信息--><%@ include file="/WEB-INF/jsp/public/pageView.jspf" %>
    然后 Action的代码,没有建立字典表,自己定义3个属性,接收前台下拉框中的值.然后直接写hql语句,来查询.最后调用service的getPageBean方法,查询出结果,并放到对象栈中回显到前台
/** * 版块 * @author liu * */@Controller@Scope("prototype")public class ForumAction extends BaseAction<Forum> {  /** * 0:查看全部主题 ;  * 1:只查看精华帖 */private int viewType = 0;/** * 0.默认排序(所有置顶帖在前面,并按最后更新时间降序排列, * 1:'只按最后更新时间排序',  * 2:'只按主题发表时间排序',  * 3:'只按回复数量排序'}" */private int orderBy = 0;/** * true:表示升序 * false:表示降序 */private boolean asc = false; /** * 显示单个版块(主题列表) * @return * @throws Exception */public String show() throws Exception { //准备数据,forumForum forum = forumService.getById(model.getId());ActionContext.getContext().put("forum",forum);                      //准备分页信息v3 拼接String hql = " FROM Topic t WHERE t.forum = ? ";List<Object> parameters = new ArrayList<Object>();parameters.add(forum);if(viewType == 1) { //1.表示只看精华帖hql = hql + " AND t.type = ? ";parameters.add(Topic.TYPE_BEST);}if (orderBy == 1) {// 1:'只按最后更新时间排序', hql = hql + " ORDER BY t.lastUpdateTime " + (asc ? "ASC" : "DESC");}else if(orderBy == 2){// 2:'只按主题发表时间排序', hql = hql + " ORDER BY t.postTime " + (asc ? "ASC" : "DESC");}else if(orderBy == 3){// 3:'只按回复数量排序'}"hql = hql + " ORDER BY t.replyCount " + (asc ? "ASC" : "DESC");}else {// 0.默认排序(所有置顶帖在前面,并按最后更新时间降序排列,hql = hql + " ORDER BY (CASE t.type WHEN 2 THEN 2 ELSE 0 END) DESC,t.lastUpdateTime DESC";}PageBean pageBean = replyService.getPageBean(pageNum,pageSize,hql,parameters);                       ActionContext.getContext().getValueStack().push(pageBean);return "show";}        …       …//getter,setter}

    最后,就是Service的代码.为了提取优化,所以将getPageBeanByTopic和getPageBeanByForum提取为getPageBean放在了DaoSupportImpl中。先是查询出所有的Topic主题(帖子)记录,然后填充条件,查询,查询出需要的分页后的记录,以及查询出总记录数,最后就是PageBean的构造方法了.

@Service@Transactional@SuppressWarnings("unchecked")public class ReplyServiceImpl extends DaoSupportImpl<Reply> implements ReplyService {     ....}

@Transactional  //注解可以继承@SuppressWarnings("unchecked")public class DaoSupportImpl<T> implements DaoSupport<T> {/** * 公共的查询分页信息方法 */@Deprecatedpublic PageBean getPageBean(int pageNum, int pageSize, String hql,List<Object> parameters) {System.out.println("---------->DAOSupportImpl.getPageBean()");//查询所有记录Query listQuery = getSession().createQuery(hql); //创建查询对象if (parameters != null) { //设置参数for (int i = 0; i < parameters.size(); i ++ ) {listQuery.setParameter(i,parameters.get(i));}}listQuery.setFirstResult((pageNum -1) * pageSize);listQuery.setMaxResults(pageSize);List list = listQuery.list();//查询总记录数Query countQuery = getSession().createQuery("SELECT COUNT(*)" + hql);if (parameters != null) { //设置参数for (int i = 0; i < parameters.size(); i ++ ) {countQuery.setParameter(i,parameters.get(i));}}Long count = (Long) countQuery.uniqueResult(); //执行查询return new PageBean(pageNum,pageSize,count.intValue(),list);} }
    这样,带参数的分页也完成了,难度主要就是要接收参数,然后感觉参数对应的值,拼凑hql语句,来查询.但是可以看出hql语句都写在Action中了,这样是很不好的.所以需要进一步的优化.

封装分页

    建立一个QueryHelper类,里面封装hql语句.将hql分为3部分,先是查什么,fromClause,以及怎么查,whereClause,以及查完如何排序orderByClause.
    在QueryHelper中,封装FROM子句,WHERE和ORDERBY子句.对于构造方法,提供别名参数,对于where子句,需要使用addCondition,添加条件,里面可以放各种参数.并且为了优化,设置了重载,只要append传入true,则可以一直将多个addCondition写成一条语句.还有就是添加orderBy属性的.中间的都还比较简单,以及最后的preparePageBean,调用service查询出分页数据,然后放到对象栈中.
public class QueryHelper {private String fromClause; // FROM子句private String whereClause = ""; // WHERE子句private String orderByClause = ""; // ORDERBY子句private List<Object> parameters = new ArrayList<Object>();/** * 生成FROM子句 * @param clazz * @param alias */public QueryHelper(Class clazz,String alias) {fromClause = "FROM " + clazz.getSimpleName() + " " + alias;}/** * 拼接WHERE子句 * @param condition * @param param */public QueryHelper addCondition(String condition,Object... params) {if(whereClause.length() == 0) {whereClause = " WHERE " + condition;}else {whereClause = whereClause + " AND " + condition;}if (params != null) {for (Object p : params) {parameters.add(p);}}return this;}/** * 如果第一个参数为true,则拼接WHERE语句 * @param append * @param condition * @param params */public QueryHelper addCondition(boolean append,String condition,Object... params) {if (append) {addCondition(condition,params);}return this;}/** * 拼接OrderBy子句 * @param propertyName * 参与排序的属性名 * @param asc * true,表示升序;false表示降序 */public QueryHelper addOrderProperty(String propertyName,boolean asc) {if(orderByClause.length() == 0) {orderByClause = " ORDER BY " + propertyName + (asc ? " ASC":" DESC");}else {orderByClause += ", " + propertyName + (asc ? " ASC":" DESC");}return this;}/** * 如果第一个参数为true,则拼接ORDER BY语句 * @param append * @param propertyName * @param asc */public QueryHelper addOrderProperty(boolean append,String propertyName,boolean asc) {if (append) {addOrderProperty(propertyName,asc);}return this;}/** * 获取生成的用于查询数据列表的HQL语句 * @return */public String getListQueryHql() {return fromClause + whereClause + orderByClause;}/** * 获取生成的用于查询总记录数的HQL语句 * @return */public String getCountQueryHql() {return "SELECT COUNT(*) " + fromClause + whereClause ;}/** * 获取HQL中的参数值列表 * @return */public List<Object> getParameters() {return parameters;}/** * 查询分页信息,并放到值栈栈顶 * @param service * @param pageNum * @param pageSize */public void preparePageBean(DaoSupport<?> service,int pageNum,int pageSize) {PageBean pageBean = service.getPageBean(pageNum, pageSize, this);ActionContext.getContext().getValueStack().push(pageBean);}}
    这样Action中就用QueryHelper就能完成查询分页的功能.不要看这代码这么长,实际只有一句,就是不停的添加各种情况.若viewType为1,就是只显示精华帖,如果viewType为0,显示全部,那就不用条件了.如果orderby为0,1,2,3各种情况,其中默认除了要置顶贴在最前面,还有按最后更新时间降序排列.最后preparePageBean()查询数据,放到对象栈中.
new QueryHelper(Topic.class, "t")       //过滤条件 .addCondition("t.forum=?", forum) .addCondition(viewType == 1, "t.type=?", Topic.TYPE_BEST)  //排序条件 .addOrderProperty(orderBy == 1, "t.lastUpdateTime", asc) // 1:'只按最后更新时间排序', .addOrderProperty(orderBy == 2, "t.postTime", asc) // 2:'只按主题发表时间排序', .addOrderProperty(orderBy == 3, "t.replyCount", asc) // 3:'只按回复数量排序'}" .addOrderProperty(orderBy == 0, "(CASE t.type WHEN 2 THEN 2 ELSE 0 END)", false) // 0.默认排序(所有置顶帖在前面,并按最后更新时间降序排列, .addOrderProperty(orderBy == 0, "t.lastUpdateTime", false) .preparePageBean(replyService, pageNum, pageSize);
    而简单的分页,也可以简化成.
//准备分页信息v4 QueryHelper 最终版new QueryHelper(Reply.class, "r")  .addCondition("r.topic = ?", topic) .addOrderProperty("r.postTime", true) .preparePageBean(replyService, pageNum, pageSize); 
    经过这一系列的优化,现在写一个分页,已经非常简单了.只要引入pageView.jspf,然后在Action中写这么一句就可以了.
    到目前为止,汤阳光的OA视频,全部完成了,总结也全部完成了.至于说的用思维导图和一份大总结,我先暂时失忆了.
    写这份总结,之前对我来说,让我深入了解各种知识不少,到后期就感觉不想写了,但是好在坚持写完了,后期主要觉得收获有点小,而且这样写感觉有些太细了,有些没有重点,不过好在都完成了,一切都不是事了。
0 0
原创粉丝点击