SSM博客实战(8)-文章搜索和分页实现

本文将介绍如何实现搜索功能和分页功能。其中分页功能在之前已经介绍了,这里还会贴出代码。

本文中的数据用于测试,包括分类和标签信息是随便填的。

先上效果,这个是以后台为例子

SSM博客实战(8)-文章搜索和分页实现

一、先看数据表

注意:下表只是部分截图,不代表全部字段。表中的内容都是随便打的,尤其是 文章的标签和分类

1、article 表

SSM博客实战(8)-文章搜索和分页实现

 

2、category 表

SSM博客实战(8)-文章搜索和分页实现

 

3、tag 表

SSM博客实战(8)-文章搜索和分页实现

 

4、user 表

SSM博客实战(8)-文章搜索和分页实现

 

二、实体类

ArticleSearchVo.java

  1. package com.liuyanzhao.blog.po.custom;
  2. import com.liuyanzhao.blog.util.others.Page;
  3. import java.util.List;
  4. /**
  5.  * 用于封装搜索的文章列表,包括文章信息,作者信息,分类信息,标签信息,搜索信息
  6.  * Created by 言曌 on 2017/8/24.
  7.  */
  8. public class ArticleSearchVo extends ArticleCustom {
  9.     //文章信息
  10.     private ArticleCustom articleCustom;
  11.     //文章对应的分类
  12.     private List<CategoryCustom> categoryCustomList;
  13.     //文章对应的标签
  14.     private List<TagCustom> tagCustomList;
  15.     //作者信息
  16.     private UserCustom userCustom;
  17.     //文章分页信息
  18.     private Page page;
  19.     //搜索关键词
  20.     private String query;
  21.     public String getQuery() {
  22.         return query;
  23.     }
  24.     public UserCustom getUserCustom() {
  25.         return userCustom;
  26.     }
  27.     public void setUserCustom(UserCustom userCustom) {
  28.         this.userCustom = userCustom;
  29.     }
  30.     public void setQuery(String query) {
  31.         this.query = query;
  32.     }
  33.     public ArticleCustom getArticleCustom() {
  34.         return articleCustom;
  35.     }
  36.     public void setArticleCustom(ArticleCustom articleCustom) {
  37.         this.articleCustom = articleCustom;
  38.     }
  39.     public List<CategoryCustom> getCategoryCustomList() {
  40.         return categoryCustomList;
  41.     }
  42.     public void setCategoryCustomList(List<CategoryCustom> categoryCustomList) {
  43.         this.categoryCustomList = categoryCustomList;
  44.     }
  45.     public List<TagCustom> getTagCustomList() {
  46.         return tagCustomList;
  47.     }
  48.     public void setTagCustomList(List<TagCustom> tagCustomList) {
  49.         this.tagCustomList = tagCustomList;
  50.     }
  51.     public Page getPage() {
  52.         return page;
  53.     }
  54.     public void setPage(Page page) {
  55.         this.page = page;
  56.     }
  57. }

 

因为我们要输出文章列表,文章列表至少要包括 文章信息(标题,id 和发布时间),分类信息(分类名称和 id ),标签信息(标签名称和 id ),作者信息(作者昵称和 id )。

对于每篇文章而言,其中分类信息标签信息,在 article 里是以逗号分隔的,也就是我们要用一个 List 集合存储;而文章信息,作者信息和分页信息,用对象存储即可;因为我们还要返回查询输入的关键词,所以还要有一个变量。

其中 ArticleCustom,UserCustom,TagCustom,CategoryCustom 分别是 po 的扩展。

 

三、分页 类

Page.java

  1. package com.liuyanzhao.blog.util.others;
  2. /**
  3.  * 分页
  4.  * Created by 言曌 on 2017/8/26.
  5.  */
  6. import java.io.Serializable;
  7. public class Page implements Serializable {
  8.     private static final long serialVersionUID = -3198048449643774660L;
  9.     private int pageNow = 1// 当前页数
  10.     private int pageSize; // 每页显示记录的条数
  11.     private int totalCount; // 总的记录条数
  12.     private int totalPageCount; // 总的页数
  13.     @SuppressWarnings("unused")
  14.     private int startPos; // 开始位置,从0开始
  15.     @SuppressWarnings("unused")
  16.     private boolean hasFirst;// 是否有首页
  17.     @SuppressWarnings("unused")
  18.     private boolean hasPre;// 是否有前一页
  19.     @SuppressWarnings("unused")
  20.     private boolean hasNext;// 是否有下一页
  21.     @SuppressWarnings("unused")
  22.     private boolean hasLast;// 是否有最后一页
  23.     /**
  24.      * 通过构造函数 传入  总记录数  和  当前页
  25.      * @param totalCount
  26.      * @param pageNow
  27.      */
  28.     public Page(int totalCount, int pageNow,int pageSize) {
  29.         this.totalCount = totalCount;
  30.         this.pageNow = pageNow;
  31.         this.pageSize = pageSize;
  32.     }
  33.     /**
  34.      * 取得总页数,总页数=总记录数/总页数
  35.      * @return
  36.      */
  37.     public int getTotalPageCount() {
  38.         totalPageCount = getTotalCount() / getPageSize();
  39.         return (totalCount % pageSize == 0) ? totalPageCount
  40.             : totalPageCount + 1;
  41.     }
  42.     public void setTotalPageCount(int totalPageCount) {
  43.         this.totalPageCount = totalPageCount;
  44.     }
  45.     public int getPageNow() {
  46.         return pageNow;
  47.     }
  48.     public void setPageNow(int pageNow) {
  49.         this.pageNow = pageNow;
  50.     }
  51.     public int getPageSize() {
  52.         return pageSize;
  53.     }
  54.     public void setPageSize(int pageSize) {
  55.         this.pageSize = pageSize;
  56.     }
  57.     public int getTotalCount() {
  58.         return totalCount;
  59.     }
  60.     public void setTotalCount(int totalCount) {
  61.         this.totalCount = totalCount;
  62.     }
  63.     /**
  64.      * 取得选择记录的初始位置
  65.      * @return
  66.      */
  67.     public int getStartPos() {
  68.         return (pageNow - 1) * pageSize;
  69.     }
  70.     public void setStartPos(int startPos) {
  71.         this.startPos = startPos;
  72.     }
  73.     /**
  74.      * 是否是第一页
  75.      * @return
  76.      */
  77.     public boolean isHasFirst() {
  78.         return (pageNow == 1) ? false : true;
  79.     }
  80.     public void setHasFirst(boolean hasFirst) {
  81.         this.hasFirst = hasFirst;
  82.     }
  83.     /**
  84.      * 是否有首页
  85.      * @return
  86.      */
  87.     public boolean isHasPre() {
  88.         // 如果有首页就有前一页,因为有首页就不是第一页
  89.         return isHasFirst() ? true : false;
  90.     }
  91.     public void setHasPre(boolean hasPre) {
  92.         this.hasPre = hasPre;
  93.     }
  94.     /**
  95.      * 是否有下一页
  96.      * @return
  97.      */
  98.     public boolean isHasNext() {
  99.         // 如果有尾页就有下一页,因为有尾页表明不是最后一页
  100.         return isHasLast() ? true : false;
  101.     }
  102.     public void setHasNext(boolean hasNext) {
  103.         this.hasNext = hasNext;
  104.     }
  105.     /**
  106.      * 是否有尾页
  107.      * @return
  108.      */
  109.     public boolean isHasLast() {
  110.         // 如果不是最后一页就有尾页
  111.         return (pageNow == getTotalCount()) ? false : true;
  112.     }
  113.     public void setHasLast(boolean hasLast) {
  114.         this.hasLast = hasLast;
  115.     }
  116. }

具体使用,见下

 

四、dao 层

ArticleCustomMapper.java

  1. //文章结果查询结果的数量
  2.     public Integer getSearchResultCount(String s) throws Exception;
  3.     //查询文章分页操作
  4.     public List<ArticleCustom> listSearchResultByPage(@Param(value="queryContent") String queryContent,@Param(value="startPos") Integer startPos,@Param(value="pageSize") Integer pageSize) throws Exception;

 

ArticleCustomMapper.xml

  1. <!--查询结果统计-->
  2. <select id="getSearchResultCount" parameterType="String" resultType="Integer">
  3.     SELECT count(*) FROM `article`
  4.     <where>
  5.         article_status > 0 AND
  6.         article_title LIKE "%${value}%" OR
  7.         article_content LIKE "%${value}%"
  8.     </where>
  9. </select>
  10. <!--文章查询分页显示-->
  11. <select id="listSearchResultByPage"  resultType="com.liuyanzhao.blog.po.custom.ArticleCustom">
  12.     SELECT
  13.     <include refid="article_table_all_columns"/>
  14.     FROM `article`
  15.     <where>
  16.         article_status > 0 AND
  17.         article_title LIKE '%${queryContent}%' OR
  18.         article_content LIKE '%${queryContent}%'
  19.         limit #{startPos},#{pageSize}
  20.     </where>
  21. </select>

注意字段名不要写错了,我这里的 article_status 是文章状态,0表示禁用不显示,1表示正常,2表示置顶

 

五、Service 层

ArticleServiceImpl.java

  1. //文章查询结果分页
  2.     @Override
  3.     public List<ArticleSearchVo> listSearchResultByPage(HttpServletRequest request, Model model,Integer pageNow,Integer pageSize,String query) throws Exception {
  4.         Page page = null;
  5.         List<ArticleCustom> articleCustomList = new ArrayList<ArticleCustom>();
  6.         int totalCount = articleMapperCustom.getSearchResultCount(query);
  7.         if (pageNow != null) {
  8.             page = new Page(totalCount, pageNow, pageSize);
  9.             articleCustomList = this.articleMapperCustom.listSearchResultByPage(query, page.getStartPos(), page.getPageSize());
  10.         } else {
  11.             page = new Page(totalCount, 1, pageSize);
  12.             articleCustomList = this.articleMapperCustom.listSearchResultByPage(query, page.getStartPos(), page.getPageSize());
  13.         }
  14.         List<ArticleSearchVo> articleSearchVoList = new ArrayList<ArticleSearchVo>();
  15.         //查询结果条数为0,下面的不执行,防止空指针
  16.         if(totalCount!=0) {
  17.             for (int i = 0; i < articleCustomList.size(); i++) {
  18.                 ArticleSearchVo articleSearchVo = new ArticleSearchVo();
  19.                 //1、将文章信息装到 articleListVoList 中
  20.                 ArticleCustom articleCustom = articleCustomList.get(i);
  21.                 articleSearchVo.setArticleCustom(articleCustom);
  22.                 //2、将分类信息装到 articleListVoList 中
  23.                 List<CategoryCustom> categoryCustomList = new ArrayList<CategoryCustom>();
  24.                 String categoryIds = articleCustomList.get(i).getArticleCategoryIds();
  25.                 String[] cateId = categoryIds.split(",");
  26.                 for (int j = 0; j < cateId.length; j++) {
  27.                     Category category = categoryMapper.selectByPrimaryKey(Integer.valueOf(cateId[j]));
  28.                     CategoryCustom categoryCustom = new CategoryCustom();
  29.                     BeanUtils.copyProperties(category, categoryCustom);
  30.                     categoryCustomList.add(categoryCustom);
  31.                 }
  32.                 articleSearchVo.setCategoryCustomList(categoryCustomList);
  33.                 //3、获得标签信息
  34.                 List<TagCustom> tagCustomList = new ArrayList<TagCustom>();
  35.                 String tagIds = articleCustomList.get(i).getArticleTagIds();
  36.                 String[] tagId = tagIds.split(",");
  37.                 for (int j = 0; j < tagId.length; j++) {
  38.                     Tag tag = tagMapper.selectByPrimaryKey(Integer.valueOf(tagId[j]));
  39.                     TagCustom tagCustom = new TagCustom();
  40.                     BeanUtils.copyProperties(tag, tagCustom);
  41.                     tagCustomList.add(tagCustom);
  42.                 }
  43.                 articleSearchVo.setTagCustomList(tagCustomList);
  44.                 //4、获得作者信息
  45.                 User user = userMapper.selectByPrimaryKey(articleCustom.getArticleUserId());
  46.                 UserCustom userCustom = new UserCustom();
  47.                 BeanUtils.copyProperties(user, userCustom);
  48.                 articleSearchVo.setUserCustom(userCustom);
  49.                 articleSearchVoList.add(articleSearchVo);
  50.             }
  51.         } else {
  52.             //不执行的话,也要创建一个元素,存储分页信息和查询关键字
  53.             ArticleSearchVo articleSearchVo = new ArticleSearchVo();
  54.             articleSearchVoList.add(articleSearchVo);
  55.         }
  56.         //5、page信息存储在第一个元素中
  57.         articleSearchVoList.get(0).setPage(page);
  58.         //6、将查询的关键词存储到第一个元素
  59.         articleSearchVoList.get(0).setQuery(query);
  60.         return articleSearchVoList;
  61.     }

这里的 service 调用了 DAO 层的两个方法,分别是获取查询结果的条数和查询结果分页显示。

 

六、Controller 层

ArticleController.java

  1. //搜索实现
  2.   @RequestMapping("/admin/article/search")
  3.   @ResponseBody
  4.   public ModelAndView SearchPageView(HttpServletRequest request,Model model) throws Exception {
  5.       ModelAndView modelAndView = new ModelAndView();
  6.       //设置每页显示的数量
  7.       int pageSize = 5;
  8.       String query = request.getParameter("query");
  9.       List<ArticleSearchVo> articleSearchVoList = articleService.listSearchResultByPage(request,model,null,pageSize,query);
  10.       modelAndView.addObject("articleSearchVoList", articleSearchVoList);
  11.       modelAndView.setViewName("Admin/Article/search");
  12.       return modelAndView;
  13.   }
  14.   //搜索分页实现
  15.   @RequestMapping("/admin/article/p/{pageNow}/search")
  16.   @ResponseBody
  17.   public  ModelAndView SearchPageByPageView(HttpServletRequest request, Model model,@PathVariable("pageNow") Integer pageNow) throws Exception {
  18.       ModelAndView modelAndView = new ModelAndView();
  19.       //设置每页显示的数量
  20.       int pageSize = 5;
  21.       String query = request.getParameter("query");
  22.       List<ArticleSearchVo> articleSearchVoList = articleService.listSearchResultByPage(request,model,pageNow,pageSize,query);
  23.       modelAndView.addObject("articleSearchVoList", articleSearchVoList);
  24.       modelAndView.setViewName("/Admin/Article/search");
  25.       return modelAndView;
  26.   }

 

第一个方法是用户输入查询信息,点击查询按钮的时候,会提交表单到 /admin/article/search 这里。

因为我们设置的查询结果是按分页显示的,如果超过 10 条,会分页。点击分页按钮,会执行第二个方法。

 

七、视图层

index.jsp

  1. <form class="layui-form" action="${pageContext.request.contextPath}/admin/article/search">
  2.         <div class="layui-form-item">
  3.             <div class="layui-input-block">
  4.                 <input type="text" name="query" required  lay-verify="required" placeholder="请输入关键词" autocomplete="off" class="layui-input" >
  5.                 <button class="layui-btn" lay-submit lay-filter="formDemo" type="submit">搜索</button>
  6.             </div>
  7.         </div>
  8.     </form>

这个是后台文章列表 首页,有一个这样的搜索框

SSM博客实战(8)-文章搜索和分页实现

 

search.jsp

  1. <blockquote class="layui-elem-quote">搜索 ${articleSearchVoList[0].query} 找到 ${articleSearchVoList[0].page.totalCount} 条数据</blockquote>
  2.   <form class="layui-form" action="${pageContext.request.contextPath}/admin/article/search">
  3.       <div class="layui-form-item">
  4.           <div class="layui-input-block">
  5.               <input type="text" name="query" required  lay-verify="required" placeholder="请输入标题" autocomplete="off" class="layui-input" value="${articleSearchVoList[0].query}" >
  6.               <button class="layui-btn" lay-submit lay-filter="formDemo" type="submit">搜索</button>
  7.               <button class="layui-btn" lay-submit lay-filter="formDemo" style="float: right;">批量删除</button>
  8.           </div>
  9.       </div>
  10.   </form>
  11.   <c:choose>
  12.       <%--查询结果不为0--%>
  13.       <c:when test="${articleSearchVoList[0].page.totalCount!=0}">
  14.           <table class="layui-table">
  15.               <colgroup>
  16.                   <col width="25">
  17.                   <col width="25">
  18.                   <col width="300">
  19.                   <col width="200">
  20.                   <col width="200">
  21.                   <col width="200">
  22.                   <col width="100">
  23.                   <col width="200">
  24.               </colgroup>
  25.               <thead>
  26.               <tr>
  27.                   <th><input type="checkbox" id="allSelect"  onclick="javascript:DoCheck()"></th>
  28.                   <th>id</th>
  29.                   <th>标题</th>
  30.                   <th>所属分类</th>
  31.                   <th>所带标签</th>
  32.                   <th>发布时间</th>
  33.                   <th>作者</th>
  34.                   <th>操作</th>
  35.               </tr>
  36.               </thead>
  37.               <tbody>
  38.               <c:forEach items="${articleSearchVoList}" var="a">
  39.                   <tr>
  40.                       <td><input type="checkbox" name="select_id"></td>
  41.                       <td>${a.articleCustom.articleId}</td>
  42.                       <td><a href="${pageContext.request.contextPath}/article/${a.articleCustom.articleId}" target="_blank">
  43.                               ${fn:substring(a.articleCustom.articleTitle, 0,20 )}
  44.                       </a></td>
  45.                       <td>
  46.                           <c:forEach items="${a.categoryCustomList}" var="c">
  47.                               <a href="${pageContext.request.contextPath}/category/${c.categoryId}" target="_blank">${c.categoryName}</a>
  48.                               &nbsp;
  49.                           </c:forEach>
  50.                       </td>
  51.                       <td>
  52.                           <c:forEach items="${a.tagCustomList}" var="t">
  53.                               <a href="${pageContext.request.contextPath}/tag/${t.tagId}" target="_blank">${t.tagName}</a>
  54.                               &nbsp;
  55.                           </c:forEach>
  56.                       </td>
  57.                       <td>
  58.                           <fmt:formatDate value="${a.articleCustom.articlePostTime}" pattern="yyyy-MM-dd HH:mm:ss"/>
  59.                       </td>
  60.                       <td>
  61.                           <a href="${pageContext.request.contextPath}/admin/user/${a.userCustom.userId}">
  62.                                   ${a.userCustom.userNickname}
  63.                           </a>
  64.                       </td>
  65.                       <td>
  66.                           <a href="editArticle">编辑</a>
  67.                           <a href="">删除</a>
  68.                       </td>
  69.                   </tr>
  70.               </c:forEach>
  71.               </tbody>
  72.           </table>
  73.           <%--分页 start--%>
  74.           <nav class="navigation pagination" role="navigation">
  75.               <div class="nav-links">
  76.                   <c:choose>
  77.                       <c:when test="${articleSearchVoList[0].page.totalPageCount <= 3 }">
  78.                           <c:set var="begin" value="1"/>
  79.                           <c:set var="end" value="${articleSearchVoList[0].page.totalPageCount }"/>
  80.                       </c:when>
  81.                       <c:otherwise>
  82.                           <c:set var="begin" value="${articleSearchVoList[0].page.pageNow-1 }"/>
  83.                           <c:set var="end" value="${articleSearchVoList[0].page.pageNow + 2}"/>
  84.                           <c:if test="${begin < 2 }">
  85.                               <c:set var="begin" value="1"/>
  86.                               <c:set var="end" value="3"/>
  87.                           </c:if>
  88.                           <c:if test="${end > articleSearchVoList[0].page.totalPageCount }">
  89.                               <c:set var="begin" value="${articleSearchVoList[0].page.totalPageCount-2 }"/>
  90.                               <c:set var="end" value="${articleSearchVoList[0].page.totalPageCount }"/>
  91.                           </c:if>
  92.                       </c:otherwise>
  93.                   </c:choose>
  94.                       <%--上一页 --%>
  95.                   <c:choose>
  96.                       <c:when test="${articleSearchVoList[0].page.pageNow eq 1 }">
  97.                           <%--当前页为第一页,隐藏上一页按钮--%>
  98.                       </c:when>
  99.                       <c:otherwise>
  100.                           <a class="page-numbers" href="${pageContext.request.contextPath}/admin/article/p/${articleSearchVoList[0].page.pageNow-1}/search?query=${articleSearchVoList[0].query}" >
  101.                               <i class="layui-icon">&#xe603;</i>
  102.                           </a>
  103.                       </c:otherwise>
  104.                   </c:choose>
  105.                       <%--显示第一页的页码--%>
  106.                   <c:if test="${begin >= 2 }">
  107.                       <a class="page-numbers" href="${pageContext.request.contextPath}/admin/article/p/1/search?query=${articleSearchVoList[0].query}">1</a>
  108.                   </c:if>
  109.                       <%--显示点点点--%>
  110.                   <c:if test="${begin  > 2 }">
  111.                       <span class="page-numbers dots"></span>
  112.                   </c:if>
  113.                       <%--打印 页码--%>
  114.                   <c:forEach begin="${begin }" end="${end }" var="i">
  115.                       <c:choose>
  116.                           <c:when test="${i eq articleSearchVoList[0].page.pageNow }">
  117.                               <a class="page-numbers current" >${i}</a>
  118.                           </c:when>
  119.                           <c:otherwise>
  120.                               <a  class="page-numbers" href="${pageContext.request.contextPath}/admin/article/p/${i}/search?query=${articleSearchVoList[0].query}">${i }</a>
  121.                           </c:otherwise>
  122.                       </c:choose>
  123.                   </c:forEach>
  124.                       <%-- 显示点点点 --%>
  125.                   <c:if test="${end < articleSearchVoList[0].page.totalPageCount-1 }">
  126.                       <span class="page-numbers dots"></span>
  127.                   </c:if>
  128.                       <%-- 显示最后一页的数字 --%>
  129.                   <c:if test="${end < articleSearchVoList[0].page.totalPageCount }">
  130.                       <a href="${pageContext.request.contextPath}/admin/article/p/${articleSearchVoList[0].page.totalPageCount}/search?query=${articleSearchVoList[0].query}">
  131.                               ${articleSearchVoList[0].page.totalPageCount}
  132.                       </a>
  133.                   </c:if>
  134.                       <%--下一页 --%>
  135.                   <c:choose>
  136.                       <c:when test="${articleSearchVoList[0].page.pageNow eq articleSearchVoList[0].page.totalPageCount }">
  137.                           <%--到了尾页隐藏,下一页按钮--%>
  138.                       </c:when>
  139.                       <c:otherwise>
  140.                           <a class="page-numbers" href="${pageContext.request.contextPath}/admin/article/p/${articleSearchVoList[0].page.pageNow+1}/search?query=${articleSearchVoList[0].query}">
  141.                               <i class="layui-icon">&#xe602;</i>
  142.                           </a>
  143.                       </c:otherwise>
  144.                   </c:choose>
  145.               </div>
  146.           </nav>
  147.           <%--分页 end--%>
  148.       </c:when>
  149.       <%--查询结果为0--%>
  150.       <c:otherwise>
  151.          <center><br>很遗憾,没有查询到带有 <font style="color: red;"> ${articleSearchVoList[0].query} </font> 的内容,换一个关键词再试试吧。</center>
  152.       </c:otherwise>
  153.   </c:choose>

 

 

注意:博主后台使用的是 layui 的框架,如果你没有使用,请忽略一些样式代码。分页部分代码也稍微复杂,比较累赘。

 

八、效果预览

为了方便截图,这里设置一页显示五条数据

SSM博客实战(8)-文章搜索和分页实现

本文地址:https://liuyanzhao.com/6241.html

  • 微信
  • 交流学习,有偿服务
  • weinxin
  • 博客/Java交流群
  • 资源分享,问题解决,技术交流。群号:590480292
  • weinxin
言曌

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

目前评论:3   其中:访客  2   博主  1

    • avatar 向天空大声的呼喊

      好东西

      • avatar 威客系统

        干货干货哈哈哈收入囊中