第08课:首页展示及分页(PageHelper)

在正式实现首页和分页功能之前,我们首先思考两个问题:

1.index.jsp 页面动态数据怎么获取? 2.如果用 AJAX 获取,点击下一页的时候又要怎么办?

前期准备

引入 index.jsp 文件,将原 index.jsp 文件替换。

启动 Tomcat 后访问,效果如下:

基于Spring+SpringMVC+MyBatis博客系统的开发教程(八)_自定义

基于Spring+SpringMVC+MyBatis博客系统的开发教程(八)_博客系统_02

因为页面都是死数据,无法完成分页功能,所以先导入文章的数据 SQL,SQL 文件会放入文末的百度网盘链接中。

说明:因为 user_content 表存在外键关系,所以要先解除外键约束,否则无法删除和修改表:

SET FOREIGN_KEY_CHECKS=0; #解除外键约束

等插入数据之后,再设置外键约束:

SET FOREIGN_KEY_CHECKS=1;#设置外键约束

导入 SQL 文件和之前一样,如图:

基于Spring+SpringMVC+MyBatis博客系统的开发教程(八)_博客系统_03

再将 image 文件夹拷贝到 dreamland\dreamland-web\target\dreamland-web\images\ 目录下,主要是网页用到的网页图片,image 文件夹也在文末的百度网盘链接中。

index 页面

右上角的登录、注册

index.jsp 页面逻辑判断代码,如下所示:

 <div style="position: absolute;margin-left: 980px;margin-top: -40px;">
        <c:if test="${empty user}">
            <a name="tj_login" class="lb" href="login?error=login" style="color: black">[登录]</a>
            &nbsp;&nbsp;
            <a name="tj_login" class="lb" href="register" style="color: black">[注册]</a>
        </c:if>
        <c:if test="${not empty user}">
            <a name="tj_loginp" href="void(0);"   class="lb" onclick="personal('${user.id}');" style="color: black"><font color="#9370db">${user.nickName}, 欢迎您!</font></a>
            &nbsp;&nbsp;
            <a name="tj_login" class="lb" href="${ctx}/loginout" style="color: black">[退出]</a>
        </c:if>

    </div>

通过 EL 表达式判断:

  1. 如果用户为空,则显示“登录”、“注册”;
  2. 如果不为空,则显示“xxx,欢迎您!”和“退出”。

引入天气预报

只需将最底下的 div,如下:

     <div class="col-md-3" style="background-color: #C6E2FF;position:absolute;top:0px;left: 873px;width: 268px">
            <h2>Sidebar</h2>
            <ul class="nav nav-tabs nav-stacked">
                <li><a href='#'>Another Link 1</a></li>
                <li><a href='#'>Another Link 2</a></li>
                <li><a href='#'>Another Link 3</a></li>
            </ul>
        </div>

替换成下面的代码,即可:

     <div class="col-md-3" style="position:absolute;top:0px;left: 880px;width: 268px;">
            <div style="background-color: white;width: 250px;height: 440px">
                <iframe name="weather_inc" src="http://i.tianqi.com/index.php?c=code&id=82" width="250" height="440" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
            </div>
        </div>

这个只是简单的引用,且必须联网才能使用。效果如下:

基于Spring+SpringMVC+MyBatis博客系统的开发教程(八)_ico_04

关于 index 页面的数据获取

首先,通过 AJAX 请求获取数据,JavaScript 代码大概是这样的(看看就好,不建议):

<script language=javascript>
    $.ajax({
        type: "post",
        dataType: "json",
        url: '${ctx}/index',
        success: function (data) {

            var Data=data;
            var content = null;
            var page =null;
            for(var key in Data) {
                if(key == "page"){
                    page = Data[key];
                }
                if(key == "content"){
                    content = Data[key];
                }
            }

            if (data!=null && data != "" ) {
               $(content).each(function () {
                   var ll = this.title;
                  // alert(ll);
                  // alert(0);
                   $("#content_col").append("<div class='content-text' ><div class='author clearfix'><div>"
                       +"<a href='#' target='_blank' rel='nofollow' style='height: 35px' οnclick=''>"
                       +"<img src='"+this.imgUrl+"'></a></div><a href='' target='_blank' οnclick=''>"
                       +"<h2 class='author-h2'>"+this.nickName+"</h2></a></div>"
                       +"<h2>"+this.title+"</h2>"+this.content+
                       "<div style='height: 5px'></div><div class='stats'><span class='stats-vote'><i class='number'>"+this.upvote+"</i> 赞</span>"
                      +"<span class='stats-comments'><span class='dash'> · </span>"
                   +"<a href='#' class='comments' target='_blank' οnclick=''>"
                       +"<i class='number'>"+this.commentNum+"</i> 评论 </a></span> </div><div style='height: 5px'></div>"
                     +"<div class='stats-buttons bar clearfix'><a><i class='icon icon-thumbs-o-up icon-2x'></i><span class='number hidden'>"+this.upvote+"</span></a>"
                       +"&nbsp;&nbsp;&nbsp;<a><i class='icon icon-thumbs-o-down icon-2x'></i><span class='number hidden'>"+this.downvote+"</span></a>&nbsp;&nbsp;&nbsp;<a><i class='icon icon-comment-alt icon-2x'></i></a>"
                       +"</div><div class='single-share'><a class='share-wechat' data-type='wechat' title='分享到微信' rel='nofollow' style='margin-left:18px;color: grey'>"
                       +"<i class='icon icon-wechat icon-2x'></i></a><a class='share-qq' data-type='qq' title='分享到QQ' rel='nofollow' style='margin-left:18px;color: grey'>"
                       +"<i class='icon icon-qq icon-2x'></i> </a><a class='share-weibo' data-type='weibo' title='分享到微博' rel='nofollow' style='margin-left:18px;color: grey'>"
                       +"<i class='icon icon-weibo icon-2x'></i></a></div><br/>&nbsp;<div class='single-clear'></div></div>"
                       +"<div style='position: absolute;width:900px;background-color: #EBEBEB;height: 10px;left: 0px'></div>"
                   );

               });

                var cnt = "<ul class='pager pager-loose'>";
                if(page.pageNum <= 1){
                    cnt += "<li><a href='void(0);'>« 上一页</a></li>";
                }else {
                    var pNum = page.pageNum-1;
                    cnt += "<li class='previous'><a href='${ctx}/index_list?pageNum="+pNum+"&&id=${user.id}'>« 上一页</a></li>";
                }
                for(var i=1;i<=page.pages;i++){
                    if(page.pageNum == i){
                     cnt += "<li class='active'><a href='void(0);'>"+i+"</a></li>";
                    }else{
                        cnt += "<li ><a href='${ctx}/index_list?pageNum="+i+"&&id=${user.id}'>"+i+"</a></li>";
                    }
                }

                if(page.pageNum >= page.pages){
                    cnt += "<li><a href='void(0);'>下一页 »</a></li>";
                }else {
                    var pNum = page.pageNum+1;
                    cnt += "<li><a href='${ctx}/index_list?pageNum="+pNum+"&&id=${user.id}'>下一页 »</a></li></ul>";
                }

               //分页
                $("#page-info").append(cnt);

            }
        }
    });
    </script>

主要是通过 AJAX 获取数据后再添加到 div 中,这样只是解决了 index 页面的首次正常访问,有动态数据,但当点击下一页的时候就会产生新问题。这时可以新建一个和 index.jsp 一样的页面 index2.jsp,只不过这次不是用 AJAX 从后台获取数据,而是通过点击下一页或者其他页码,先经过 Controller,将分页数据封装后返回到 index2.jsp。这样做费时费力还耗资源,所以也不可取。

推荐的解决办法是:在进入 index.jsp 之前进行过滤器拦截,先获取页面数据,之后再返回 index.jsp 页面。

实现步骤如下:

(1)在 web.xml 中,引入自定义过滤器 filter:

    <!--自定义过滤器-->
      <filter>
        <filter-name>dispatcherDemoFilter</filter-name>
        <filter-class>wang.dreamland.www.interceptor.IndexJspFilter</filter-class>
      </filter>
      <filter-mapping>
        <filter-name>dispatcherDemoFilter</filter-name>
        <url-pattern>/index.jsp</url-pattern>
      </filter-mapping>

在访问 index.jsp 之前进入自定义过滤器 dispatcherDemoFilter,通过该名字找到自定义过滤器具体路径 wang.dreamland.www.interceptor.IndexJspFilter

(2)在 wang.dreamland.www 下新建包 interceptor,在 interceptor 包下新建 IndexJspFilter.java:

public class IndexJspFilter implements Filter{
        public void init(FilterConfig filterConfig) throws ServletException {

        }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("===========自定义过滤器==========");
        ServletContext context = request.getServletContext();
        ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(context);
        UserContentMapper userContentMapper = ctx.getBean(UserContentMapper.class);
        PageHelper.startPage(null, null);//开始分页
        List<UserContent> list =  userContentMapper.select( null );
        PageHelper.Page endPage = PageHelper.endPage();//分页结束
        request.setAttribute("page", endPage );
        chain.doFilter(request, response);
    }

    public void destroy() {

        }
    }

代码解读如下:

filter 初始化时,注解的 bean 还没初始化,加 @Autowired 注解不会起作用,所以通过 ApplicationContext 手动获取 UserContentMapper 对象。

UserContentMapper userContentMapper = ctx.getBean(UserContentMapper.class);

在开始分页和结束分页之间查询数据,PageHelper 会对分页开始之后的第一个查询语句进行分页,封装在 Page 对象中。

PageHelper.startPage(null, null);//开始分页
List<UserContent> list =  userContentMapper.select( null );
PageHelper.Page endPage = PageHelper.endPage();//分页结束

pageNum 如果为 Null 则默认为第一页,pageSize 为 null 则默认每页显示7条数据,在 PageHelper 中有设默认值:

public Page(Integer pageNum, Integer pageSize) {
    if (pageNum == null || pageNum < 1) {
        pageNum = 1;
    }
    if (pageSize == null || pageSize < 1) {
        pageSize = 7;
    }
    this.pageNum = pageNum;
    this.pageSize = pageSize;
}

然后将 Page 对象放在 request 域中,前台可通过 EL 表达式 ${page} 获取:

request.setAttribute("page", endPage );

(3)index.jsp 页面获取数据的代码如下:

<c:forEach var="cont" items="${page.result}" varStatus="i">
                    <!-- 正文开始 -->

                    <div class="content-text">

                        <div class="author clearfix">
                            <div>
                                <a href="#" target="_blank" rel="nofollow" style="height: 35px">
                                     <img src="${cont.imgUrl}">
                                </a>
                            </div>
                            <a href="#" target="_blank">
                                <h2 class="author-h2">
                                    ${cont.nickName}
                                </h2>
                            </a>
                        </div>

                       <h2>${cont.title}</h2>
                            ${cont.content}
                        <div style="height: 5px"></div>
                        <div class="stats">
                            <!-- 笑脸、评论数等 -->
                            <span class="stats-vote"><i id="${cont.id}" class="number">${cont.upvote}</i> 赞</span>
                            <span class="stats-comments">
                    <span class="dash"> · </span>
                         <a  onclick="reply(${cont.id},${cont.uId});">
                              <i class="number" id="comment_num_${cont.id}">${cont.commentNum}</i> 评论
                          </a>
                    </span>
                        </div>
                        <div style="height: 5px"></div>
                        <div class="stats-buttons bar clearfix">
                            <a style="cursor: pointer;" onclick="upvote_click(${cont.id},1);">
                                <i class="icon icon-thumbs-o-up icon-2x"></i>
                                <span class="number hidden" id="up_${cont.id}">${cont.upvote}</span>
                            </a>
                            &nbsp;
                            <a style="cursor: pointer;" onclick="upvote_click(${cont.id},-1);">
                                <i class="icon icon-thumbs-o-down icon-2x"></i>
                                <span class="number hidden" id="down_${cont.id}">${cont.downvote}</span>
                            </a>
                            &nbsp;
                            <a style="cursor: pointer;" onclick="reply(${cont.id},${cont.uId});" title="点击打开或关闭">
                                <i class="icon icon-comment-alt icon-2x"></i>
                            </a>
                        </div>
                        <div class="single-share">
                            <a class="share-wechat" data-type="wechat" title="分享到微信" rel="nofollow" style="margin-left:18px;color: grey;cursor: pointer; text-decoration:none;">
                                <i class="icon icon-wechat icon-2x"></i>
                            </a>
                            <a class="share-qq" data-type="qq" title="分享到QQ" rel="nofollow" style="margin-left:18px;color: grey;cursor: pointer; text-decoration:none;">
                                <i class="icon icon-qq icon-2x"></i>
                            </a>
                            <a  class="share-weibo" data-type="weibo" title="分享到微博" rel="nofollow" style="margin-left:18px;color: grey;cursor: pointer; text-decoration:none;">
                                <i class="icon icon-weibo icon-2x"></i>
                            </a>
                        </div>
                        <br/>
                        &nbsp;
                        <div class="commentAll" style="display:none" id="comment_reply_${cont.id}">
                            <!--评论区域 begin-->
                            <div class="reviewArea clearfix">
                                <textarea class="content comment-input" placeholder="Please enter a comment&hellip;" onkeyup="keyUP(this)"></textarea>
                                <a class="plBtn" id="comment_${cont.id}" onclick="_comment(${cont.id},${user.id==null?0:user.id},${cont.uId})" style="color: white;cursor: pointer;">评论</a>
                            </div>
                            <!--评论区域 end-->
                            <div class="comment-show-first" id="comment-show-${cont.id}">

                            </div>

                        </div>

                        <div class="single-clear">

                        </div>
                    </div>
                    <!-- 正文结束 -->
                    <div style="position: absolute;width:900px;background-color: #EBEBEB;height: 10px;left: 0px">
                    </div>
                </c:forEach>

代码解读如下:

  • 通过 <c:forEach></c:forEach> 循环获取文章内容 Content。
  • items="${page.result}" 中,item 是 List<Content> 集合。
  • “var="cont" 中,cont 是每一个 Content,通过 ${cont.属性} 即可获得对应的属性值。
  • varStatus="i" 中,i.index 就是从0开始的迭代索引,i.count 就是从1开始迭代计数。

(4)分页信息如下:

<div id="page-info" style="position: absolute;width:900px;background-color: #EBEBEB;height: 80px;left: 0px;">
                <ul class="pager pager-loose">
                    <c:if test="${page.pageNum <= 1}">
                        <li><a href="void(0);">« 上一页</a></li>
                    </c:if>
                    <c:if test="${page.pageNum > 1}">
                        <li class="previous"><a href="${ctx}/index_list?pageNum=${page.pageNum-1}&&id=${user.id}">« 上一页</a></li>
                    </c:if>
                    <c:forEach begin="1" end="${page.pages}" var="pn">
                        <c:if test="${page.pageNum==pn}">
                            <li class="active"><a href="void(0);">${pn}</a></li>
                        </c:if>
                        <c:if test="${page.pageNum!=pn}">
                            <li ><a href="${ctx}/index_list?pageNum=${pn}&&id=${user.id}">${pn}</a></li>
                        </c:if>
                    </c:forEach>

                    <c:if test="${page.pageNum>=page.pages}">
                        <li><a href="void(0);">下一页 »</a></li>
                    </c:if>
                    <c:if test="${page.pageNum<page.pages}">
                        <li><a href="${ctx}/index_list?pageNum=${page.pageNum+1}&&id=${user.id}">下一页 »</a></li>
                    </c:if>

                </ul>
            </div>

 

判断过程如下:

  1. 页数 <=1 显示上一页,但是不能点击(点击无效);
  2. 页数 >1 显示上一页,点击返回上一页;
  3. 页数 >= 最后一页 ,显示下一页,但是点击无效;
  4. 页数 < 最后一页,显示下一页,点击跳转到下一页。

(5)中间页数循环。

代码如下:

    <c:forEach begin="1" end="${page.pages}" var="pn">

从第一页开始,最后一页结束,变量 pn=当前页数。

(6)如果 pageNum 等于当前页 pn,说明被选中,不能被再次点击(点击无效):

     <c:if test="${page.pageNum==pn}">
          <li class="active"><a href="void(0);">${pn}</a></li>
     </c:if>

(7)如果 pageNum 不是当前页,既不是被选中页,可以被点击,点击后跳转到该页:

      <c:if test="${page.pageNum!=pn}">
           <li ><a href="${ctx}/index_list?pageNum=${pn}&&id=${user.id}">${pn}</a></li>
      </c:if>

重启 Tomcat,访问:http://localhost:8080/,页面已经有动态数据了,但是点击下一页或者其他页还没实现。

IndexJspController

在 wang.dreamland.www 包下新建 IndexJspController.java,并继承 BaseController,将关于首页有关的方法都放在这里,创建映射 URL 为 /index_list 的方法:

 @Controller
    public class IndexJspController extends BaseController {
    private final static Logger log = Logger.getLogger(IndexJspController.class);

    @RequestMapping("/index_list")
    public String findAllList(Model model, @RequestParam(value = "id",required = false) String id ,
                                    @RequestParam(value = "pageNum",required = false) Integer pageNum ,
                                    @RequestParam(value = "pageSize",required = false) Integer pageSize) {

         log.info( "===========进入index_list=========" );
        User user = (User)getSession().getAttribute("user");
        if(user!=null){
            model.addAttribute( "user",user );
        }
        Page<UserContent> page =  findAll(null,pageNum,  pageSize);
        model.addAttribute( "page",page );
        return "../index";
    }

    }

代码解读如下:

  1. 通过 Session 获取 User 信息,如果不为空则把 User 添加到 Model 中;

  2. 调用 BaseController 中的 findAll 方法查询分页并将结果封装到 Page 对象中,参数分别是 null 代表查询所有、pageNum 当前页、pageSize 每页显示条数;

  3. 将 Page 对象添加到 Model 中,并返回到 index.jsp 页面。

最后重启 Tomcat 访问:http://localhost:8080/,分页完成!

基于Spring+SpringMVC+MyBatis博客系统的开发教程(八)_博客系统_05

第08课百度网盘地址:

链接:https://pan.baidu.com/s/1iyTbvLepD07MUeFVbpMxRw 密码:1xxw