本文将介绍如何实现搜索功能和分页功能。其中分页功能在之前已经介绍了,这里还会贴出代码。
本文中的数据用于测试,包括分类和标签信息是随便填的。
先上效果,这个是以后台为例子
注意:下表只是部分截图,不代表全部字段。表中的内容都是随便打的,尤其是 文章的标签和分类
1、article 表
2、category 表
3、tag 表
4、user 表
ArticleSearchVo.java
因为我们要输出文章列表,文章列表至少要包括 文章信息(标题,id 和发布时间),分类信息(分类名称和 id ),标签信息(标签名称和 id ),作者信息(作者昵称和 id )。
对于每篇文章而言,其中分类信息标签信息,在 article 里是以逗号分隔的,也就是我们要用一个 List 集合存储;而文章信息,作者信息和分页信息,用对象存储即可;因为我们还要返回查询输入的关键词,所以还要有一个变量。
其中 ArticleCustom,UserCustom,TagCustom,CategoryCustom 分别是 po 的扩展。
Page.java
具体使用,见下
ArticleCustomMapper.java
ArticleCustomMapper.xml
注意字段名不要写错了,我这里的 article_status 是文章状态,0表示禁用不显示,1表示正常,2表示置顶
ArticleServiceImpl.java
这里的 service 调用了 DAO 层的两个方法,分别是获取查询结果的条数和查询结果分页显示。
ArticleController.java
第一个方法是用户输入查询信息,点击查询按钮的时候,会提交表单到 /admin/article/search 这里。
因为我们设置的查询结果是按分页显示的,如果超过 10 条,会分页。点击分页按钮,会执行第二个方法。
index.jsp
这个是后台文章列表 首页,有一个这样的搜索框
search.jsp
注意:博主后台使用的是 layui 的框架,如果你没有使用,请忽略一些样式代码。分页部分代码也稍微复杂,比较累赘。
为了方便截图,这里设置一页显示五条数据
本文地址:https://liuyanzhao.com/6241.html
本文中的数据用于测试,包括分类和标签信息是随便填的。
先上效果,这个是以后台为例子
一、先看数据表
注意:下表只是部分截图,不代表全部字段。表中的内容都是随便打的,尤其是 文章的标签和分类
1、article 表
2、category 表
3、tag 表
4、user 表
二、实体类
ArticleSearchVo.java
- package com.liuyanzhao.blog.po.custom;
- import com.liuyanzhao.blog.util.others.Page;
- import java.util.List;
- /**
- * 用于封装搜索的文章列表,包括文章信息,作者信息,分类信息,标签信息,搜索信息
- * Created by 言曌 on 2017/8/24.
- */
- public class ArticleSearchVo extends ArticleCustom {
- //文章信息
- private ArticleCustom articleCustom;
- //文章对应的分类
- private List<CategoryCustom> categoryCustomList;
- //文章对应的标签
- private List<TagCustom> tagCustomList;
- //作者信息
- private UserCustom userCustom;
- //文章分页信息
- private Page page;
- //搜索关键词
- private String query;
- public String getQuery() {
- return query;
- }
- public UserCustom getUserCustom() {
- return userCustom;
- }
- public void setUserCustom(UserCustom userCustom) {
- this.userCustom = userCustom;
- }
- public void setQuery(String query) {
- this.query = query;
- }
- public ArticleCustom getArticleCustom() {
- return articleCustom;
- }
- public void setArticleCustom(ArticleCustom articleCustom) {
- this.articleCustom = articleCustom;
- }
- public List<CategoryCustom> getCategoryCustomList() {
- return categoryCustomList;
- }
- public void setCategoryCustomList(List<CategoryCustom> categoryCustomList) {
- this.categoryCustomList = categoryCustomList;
- }
- public List<TagCustom> getTagCustomList() {
- return tagCustomList;
- }
- public void setTagCustomList(List<TagCustom> tagCustomList) {
- this.tagCustomList = tagCustomList;
- }
- public Page getPage() {
- return page;
- }
- public void setPage(Page page) {
- this.page = page;
- }
- }
因为我们要输出文章列表,文章列表至少要包括 文章信息(标题,id 和发布时间),分类信息(分类名称和 id ),标签信息(标签名称和 id ),作者信息(作者昵称和 id )。
对于每篇文章而言,其中分类信息标签信息,在 article 里是以逗号分隔的,也就是我们要用一个 List 集合存储;而文章信息,作者信息和分页信息,用对象存储即可;因为我们还要返回查询输入的关键词,所以还要有一个变量。
其中 ArticleCustom,UserCustom,TagCustom,CategoryCustom 分别是 po 的扩展。
三、分页 类
Page.java
- package com.liuyanzhao.blog.util.others;
- /**
- * 分页
- * Created by 言曌 on 2017/8/26.
- */
- import java.io.Serializable;
- public class Page implements Serializable {
- private static final long serialVersionUID = -3198048449643774660L;
- private int pageNow = 1; // 当前页数
- private int pageSize; // 每页显示记录的条数
- private int totalCount; // 总的记录条数
- private int totalPageCount; // 总的页数
- @SuppressWarnings("unused")
- private int startPos; // 开始位置,从0开始
- @SuppressWarnings("unused")
- private boolean hasFirst;// 是否有首页
- @SuppressWarnings("unused")
- private boolean hasPre;// 是否有前一页
- @SuppressWarnings("unused")
- private boolean hasNext;// 是否有下一页
- @SuppressWarnings("unused")
- private boolean hasLast;// 是否有最后一页
- /**
- * 通过构造函数 传入 总记录数 和 当前页
- * @param totalCount
- * @param pageNow
- */
- public Page(int totalCount, int pageNow,int pageSize) {
- this.totalCount = totalCount;
- this.pageNow = pageNow;
- this.pageSize = pageSize;
- }
- /**
- * 取得总页数,总页数=总记录数/总页数
- * @return
- */
- public int getTotalPageCount() {
- totalPageCount = getTotalCount() / getPageSize();
- return (totalCount % pageSize == 0) ? totalPageCount
- : totalPageCount + 1;
- }
- public void setTotalPageCount(int totalPageCount) {
- this.totalPageCount = totalPageCount;
- }
- public int getPageNow() {
- return pageNow;
- }
- public void setPageNow(int pageNow) {
- this.pageNow = pageNow;
- }
- public int getPageSize() {
- return pageSize;
- }
- public void setPageSize(int pageSize) {
- this.pageSize = pageSize;
- }
- public int getTotalCount() {
- return totalCount;
- }
- public void setTotalCount(int totalCount) {
- this.totalCount = totalCount;
- }
- /**
- * 取得选择记录的初始位置
- * @return
- */
- public int getStartPos() {
- return (pageNow - 1) * pageSize;
- }
- public void setStartPos(int startPos) {
- this.startPos = startPos;
- }
- /**
- * 是否是第一页
- * @return
- */
- public boolean isHasFirst() {
- return (pageNow == 1) ? false : true;
- }
- public void setHasFirst(boolean hasFirst) {
- this.hasFirst = hasFirst;
- }
- /**
- * 是否有首页
- * @return
- */
- public boolean isHasPre() {
- // 如果有首页就有前一页,因为有首页就不是第一页
- return isHasFirst() ? true : false;
- }
- public void setHasPre(boolean hasPre) {
- this.hasPre = hasPre;
- }
- /**
- * 是否有下一页
- * @return
- */
- public boolean isHasNext() {
- // 如果有尾页就有下一页,因为有尾页表明不是最后一页
- return isHasLast() ? true : false;
- }
- public void setHasNext(boolean hasNext) {
- this.hasNext = hasNext;
- }
- /**
- * 是否有尾页
- * @return
- */
- public boolean isHasLast() {
- // 如果不是最后一页就有尾页
- return (pageNow == getTotalCount()) ? false : true;
- }
- public void setHasLast(boolean hasLast) {
- this.hasLast = hasLast;
- }
- }
具体使用,见下
四、dao 层
ArticleCustomMapper.java
- //文章结果查询结果的数量
- public Integer getSearchResultCount(String s) throws Exception;
- //查询文章分页操作
- public List<ArticleCustom> listSearchResultByPage(@Param(value="queryContent") String queryContent,@Param(value="startPos") Integer startPos,@Param(value="pageSize") Integer pageSize) throws Exception;
ArticleCustomMapper.xml
- <!--查询结果统计-->
- <select id="getSearchResultCount" parameterType="String" resultType="Integer">
- SELECT count(*) FROM `article`
- <where>
- article_status > 0 AND
- article_title LIKE "%${value}%" OR
- article_content LIKE "%${value}%"
- </where>
- </select>
- <!--文章查询分页显示-->
- <select id="listSearchResultByPage" resultType="com.liuyanzhao.blog.po.custom.ArticleCustom">
- SELECT
- <include refid="article_table_all_columns"/>
- FROM `article`
- <where>
- article_status > 0 AND
- article_title LIKE '%${queryContent}%' OR
- article_content LIKE '%${queryContent}%'
- limit #{startPos},#{pageSize}
- </where>
- </select>
注意字段名不要写错了,我这里的 article_status 是文章状态,0表示禁用不显示,1表示正常,2表示置顶
五、Service 层
ArticleServiceImpl.java
- //文章查询结果分页
- @Override
- public List<ArticleSearchVo> listSearchResultByPage(HttpServletRequest request, Model model,Integer pageNow,Integer pageSize,String query) throws Exception {
- Page page = null;
- List<ArticleCustom> articleCustomList = new ArrayList<ArticleCustom>();
- int totalCount = articleMapperCustom.getSearchResultCount(query);
- if (pageNow != null) {
- page = new Page(totalCount, pageNow, pageSize);
- articleCustomList = this.articleMapperCustom.listSearchResultByPage(query, page.getStartPos(), page.getPageSize());
- } else {
- page = new Page(totalCount, 1, pageSize);
- articleCustomList = this.articleMapperCustom.listSearchResultByPage(query, page.getStartPos(), page.getPageSize());
- }
- List<ArticleSearchVo> articleSearchVoList = new ArrayList<ArticleSearchVo>();
- //查询结果条数为0,下面的不执行,防止空指针
- if(totalCount!=0) {
- for (int i = 0; i < articleCustomList.size(); i++) {
- ArticleSearchVo articleSearchVo = new ArticleSearchVo();
- //1、将文章信息装到 articleListVoList 中
- ArticleCustom articleCustom = articleCustomList.get(i);
- articleSearchVo.setArticleCustom(articleCustom);
- //2、将分类信息装到 articleListVoList 中
- List<CategoryCustom> categoryCustomList = new ArrayList<CategoryCustom>();
- String categoryIds = articleCustomList.get(i).getArticleCategoryIds();
- String[] cateId = categoryIds.split(",");
- for (int j = 0; j < cateId.length; j++) {
- Category category = categoryMapper.selectByPrimaryKey(Integer.valueOf(cateId[j]));
- CategoryCustom categoryCustom = new CategoryCustom();
- BeanUtils.copyProperties(category, categoryCustom);
- categoryCustomList.add(categoryCustom);
- }
- articleSearchVo.setCategoryCustomList(categoryCustomList);
- //3、获得标签信息
- List<TagCustom> tagCustomList = new ArrayList<TagCustom>();
- String tagIds = articleCustomList.get(i).getArticleTagIds();
- String[] tagId = tagIds.split(",");
- for (int j = 0; j < tagId.length; j++) {
- Tag tag = tagMapper.selectByPrimaryKey(Integer.valueOf(tagId[j]));
- TagCustom tagCustom = new TagCustom();
- BeanUtils.copyProperties(tag, tagCustom);
- tagCustomList.add(tagCustom);
- }
- articleSearchVo.setTagCustomList(tagCustomList);
- //4、获得作者信息
- User user = userMapper.selectByPrimaryKey(articleCustom.getArticleUserId());
- UserCustom userCustom = new UserCustom();
- BeanUtils.copyProperties(user, userCustom);
- articleSearchVo.setUserCustom(userCustom);
- articleSearchVoList.add(articleSearchVo);
- }
- } else {
- //不执行的话,也要创建一个元素,存储分页信息和查询关键字
- ArticleSearchVo articleSearchVo = new ArticleSearchVo();
- articleSearchVoList.add(articleSearchVo);
- }
- //5、page信息存储在第一个元素中
- articleSearchVoList.get(0).setPage(page);
- //6、将查询的关键词存储到第一个元素
- articleSearchVoList.get(0).setQuery(query);
- return articleSearchVoList;
- }
这里的 service 调用了 DAO 层的两个方法,分别是获取查询结果的条数和查询结果分页显示。
六、Controller 层
ArticleController.java
- //搜索实现
- @RequestMapping("/admin/article/search")
- @ResponseBody
- public ModelAndView SearchPageView(HttpServletRequest request,Model model) throws Exception {
- ModelAndView modelAndView = new ModelAndView();
- //设置每页显示的数量
- int pageSize = 5;
- String query = request.getParameter("query");
- List<ArticleSearchVo> articleSearchVoList = articleService.listSearchResultByPage(request,model,null,pageSize,query);
- modelAndView.addObject("articleSearchVoList", articleSearchVoList);
- modelAndView.setViewName("Admin/Article/search");
- return modelAndView;
- }
- //搜索分页实现
- @RequestMapping("/admin/article/p/{pageNow}/search")
- @ResponseBody
- public ModelAndView SearchPageByPageView(HttpServletRequest request, Model model,@PathVariable("pageNow") Integer pageNow) throws Exception {
- ModelAndView modelAndView = new ModelAndView();
- //设置每页显示的数量
- int pageSize = 5;
- String query = request.getParameter("query");
- List<ArticleSearchVo> articleSearchVoList = articleService.listSearchResultByPage(request,model,pageNow,pageSize,query);
- modelAndView.addObject("articleSearchVoList", articleSearchVoList);
- modelAndView.setViewName("/Admin/Article/search");
- return modelAndView;
- }
第一个方法是用户输入查询信息,点击查询按钮的时候,会提交表单到 /admin/article/search 这里。
因为我们设置的查询结果是按分页显示的,如果超过 10 条,会分页。点击分页按钮,会执行第二个方法。
七、视图层
index.jsp
- <form class="layui-form" action="${pageContext.request.contextPath}/admin/article/search">
- <div class="layui-form-item">
- <div class="layui-input-block">
- <input type="text" name="query" required lay-verify="required" placeholder="请输入关键词" autocomplete="off" class="layui-input" >
- <button class="layui-btn" lay-submit lay-filter="formDemo" type="submit">搜索</button>
- </div>
- </div>
- </form>
这个是后台文章列表 首页,有一个这样的搜索框
search.jsp
- <blockquote class="layui-elem-quote">搜索 ${articleSearchVoList[0].query} 找到 ${articleSearchVoList[0].page.totalCount} 条数据</blockquote>
- <form class="layui-form" action="${pageContext.request.contextPath}/admin/article/search">
- <div class="layui-form-item">
- <div class="layui-input-block">
- <input type="text" name="query" required lay-verify="required" placeholder="请输入标题" autocomplete="off" class="layui-input" value="${articleSearchVoList[0].query}" >
- <button class="layui-btn" lay-submit lay-filter="formDemo" type="submit">搜索</button>
- <button class="layui-btn" lay-submit lay-filter="formDemo" style="float: right;">批量删除</button>
- </div>
- </div>
- </form>
- <c:choose>
- <%--查询结果不为0--%>
- <c:when test="${articleSearchVoList[0].page.totalCount!=0}">
- <table class="layui-table">
- <colgroup>
- <col width="25">
- <col width="25">
- <col width="300">
- <col width="200">
- <col width="200">
- <col width="200">
- <col width="100">
- <col width="200">
- </colgroup>
- <thead>
- <tr>
- <th><input type="checkbox" id="allSelect" onclick="javascript:DoCheck()"></th>
- <th>id</th>
- <th>标题</th>
- <th>所属分类</th>
- <th>所带标签</th>
- <th>发布时间</th>
- <th>作者</th>
- <th>操作</th>
- </tr>
- </thead>
- <tbody>
- <c:forEach items="${articleSearchVoList}" var="a">
- <tr>
- <td><input type="checkbox" name="select_id"></td>
- <td>${a.articleCustom.articleId}</td>
- <td><a href="${pageContext.request.contextPath}/article/${a.articleCustom.articleId}" target="_blank">
- ${fn:substring(a.articleCustom.articleTitle, 0,20 )}
- </a></td>
- <td>
- <c:forEach items="${a.categoryCustomList}" var="c">
- <a href="${pageContext.request.contextPath}/category/${c.categoryId}" target="_blank">${c.categoryName}</a>
-
- </c:forEach>
- </td>
- <td>
- <c:forEach items="${a.tagCustomList}" var="t">
- <a href="${pageContext.request.contextPath}/tag/${t.tagId}" target="_blank">${t.tagName}</a>
-
- </c:forEach>
- </td>
- <td>
- <fmt:formatDate value="${a.articleCustom.articlePostTime}" pattern="yyyy-MM-dd HH:mm:ss"/>
- </td>
- <td>
- <a href="${pageContext.request.contextPath}/admin/user/${a.userCustom.userId}">
- ${a.userCustom.userNickname}
- </a>
- </td>
- <td>
- <a href="editArticle">编辑</a>丨
- <a href="">删除</a>
- </td>
- </tr>
- </c:forEach>
- </tbody>
- </table>
- <%--分页 start--%>
- <nav class="navigation pagination" role="navigation">
- <div class="nav-links">
- <c:choose>
- <c:when test="${articleSearchVoList[0].page.totalPageCount <= 3 }">
- <c:set var="begin" value="1"/>
- <c:set var="end" value="${articleSearchVoList[0].page.totalPageCount }"/>
- </c:when>
- <c:otherwise>
- <c:set var="begin" value="${articleSearchVoList[0].page.pageNow-1 }"/>
- <c:set var="end" value="${articleSearchVoList[0].page.pageNow + 2}"/>
- <c:if test="${begin < 2 }">
- <c:set var="begin" value="1"/>
- <c:set var="end" value="3"/>
- </c:if>
- <c:if test="${end > articleSearchVoList[0].page.totalPageCount }">
- <c:set var="begin" value="${articleSearchVoList[0].page.totalPageCount-2 }"/>
- <c:set var="end" value="${articleSearchVoList[0].page.totalPageCount }"/>
- </c:if>
- </c:otherwise>
- </c:choose>
- <%--上一页 --%>
- <c:choose>
- <c:when test="${articleSearchVoList[0].page.pageNow eq 1 }">
- <%--当前页为第一页,隐藏上一页按钮--%>
- </c:when>
- <c:otherwise>
- <a class="page-numbers" href="${pageContext.request.contextPath}/admin/article/p/${articleSearchVoList[0].page.pageNow-1}/search?query=${articleSearchVoList[0].query}" >
- <i class="layui-icon"></i>
- </a>
- </c:otherwise>
- </c:choose>
- <%--显示第一页的页码--%>
- <c:if test="${begin >= 2 }">
- <a class="page-numbers" href="${pageContext.request.contextPath}/admin/article/p/1/search?query=${articleSearchVoList[0].query}">1</a>
- </c:if>
- <%--显示点点点--%>
- <c:if test="${begin > 2 }">
- <span class="page-numbers dots">…</span>
- </c:if>
- <%--打印 页码--%>
- <c:forEach begin="${begin }" end="${end }" var="i">
- <c:choose>
- <c:when test="${i eq articleSearchVoList[0].page.pageNow }">
- <a class="page-numbers current" >${i}</a>
- </c:when>
- <c:otherwise>
- <a class="page-numbers" href="${pageContext.request.contextPath}/admin/article/p/${i}/search?query=${articleSearchVoList[0].query}">${i }</a>
- </c:otherwise>
- </c:choose>
- </c:forEach>
- <%-- 显示点点点 --%>
- <c:if test="${end < articleSearchVoList[0].page.totalPageCount-1 }">
- <span class="page-numbers dots">…</span>
- </c:if>
- <%-- 显示最后一页的数字 --%>
- <c:if test="${end < articleSearchVoList[0].page.totalPageCount }">
- <a href="${pageContext.request.contextPath}/admin/article/p/${articleSearchVoList[0].page.totalPageCount}/search?query=${articleSearchVoList[0].query}">
- ${articleSearchVoList[0].page.totalPageCount}
- </a>
- </c:if>
- <%--下一页 --%>
- <c:choose>
- <c:when test="${articleSearchVoList[0].page.pageNow eq articleSearchVoList[0].page.totalPageCount }">
- <%--到了尾页隐藏,下一页按钮--%>
- </c:when>
- <c:otherwise>
- <a class="page-numbers" href="${pageContext.request.contextPath}/admin/article/p/${articleSearchVoList[0].page.pageNow+1}/search?query=${articleSearchVoList[0].query}">
- <i class="layui-icon"></i>
- </a>
- </c:otherwise>
- </c:choose>
- </div>
- </nav>
- <%--分页 end--%>
- </c:when>
- <%--查询结果为0--%>
- <c:otherwise>
- <center><br>很遗憾,没有查询到带有 <font style="color: red;"> ${articleSearchVoList[0].query} </font> 的内容,换一个关键词再试试吧。</center>
- </c:otherwise>
- </c:choose>
注意:博主后台使用的是 layui 的框架,如果你没有使用,请忽略一些样式代码。分页部分代码也稍微复杂,比较累赘。
八、效果预览
为了方便截图,这里设置一页显示五条数据
本文地址:https://liuyanzhao.com/6241.html
2018年08月30日 16:30:28
好东西
2018年08月30日 17:33:28
帮忙点击一下广告啊
2017年09月19日 15:55:00
干货干货哈哈哈收入囊中