首页 > 代码库 > 基于Maven搭建SpringMVC+Mybatis项目(3)

基于Maven搭建SpringMVC+Mybatis项目(3)

 | 从高考,到程序员      CSDN日报20170620——《找一个好工作,谈一份好薪水》      6 月书讯 | 最受欢迎的 SQL 入门书重磅升级
  

    

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

标签:               JAVAmavenspring mvcmybatis                     
本文章已收录于:        
技术分享 Java 知识库
    
<style>.embody { padding: 10px 10px 10px; margin: 0 -20px; border-bottom: solid 1px #ededed } .embody_b { margin: 0; padding: 10px 0 } .embody .embody_t,.embody .embody_c { display: inline-block; margin-right: 10px } .embody_t { font-size: 12px; color: #999 } .embody_c { font-size: 12px } .embody_c img,.embody_c em { display: inline-block; vertical-align: middle } .embody_c img { width: 30px; height: 30px } .embody_c em { margin: 0 20px 0 10px; color: #333; font-style: normal }</style>
    
      
技术分享             分类:        
作者同类文章X
                               

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

    从头阅读传送门

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

    看一下完成的效果:

    技术分享

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

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

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

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

    技术分享

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

     

    package com.example.petstore.persist.model;
    
    import java.util.List;
    import org.apache.ibatis.annotations.Param;
    
    public interface ProductMapper {
    	
    	/**
    	 * 查询符合条件的记录总数
    	 * @param id
    	 * @param name
    	 * @param fromPrice
    	 * @param toPrice
    	 * @return
    	 */
    	int matches(@Param(value="http://www.mamicode.com/id") int id, @Param(value="http://www.mamicode.com/name") String name, @Param(value="http://www.mamicode.com/fromPrice") float fromPrice, @Param(value="http://www.mamicode.com/toPrice") float toPrice);
    	
    	/**
    	 * 按查询条件及分页条件分段查询记录
    	 * @param id
    	 * @param name
    	 * @param fromPrice
    	 * @param toPrice
    	 * @param fetchIndex
    	 * @param fetchCount
    	 * @return
    	 */
    	List<Product> findProducts(@Param(value="http://www.mamicode.com/id") int id, @Param(value="http://www.mamicode.com/name") String name, @Param(value="http://www.mamicode.com/fromPrice") float fromPrice, @Param(value="http://www.mamicode.com/toPrice") float toPrice, @Param(value="http://www.mamicode.com/fetchIndex") int fetchIndex, @Param(value="http://www.mamicode.com/fetchCount") int fetchCount);
    }
    使用时,首先调用matches方法获得符合条件的记录总数,然后根据每页显示的记录数和当前页数计算读取数据的Limit偏移量和记录数,再调用findProducts方法读取数据。两个方法的参数都使用了@Param注解,例如@Param(value="http://www.mamicode.com/id") int id,在映射文件中,可通过#{id}的格式来使用这个参数。

     

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

     

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.example.petstore.persist.model.ProductMapper">
    	<resultMap type="com.example.petstore.persist.model.Product"
    		id="productMap">
    		<id column="p_id" property="id" />
    		<result column="p_name" property="name" />
    		<result column="p_price" property="price" />
    	</resultMap>
    	<select id="matches" resultType="int">
    		select count(*) from t_product
    		<where>
    			<if test="id>0">
    				p_id=#{id}
    			</if>
    			<if test="name!=null and name!=‘‘ ">
    				and locate(#{name},p_name)>0
    			</if>
    			<if test="fromPrice>-1">
    				and p_price>=#{fromPrice}
    			</if>
    			<if test="toPrice>-1">
    				and p_price<=#{toPrice}
    			</if>
    		</where>
    	</select>
    	<select id="findProducts" resultMap="productMap">
    		select * from t_product
    		<where>
    			<if test="id>0">
    				p_id=#{id}
    			</if>
    			<if test="name!=null and name!=‘‘ ">
    				and locate(#{name},p_name)>0
    			</if>
    			<if test="fromPrice>-1">
    				and p_price>=#{fromPrice}
    			</if>
    			<if test="toPrice>-1">
    				and p_price<=#{toPrice}
    			</if>
    		</where>
    		limit #{fetchIndex},#{fetchCount}
    	</select>
    </mapper>

     

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

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

    技术分享

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

     

    	private int pageIndex = 1;  //当前页数
    	private int pageSize = 20;  //默认每页行数
    	private int pageCount = 0;  //记录总页数
    	private int itemCount = 0;  //记录总条数
    	private int numCount = 10;  //分页栏数字导航链接个数
    	
    	@Override
    	public void doTag() throws JspException, IOException {
    		JspWriter out = this.getJspContext().getOut();
    		
    		out.write("<script type=\"text/javascript\">function navigatorPage(pageIndex) {document.getElementById(‘pageIndex‘).value = http://www.mamicode.com/pageIndex;document.forms[0].submit();}</script>");
    		
    		out.write("每页显示");
    		out.write("<select id=‘pageSize‘ name=‘pageSize‘ onchange=‘navigatorPage(" + pageIndex + ")‘>");
    		out.write("<option value=http://www.mamicode.com/‘5‘" + (pageSize == 5 ? " selected=‘true‘" : "") + ">5</option>");
    		out.write("<option value=http://www.mamicode.com/‘10‘" + (pageSize == 10 ? " selected=‘true‘" : "") + ">10</option>");
    		out.write("<option value=http://www.mamicode.com/‘20‘" + (pageSize == 20 ? " selected=‘true‘" : "") + ">20</option>");
    		out.write("<option value=http://www.mamicode.com/‘50‘" + (pageSize == 50 ? " selected=‘true‘" : "") + ">50</option>");
    		out.write("<option value=http://www.mamicode.com/‘100‘" + (pageSize == 100 ? " selected=‘true‘" : "") + ">100</option>");
    		out.write("<option value=http://www.mamicode.com/‘500‘" + (pageSize == 500 ? " selected=‘true‘" : "") + ">500</option>");
    		out.write("</select>");
    		out.write("条    ");
    		
    		out.write(pageIndex + "/" + pageCount + "页    ");
    		out.write("共" + itemCount + "条记录    ");
    		
    		out.write("<input type=‘button‘ value=‘第一页‘ onclick=‘javascript:navigatorPage(1);‘" + (pageIndex > 1 ? "" : " disabled=‘true‘") + " />  ");
    		out.write("<input type=‘button‘ value=‘上一页‘ onclick=‘javascript:navigatorPage(" + (pageIndex - 1) + ");‘" + (pageIndex > 1 ? "" : " disabled=‘true‘") + " />  ");
    		
    		//数字导航栏
    		int iStartIndex = 1;
    		int iEndIndex = pageCount;
    		if(pageCount <= numCount) {
    		} else if ((pageIndex + (numCount + 1) / 2) > pageCount) {
    			iStartIndex = pageCount - (numCount - 1);
    			iEndIndex = pageCount;
            } else if (pageIndex <= (numCount + 1) / 2) {
            	iEndIndex = numCount;
            } else {
                if (numCount % 2 == 0) {
                	iStartIndex = pageIndex - numCount / 2;
                	iEndIndex = pageIndex + (numCount - 1) / 2;
                } else {
                	iStartIndex = pageIndex - numCount / 2;
                	iEndIndex = pageIndex + numCount / 2;
                }
            }
    		for(int i = iStartIndex; i <= iEndIndex; i++) {
    			if(i == pageIndex) {
    				out.write("<strong>" + i + "</strong>  ");
    			} else {
    				out.write("<a href=‘javascript:navigatorPage(" + i + ");‘>" + i + "</a>  ");
    			}
    		}
    		
    		out.write("<input type=‘button‘ value=‘下一页‘ onclick=‘javascript:navigatorPage(" + (pageIndex + 1) + ");‘" + (pageIndex < pageCount ? "" : " disabled=‘true‘") + " />  ");
    		out.write("<input type=‘button‘ value=‘最后页‘ onclick=‘javascript:navigatorPage(" + pageCount + ");‘" + (pageIndex < pageCount ? "" : " disabled=‘true‘") + " />");
    		out.write("<input type=‘hidden‘ id=‘pageIndex‘ name=‘pageIndex‘ value=http://www.mamicode.com/‘" + pageIndex + "‘/>");
    	}
    接下来还需要一个标签配置文件来声明这个标签的使用方法。

     

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

     

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE taglib
      PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
      "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
    <taglib>
    	<tlib-version>2.0</tlib-version>
    	<jsp-version>1.2</jsp-version>
    	<short-name>Paging</short-name>
    	<uri>http://blog.csdn.net/autfish/tag/</uri>
    	<display-name>Paging Tag</display-name>
    	<description>Paging Tag library</description>
    
    	<tag>
    		<name>pagingTag</name>
    		<tag-class>com.example.petstore.web.tag.PagingTag</tag-class>
    		<body-content>empty</body-content>
    		<description>create navigation for paging</description>
    		<attribute>
    			<name>pageIndex</name>
    			<rtexprvalue>true</rtexprvalue>
    		</attribute>
    		<attribute>
    			<name>pageSize</name>
    			<rtexprvalue>true</rtexprvalue>
    		</attribute>
    		<attribute>
    			<name>pageCount</name>
    			<rtexprvalue>true</rtexprvalue>
    		</attribute>
    		<attribute>
    			<name>itemCount</name>
    			<rtexprvalue>true</rtexprvalue>
    		</attribute>
    	</tag>
    </taglib>
    注意其中的uri元素,这里并不需要配置真实存在的url,但该uri在你的classpath中应保持唯一,不能被其他组件声明使用。

     

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

     

    	<jsp-config>
    		<taglib>
    			<taglib-uri>http://blog.csdn.net/autfish/tag/</taglib-uri>
    			<taglib-location>/WEB-INF/tld/pagingTag.tld</taglib-location>
    		</taglib>
    	</jsp-config>
    在jsp中使用:

     

     

    <%@ taglib prefix="my" uri="http://blog.csdn.net/autfish/tag/" %>
    <my:pagingTag pageIndex="${contentModel.pageIndex}" pageSize="${contentModel.pageSize}" pageCount="${contentModel.pageCount}" itemCount="${contentModel.itemCount}" />
    对于四个属性的赋值,contentModel是一个PagingList.java类的实例,用于辅助分页,由分页属性和数据表构成,在Controller中填充数据并传递到视图JSP。属性如下:

     

     

    	private int pageIndex = 1;
    	private int pageSize = 20;
    	private int pageCount = 0;
    	private int itemCount = 0;
    	
    	private List<T> items;
    Controller代码:

     

     

    @Controller
    @RequestMapping("/product")
    public class ProductController {
    
    	@Autowired
    	private ProductService productService;
    
    	@RequestMapping(value="http://www.mamicode.com/list")
    	public String listProduct(Model model, @ModelAttribute("searchModel") SearchModel formModel, 
    			@RequestParam(value=http://www.mamicode.com/PagingList.PAGE_INDEX_NAME, defaultValue="1") int pageIndex,
    			@RequestParam(value=http://www.mamicode.com/PagingList.PAGE_SIZE_NAME, defaultValue="10") int pageSize) {
    
    		int id = 0;
    		String name = "";
    		float fromPrice = -1;
    		float toPrice = -1;
    		if(formModel != null) {
    			id = NumberUtils.toInt(formModel.getId(), 0);
    			name = formModel.getName();
    			fromPrice = NumberUtils.toFloat(formModel.getFromPrice(), -1);
    			toPrice = NumberUtils.toFloat(formModel.getToPrice(), -1);
    		}
    		model.addAttribute("searchModel", formModel);
    		PagingList<Product> contentModel = this.productService.findProducts(id, name, fromPrice, toPrice, pageIndex, pageSize);
    		model.addAttribute("contentModel", contentModel);
    
    		return "product/list";
    	}
    }
    Controller中注入了一个ProductService的实例,用于管理持久层的调用,主要代码如下:

     

     

    @Service
    public class ProductServiceStdImpl implements ProductService {
    
    	@Autowired
    	private ProductMapper productMapper;
    
    	@Override
    	public PagingList<Product> findProducts(int id, String name,
    			float fromPrice, float toPrice, int pageIndex, int pageSize) {
    		int total = this.productMapper.matches(id, name, fromPrice, toPrice);
    		int pageCount = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;
    		if(pageIndex > pageCount)
    			pageIndex = pageCount;
    		int fetchIndex = (pageIndex - 1) * pageSize;
    		int fetchCount = fetchIndex + pageSize > total ? (total - fetchIndex) : pageSize;
    		List<Product> list = this.productMapper.findProducts(id, name, fromPrice, toPrice, fetchIndex, fetchCount);
    		PagingList<Product> paging = new PagingList<Product>();
    		paging.setItemCount(total);
    		paging.setPageCount(pageCount);
    		paging.setPageIndex(pageIndex);
    		paging.setPageSize(pageSize);
    		paging.setItems(list);
    		return paging;
    	}
    
    }

     

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

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

    总结

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

     

    本节源码下载

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

    基于Maven搭建SpringMVC+Mybatis项目(3)