• 从头开始基于Maven搭建SpringMVC+Mybatis项目(3)


    接上文内容,本节介绍基于Mybatis的查询和分页功能,并展示一个自定义的分页标签,可重复使用以简化JSP页面的开发。

    从头阅读传送门

    在上一节中,我们已经使用Maven搭建好了项目的基础结构,包括一个父项目petstore-parent和数据库持久层模块petstore-persist及Web站点petstore-web,现在来为petstore-web添加一些功能。对于初学者来说,可能第一个遇到的较复杂问题就是分页查询,那么就先从解决它开始。

    看一下完成的效果:

    上面是四个可选的查询条件,用户可以根据需要组合查询条件。

    中间是符合条件的数据展示表格,对查询结果可以执行修改和删除操作,但是暂未实现。

    最下面是一个分页导航栏,以自定义标签(Tag)技术实现,可复用到多个jsp页面。

    下面来介绍关键步骤和代码。首先是petstore-persist模块,目录结构如下:

    Product.Java是一个普通的Java Bean,这里略过。ProductMapper.java中定义了两个方法:

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. package com.example.petstore.persist.model;  
    2.   
    3. import java.util.List;  
    4. import org.apache.ibatis.annotations.Param;  
    5.   
    6. public interface ProductMapper {  
    7.       
    8.     /** 
    9.      * 查询符合条件的记录总数 
    10.      * @param id 
    11.      * @param name 
    12.      * @param fromPrice 
    13.      * @param toPrice 
    14.      * @return 
    15.      */  
    16.     int matches(@Param(value="id") int id, @Param(value="name") String name, @Param(value="fromPrice") float fromPrice, @Param(value="toPrice") float toPrice);  
    17.       
    18.     /** 
    19.      * 按查询条件及分页条件分段查询记录 
    20.      * @param id 
    21.      * @param name 
    22.      * @param fromPrice 
    23.      * @param toPrice 
    24.      * @param fetchIndex 
    25.      * @param fetchCount 
    26.      * @return 
    27.      */  
    28.     List<Product> findProducts(@Param(value="id") int id, @Param(value="name") String name, @Param(value="fromPrice") float fromPrice, @Param(value="toPrice") float toPrice, @Param(value="fetchIndex") int fetchIndex, @Param(value="fetchCount") int fetchCount);  
    29. }  

    使用时,首先调用matches方法获得符合条件的记录总数,然后根据每页显示的记录数和当前页数计算读取数据的Limit偏移量和记录数,再调用findProducts方法读取数据。两个方法的参数都使用了@Param注解,例如@Param(value="id") int id,在映射文件中,可通过#{id}的格式来使用这个参数。

    在Product.xml中添加两个方法的SQL映射:

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
    3. <mapper namespace="com.example.petstore.persist.model.ProductMapper">  
    4.     <resultMap type="com.example.petstore.persist.model.Product"  
    5.         id="productMap">  
    6.         <id column="p_id" property="id" />  
    7.         <result column="p_name" property="name" />  
    8.         <result column="p_price" property="price" />  
    9.     </resultMap>  
    10.     <select id="matches" resultType="int">  
    11.         select count(*) from t_product  
    12.         <where>  
    13.             <if test="id>0">  
    14.                 p_id=#{id}  
    15.             </if>  
    16.             <if test="name!=null and name!='' ">  
    17.                 and locate(#{name},p_name)>0  
    18.             </if>  
    19.             <if test="fromPrice>-1">  
    20.                 and p_price>=#{fromPrice}  
    21.             </if>  
    22.             <if test="toPrice>-1">  
    23.                 and p_price<=#{toPrice}  
    24.             </if>  
    25.         </where>  
    26.     </select>  
    27.     <select id="findProducts" resultMap="productMap">  
    28.         select * from t_product  
    29.         <where>  
    30.             <if test="id>0">  
    31.                 p_id=#{id}  
    32.             </if>  
    33.             <if test="name!=null and name!='' ">  
    34.                 and locate(#{name},p_name)>0  
    35.             </if>  
    36.             <if test="fromPrice>-1">  
    37.                 and p_price>=#{fromPrice}  
    38.             </if>  
    39.             <if test="toPrice>-1">  
    40.                 and p_price<=#{toPrice}  
    41.             </if>  
    42.         </where>  
    43.         limit #{fetchIndex},#{fetchCount}  
    44.     </select>  
    45. </mapper>  

    可以看到上面两个方法中,就是通过<where><if>等元素来组装查询SQL。Mybatis的优点之一就是直接使用SQL语法,有SQL基础的情况下非常容易上手。

    下面进入petstore-web模块,先来看整体结构:

    其中com.example.petstore.web.tag.PagingTag.java是分页标签类,关键代码:

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. private int pageIndex = 1;  //当前页数  
    2. private int pageSize = 20;  //默认每页行数  
    3. private int pageCount = 0;  //记录总页数  
    4. private int itemCount = 0;  //记录总条数  
    5. private int numCount = 10;  //分页栏数字导航链接个数  
    6.   
    7. @Override  
    8. public void doTag() throws JspException, IOException {  
    9.     JspWriter out = this.getJspContext().getOut();  
    10.       
    11.     out.write("<script type="text/javascript">function navigatorPage(pageIndex) {document.getElementById('pageIndex').value = pageIndex;document.forms[0].submit();}</script>");  
    12.       
    13.     out.write("每页显示");  
    14.     out.write("<select id='pageSize' name='pageSize' onchange='navigatorPage(" + pageIndex + ")'>");  
    15.     out.write("<option value='5'" + (pageSize == 5 ? " selected='true'" : "") + ">5</option>");  
    16.     out.write("<option value='10'" + (pageSize == 10 ? " selected='true'" : "") + ">10</option>");  
    17.     out.write("<option value='20'" + (pageSize == 20 ? " selected='true'" : "") + ">20</option>");  
    18.     out.write("<option value='50'" + (pageSize == 50 ? " selected='true'" : "") + ">50</option>");  
    19.     out.write("<option value='100'" + (pageSize == 100 ? " selected='true'" : "") + ">100</option>");  
    20.     out.write("<option value='500'" + (pageSize == 500 ? " selected='true'" : "") + ">500</option>");  
    21.     out.write("</select>");  
    22.     out.write("条    ");  
    23.       
    24.     out.write(pageIndex + "/" + pageCount + "页    ");  
    25.     out.write("共" + itemCount + "条记录    ");  
    26.       
    27.     out.write("<input type='button' value='第一页' onclick='javascript:navigatorPage(1);'" + (pageIndex > 1 ? "" : " disabled='true'") + " />  ");  
    28.     out.write("<input type='button' value='上一页' onclick='javascript:navigatorPage(" + (pageIndex - 1) + ");'" + (pageIndex > 1 ? "" : " disabled='true'") + " />  ");  
    29.       
    30.     //数字导航栏  
    31.     int iStartIndex = 1;  
    32.     int iEndIndex = pageCount;  
    33.     if(pageCount <= numCount) {  
    34.     } else if ((pageIndex + (numCount + 1) / 2) > pageCount) {  
    35.         iStartIndex = pageCount - (numCount - 1);  
    36.         iEndIndex = pageCount;  
    37.        } else if (pageIndex <= (numCount + 1) / 2) {  
    38.         iEndIndex = numCount;  
    39.        } else {  
    40.            if (numCount % 2 == 0) {  
    41.             iStartIndex = pageIndex - numCount / 2;  
    42.             iEndIndex = pageIndex + (numCount - 1) / 2;  
    43.            } else {  
    44.             iStartIndex = pageIndex - numCount / 2;  
    45.             iEndIndex = pageIndex + numCount / 2;  
    46.            }  
    47.        }  
    48.     for(int i = iStartIndex; i <= iEndIndex; i++) {  
    49.         if(i == pageIndex) {  
    50.             out.write("<strong>" + i + "</strong>  ");  
    51.         } else {  
    52.             out.write("<a href='javascript:navigatorPage(" + i + ");'>" + i + "</a>  ");  
    53.         }  
    54.     }  
    55.       
    56.     out.write("<input type='button' value='下一页' onclick='javascript:navigatorPage(" + (pageIndex + 1) + ");'" + (pageIndex < pageCount ? "" : " disabled='true'") + " />  ");  
    57.     out.write("<input type='button' value='最后页' onclick='javascript:navigatorPage(" + pageCount + ");'" + (pageIndex < pageCount ? "" : " disabled='true'") + " />");  
    58.     out.write("<input type='hidden' id='pageIndex' name='pageIndex' value='" + pageIndex + "'/>");  
    59. }  

    接下来还需要一个标签配置文件来声明这个标签的使用方法。

    在WEB-INF下建立目录tld,然后添加pagingTag.tld,内容如下:

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <!DOCTYPE taglib  
    3.   PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"  
    4.   "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">  
    5. <taglib>  
    6.     <tlib-version>2.0</tlib-version>  
    7.     <jsp-version>1.2</jsp-version>  
    8.     <short-name>Paging</short-name>  
    9.     <uri>http://blog.csdn.net/autfish/tag/</uri>  
    10.     <display-name>Paging Tag</display-name>  
    11.     <description>Paging Tag library</description>  
    12.   
    13.     <tag>  
    14.         <name>pagingTag</name>  
    15.         <tag-class>com.example.petstore.web.tag.PagingTag</tag-class>  
    16.         <body-content>empty</body-content>  
    17.         <description>create navigation for paging</description>  
    18.         <attribute>  
    19.             <name>pageIndex</name>  
    20.             <rtexprvalue>true</rtexprvalue>  
    21.         </attribute>  
    22.         <attribute>  
    23.             <name>pageSize</name>  
    24.             <rtexprvalue>true</rtexprvalue>  
    25.         </attribute>  
    26.         <attribute>  
    27.             <name>pageCount</name>  
    28.             <rtexprvalue>true</rtexprvalue>  
    29.         </attribute>  
    30.         <attribute>  
    31.             <name>itemCount</name>  
    32.             <rtexprvalue>true</rtexprvalue>  
    33.         </attribute>  
    34.     </tag>  
    35. </taglib>  

    注意其中的uri元素,这里并不需要配置真实存在的url,但该uri在你的classpath中应保持唯一,不能被其他组件声明使用。

    在web.xml中启用这个标签:

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <jsp-config>  
    2.     <taglib>  
    3.         <taglib-uri>http://blog.csdn.net/autfish/tag/</taglib-uri>  
    4.         <taglib-location>/WEB-INF/tld/pagingTag.tld</taglib-location>  
    5.     </taglib>  
    6. </jsp-config>  

    在jsp中使用:

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <%@ taglib prefix="my" uri="http://blog.csdn.net/autfish/tag/" %>  
    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <my:pagingTag pageIndex="${contentModel.pageIndex}" pageSize="${contentModel.pageSize}" pageCount="${contentModel.pageCount}" itemCount="${contentModel.itemCount}" />  

    对于四个属性的赋值,contentModel是一个PagingList.java类的实例,用于辅助分页,由分页属性和数据表构成,在Controller中填充数据并传递到视图JSP。属性如下:

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. private int pageIndex = 1;  
    2. private int pageSize = 20;  
    3. private int pageCount = 0;  
    4. private int itemCount = 0;  
    5.   
    6. private List<T> items;  

    Controller代码:

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. @Controller  
    2. @RequestMapping("/product")  
    3. public class ProductController {  
    4.   
    5.     @Autowired  
    6.     private ProductService productService;  
    7.   
    8.     @RequestMapping(value="/list")  
    9.     public String listProduct(Model model, @ModelAttribute("searchModel") SearchModel formModel,   
    10.             @RequestParam(value=PagingList.PAGE_INDEX_NAME, defaultValue="1") int pageIndex,  
    11.             @RequestParam(value=PagingList.PAGE_SIZE_NAME, defaultValue="10") int pageSize) {  
    12.   
    13.         int id = 0;  
    14.         String name = "";  
    15.         float fromPrice = -1;  
    16.         float toPrice = -1;  
    17.         if(formModel != null) {  
    18.             id = NumberUtils.toInt(formModel.getId(), 0);  
    19.             name = formModel.getName();  
    20.             fromPrice = NumberUtils.toFloat(formModel.getFromPrice(), -1);  
    21.             toPrice = NumberUtils.toFloat(formModel.getToPrice(), -1);  
    22.         }  
    23.         model.addAttribute("searchModel", formModel);  
    24.         PagingList<Product> contentModel = this.productService.findProducts(id, name, fromPrice, toPrice, pageIndex, pageSize);  
    25.         model.addAttribute("contentModel", contentModel);  
    26.   
    27.         return "product/list";  
    28.     }  
    29. }  

    Controller中注入了一个ProductService的实例,用于管理持久层的调用,主要代码如下:

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. @Service  
    2. public class ProductServiceStdImpl implements ProductService {  
    3.   
    4.     @Autowired  
    5.     private ProductMapper productMapper;  
    6.   
    7.     @Override  
    8.     public PagingList<Product> findProducts(int id, String name,  
    9.             float fromPrice, float toPrice, int pageIndex, int pageSize) {  
    10.         int total = this.productMapper.matches(id, name, fromPrice, toPrice);  
    11.         int pageCount = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;  
    12.         if(pageIndex > pageCount)  
    13.             pageIndex = pageCount;  
    14.         int fetchIndex = (pageIndex - 1) * pageSize;  
    15.         int fetchCount = fetchIndex + pageSize > total ? (total - fetchIndex) : pageSize;  
    16.         List<Product> list = this.productMapper.findProducts(id, name, fromPrice, toPrice, fetchIndex, fetchCount);  
    17.         PagingList<Product> paging = new PagingList<Product>();  
    18.         paging.setItemCount(total);  
    19.         paging.setPageCount(pageCount);  
    20.         paging.setPageIndex(pageIndex);  
    21.         paging.setPageSize(pageSize);  
    22.         paging.setItems(list);  
    23.         return paging;  
    24.     }  
    25.   
    26. }  

    限于篇幅,不能把所有的源码一一粘贴,有兴趣可以下载源码。

    在WEB容器如tomcat中运行petstore-web模块,使用http://localhost:8080/petstore-web/product/list访问,顺利的话就看到了一开始的画面。如果出错,比对源码检查差异即可。

    总结

    分页查询功能使用频繁,且开发比较复杂,按需定制一套可复用的分页组件对提高开发效率有很大的帮助。下一节我们继续完善Web模块,增加增删改查以及权限控制功能。

    本节源码下载

  • 相关阅读:
    分不清npm cnpm npx nvm ?
    gulp 中对于css文件的压缩合并出现的unable to minify JavaScript问题
    JS实现选项卡和JQ实现选项卡
    前端性能的优化
    JS中事件绑定的方式以及事件监听和事件的委托
    简易轮播图和无缝轮播图的实现
    ES6中对象的扩展以及新增方法
    javascript的基本介绍和发展
    this浅谈
    浅谈DOM的概念和作用
  • 原文地址:https://www.cnblogs.com/sa-dan/p/6836971.html
Copyright © 2020-2023  润新知