从头开始基于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模块,增加增删改查以及权限控制功能。

本节源码下载

原文地址:https://www.cnblogs.com/sa-dan/p/6836971.html