SSH框架下的条件查询及查询结果分页

之前看过许多别人BLOG中写的方案,但是没有自己动手从数据库、后端、前端全过程实现的话,难以发现自己存在问题。一是没有形成自己独有的解决方案,二是不知道理论和现实之间的差异。

本文例子的使用场景:数据库中存储了一系列商品信息,字段包括商品名字和商品价格。
需要实现功能:商品名字支持 模糊查询,商品价格 支持精确查询。查询结果分页显示。

一、数据准备
SSH框架下的条件查询及查询结果分页_第1张图片

二、项目结构
SSH框架下的条件查询及查询结果分页_第2张图片
按照惯例,创建的是Maven项目,方便依赖Jar包的导入。vo包存放的是封装好的分页数据类,提供前端进行数据展示。

三、配置文件的编写
省略web.xml和两个peoperties,因为没有特别需要注意的点。
实际在写程序时,我生成了配置文件后不会直接去写。我习惯先写一部分通用的配置,例如数据库连接池和通用事务管理器等。其他的会留到写完类文件之后,需要进行单元测试时才配置到Spring容器中。

1、Spring配置文件。因为Bean的数量少,我偷懒采用直接写的方式。没有用到包扫描。


<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop.xsd
         http://www.springframework.org/schema/tx
         http://www.springframework.org/schema/tx/spring-tx.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd">
    
    <context:property-placeholder location="classpath:jdbc.properties" />

    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init" destroy-method="close">
        
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        
        <property name="initialSize" value="1" />
        <property name="minIdle" value="1" />
        <property name="maxActive" value="20" />
    bean>

    
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialet">org.hibernate.dialect.MySQLDialectprop>
                <prop key="hibernate.show_sql">trueprop>
                <prop key="hibernate.format_sql">trueprop>
                <prop key="hibernate.hbm2ddl.auto">updateprop>
            props>
        property>
        
        <property name="mappingResources">
            <list>
                <value>org/lzx/sshm/domain/Product.hbm.xmlvalue>
                <value>org/lzx/sshm/domain/Department.hbm.xmlvalue>
                <value>org/lzx/sshm/domain/Employee.hbm.xmlvalue>
            list>
        property>
    bean>


    
    <bean id="productAction" class="org.lzx.sshm.action.ProductAction"
        scope="prototype">
        
        <property name="productService" ref="productService">property>
    bean>

    
    <bean id="productService" class="org.lzx.sshm.service.impl.ProductServiceImpl">
        <property name="productDao" ref="productDao" />
    bean>

    
    <bean id="productDao" class="org.lzx.sshm.dao.impl.ProductDaoImpl">
        <property name="sessionFactory" ref="sessionFactory">property>
    bean>

    
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory">property>
    bean>
    
    <tx:annotation-driven transaction-manager="transactionManager" />
beans>

2、Struts的配置文件



<struts>
    <package name="ssh" extends="struts-default" namespace="/">
        
        
        <action name="product_*" class="productAction" method="{1}">
            
            <result name="findAll">/jsp/show2.jspresult>
        action>
        
        <action name="viewProduct">
            <result>/jsp/show4.jspresult>
        action>
    package>
struts>

3、Hibernate交给Spring容器管理之后,就不需要写cfg.xml文件了。
右击Product实体类,使用Hibernate Tools自动生成对应的hbm.xml文件。




<hibernate-mapping>
    <class name="org.lzx.sshm.domain.Product" table="PRODUCT">
        <id name="pid" type="java.lang.Integer">
            <column name="PID" />
            <generator class="native" />
        id>
        <property name="pname" type="java.lang.String" length="20">
            <column name="PNAME" />
        property>
        <property name="price" type="java.lang.Double">
            <column name="PRICE" />
        property>
    class>
hibernate-mapping>

四、实体类编写

商品实体类 Product.java

public class Product {
    private Integer pid;
    private String pname;
    private Double price;
    //省略构造函数和get/set方法  
}

分页对象VO Pager.java

public class Pager implements Serializable {

    private static final long serialVersionUID = -8741766802354222579L;

    private int pageSize; // 每页显示多少条记录

    private int currentPage; //当前第几页数据

    private int totalRecord; // 一共多少条记录

    private int totalPage; // 一共多少页记录

    private List dataList; //要显示的数据

    public Pager(int pageNum, int pageSize, List sourceList){
        if(sourceList == null || sourceList.isEmpty()){
            return;
        }

        // 总记录条数
        this.totalRecord = sourceList.size();

        // 每页显示多少条记录
        this.pageSize = pageSize;

        //获取总页数
        this.totalPage = this.totalRecord / this.pageSize;
        if(this.totalRecord % this.pageSize !=0){
            this.totalPage = this.totalPage + 1;
        }

        // 当前第几页数据
        this.currentPage = this.totalPage < pageNum ?  this.totalPage : pageNum;

        // 起始索引
        int fromIndex   = this.pageSize * (this.currentPage -1);

        // 结束索引
        int toIndex  = this.pageSize * this.currentPage > this.totalRecord ? this.totalRecord : this.pageSize * this.currentPage;

        this.dataList = sourceList.subList(fromIndex, toIndex);
    }

    public Pager(){

    }

    public Pager(int pageSize, int currentPage, int totalRecord, int totalPage,
            List dataList) {
        super();
        this.pageSize = pageSize;
        this.currentPage = currentPage;
        this.totalRecord = totalRecord;
        this.totalPage = totalPage;
        this.dataList = dataList;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getCurrentPage() {
        return currentPage;
    }

    public void setCurrentPage(int currentPage) {
        this.currentPage = currentPage;
    }

    public int getTotalRecord() {
        return totalRecord;
    }

    public void setTotalRecord(int totalRecord) {
        this.totalRecord = totalRecord;
    }

    public int getTotalPage() {
        return totalPage;
    }

    public void setTotalPage(int totalPage) {
        this.totalPage = totalPage;
    }

    public List getDataList() {
        return dataList;
    }

    public void setDataList(List dataList) {
        this.dataList = dataList;
    }

    @Override
    public String toString() {
        return "Pager [pageSize=" + pageSize + ", currentPage=" + currentPage + ", totalRecord=" + totalRecord
                + ", totalPage=" + totalPage + ", dataList=" + dataList + "]";
    }


}

五、DAO层编写

DAO接口:ProductDao.java

/**
 * 商品管理的Dao
 * 
 * @author Skye
 *
 */
public interface ProductDao  {

    /**
     * 根据查询条件,查询商品分页信息
     * 
     * @param searchModel
     *            封装查询条件
     * @param pageNum
     *            查询第几页数据
     * @param pageSize
     *            每页显示多少条记录
     * @return 查询结果
     */
    Pager findByPage(Product searchModel, int pageNum,
            int pageSize);


}

DAO实现类:ProductDaoImpl.java

public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {

    public Pager findByPage(Product searchModel, int pageNum, int pageSize) {

        // 声明结果集
        Pager result = null;

        // 存放查询参数
        Map paramMap = new HashMap();

        String proName = searchModel.getPname();
        Double price = searchModel.getPrice();

        StringBuilder hql = new StringBuilder("from Product where 1=1");
        StringBuilder countHql = new StringBuilder("select count(pid) from Product where 1=1 ");

        if (proName != null && !proName.equals("")) {
            hql.append(" and pname like :proName ");
            countHql.append("  and pname like :proName ");
            paramMap.put("proName", "%" + proName + "%");
        }

        if (price != null && !price.equals("")) {
            hql.append(" and price = :price ");
            countHql.append(" and price = :price ");
            paramMap.put("price", price);
        }

        // 起始索引
        int fromIndex = pageSize * (pageNum - 1);

        // 存放所有查询出的商品对象
        List productList = new ArrayList();

        Session session = null;

        // 获取被Spring托管的session
        session = this.getHibernateTemplate().getSessionFactory().getCurrentSession();

        // 获取query对象
        Query hqlQuery = session.createQuery(hql.toString());
        Query countHqlQuery = session.createQuery(countHql.toString());

        // 设置查询参数
        setQueryParams(hqlQuery, paramMap);
        setQueryParams(countHqlQuery, paramMap);

        // 从第几条记录开始查询
        hqlQuery.setFirstResult(fromIndex);

        // 一共查询多少条记录
        hqlQuery.setMaxResults(pageSize);

        // 获取查询的结果
        productList = hqlQuery.list();

        // 获取总记录数
        List countResult = countHqlQuery.list();
        int totalRecord = ((Number) countResult.get(0)).intValue();

        // 获取总页数
        int totalPage = totalRecord / pageSize;
        if (totalRecord % pageSize != 0) {
            totalPage++;
        }

        // 组装pager对象
        result = new Pager(pageSize, pageNum, totalRecord, totalPage, productList);

        return result;
    }

    /**
     * 设置查询的参数
     * 
     * @param query
     * @param paramMap
     * @return
     */
    private Query setQueryParams(Query query, Map paramMap) {
        if (paramMap != null && !paramMap.isEmpty()) {
            for (Map.Entry param : paramMap.entrySet()) {
                query.setParameter(param.getKey(), param.getValue());
            }
        }
        return query;
    }
}

六、Service层编写
接口: ProductService.java

public interface ProductService {

    /**
     * 根据查询条件,查询商品分页信息
     * 
     * @param searchModel
     *            封装查询条件
     * @param pageNum
     *            查询第几页数据
     * @param pageSize
     *            每页显示多少条记录
     * @return 查询结果
     */

    Pager findByPage(Product searchModel, int pageNum, int pageSize);

}

实现类: ProductServiceImpl.java

public class ProductServiceImpl implements ProductService {

    private ProductDao productDao;

    public void setProductDao(ProductDao productDao) {
        this.productDao = productDao;
    }

    public Pager findByPage(Product searchModel, int pageNum, int pageSize) {
        Pager result = productDao.findByPage(searchModel, pageNum, pageSize);
        return result;
    }

}

七、控制层Action编写
这个Action采用了两套不同的方法。
方式一: findAll方法是支持前端使用Form表单直接提交查询数据,然后将分页对象压入值栈(方式很多,存入Servlet 对象中也是可行的);
方式二: findAllJSON方法是支持前端AJAX提交数据然后返回JSON格式数据的。详情见注释。

public class ProductAction extends ActionSupport implements ModelDriven {

    // 模型驱动需要使用的实体类
    private Product product = new Product();

    //该例子实际没有用到自动绑定,数据从request对象中取了
    public Product getModel() {
        return product;
    }

    //返回前端的JSON字符串,需要提供get/set方法
    private String responseStr; 

    public String getResponseStr() {
        return responseStr;
    }

    public void setResponseStr(String responseStr) {
        this.responseStr = responseStr;
    }

    // Struts和Spring整合过程中按名称自动注入的业务层类
    private ProductService productService;

    public void setProductService(ProductService productService) {
        this.productService = productService;
    }

    // 方式一
    public String findAll() {
        System.out.println("控制器方法启动");
        // 使用struts2的servlet接口,接收request里的参数
        // 商品名字参数
        HttpServletRequest request = ServletActionContext.getRequest();
        String proName = request.getParameter("proName");
        // 获取价格
        Double price = null;
        String priceStr = request.getParameter("price");
        if (priceStr != null && !"".equals(priceStr.trim())) {
            price = Double.parseDouble(priceStr);
        }

        // 校验pageNum参数输入合法性
        String pageNumStr = request.getParameter("pageNum");
        System.out.println("前端给的pageNum是:"+pageNumStr);

        int pageNum = 1; // 默认显示第几页数据
        if (pageNumStr != null && !"".equals(pageNumStr.trim())) {
            pageNum = Integer.parseInt(pageNumStr);
        }

        int pageSize = 5; // 默认每页显示多少条记录
        String pageSizeStr = request.getParameter("pageSize");
        if (pageSizeStr != null && !"".equals(pageSizeStr.trim())) {
            pageSize = Integer.parseInt(pageSizeStr);
        }

        // 组装模糊查询条件
        Product searchModel = new Product();
        searchModel.setPname(proName);
        searchModel.setPrice(price);
        System.out.println("==============Product对象==============");
        System.out.println(searchModel);
        // 调用service 获取查询结果
        Pager result = productService.findByPage(searchModel, pageNum, pageSize);

        // 将pageBean存入值栈,供前端页面读取        
        ActionContext.getContext().getValueStack().push(result);
        // 将查询条件也压回值栈,在初始化函数中为其初始化
        ActionContext.getContext().getValueStack().push(searchModel);
        System.out.println("==============Pager对象==============");
        System.out.println(result);
        System.out.println("控制器方法完成");
        return "findAll";
    }

    //方式二:Ajax+JSON
    public String findAllJSON() {
        // 使用struts2的servlet接口,接收request里的参数
        // 商品名字参数
        HttpServletRequest request = ServletActionContext.getRequest();
        HttpServletResponse response = ServletActionContext.getResponse();
        String proName = request.getParameter("proName");
        // 获取价格
        Double price = null;
        String priceStr = request.getParameter("price");
        if (priceStr != null && !"".equals(priceStr.trim())) {
            price = Double.parseDouble(priceStr);
        }

        // 取得页面索引
        String pageNumStr = request.getParameter("pageNum");


        int pageNum = 1; // 默认显示第几页数据
        if (pageNumStr != null && !"".equals(pageNumStr.trim())) {
            pageNum = Integer.parseInt(pageNumStr);
        }

        int pageSize = 5; // 默认每页显示多少条记录
        String pageSizeStr = request.getParameter("pageSize");
        if (pageSizeStr != null && !"".equals(pageSizeStr.trim())) {
            pageSize = Integer.parseInt(pageSizeStr);
        }

        // 组装模糊查询条件
        Product searchModel = new Product();
        searchModel.setPname(proName);
        searchModel.setPrice(price);
        // 调用service 获取查询结果
        Pager result = productService.findByPage(searchModel, pageNum, pageSize);
        // 将查询结果封装成JSON字符串格式    
        responseStr = JSONObject.toJSONString(result);
        System.out.println(responseStr);
        // 利用response对象传回前端
        response.setHeader("Cache-Control", "no-cache");  
        response.setHeader("Pragma", "no-cache");  
        response.setDateHeader("Expires", 0);  
        response.setContentType("text/html;charset=utf-8");     
        try {
            Writer writer = response.getWriter();
            writer.write(responseStr);
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return NONE;
    }
}

八、前端的实现方式
都采用了JQuery-Pagination插件。
方式一:
注意:在Struts值栈中的对象,可以直接用属性名拿到他的值。比如说,我们之前往值栈中push了一个Pager对象,他有一个属性是dataList,那么我们就能够直接在c:forEach items=”${dataList}”;为了保留上一页的查询条件,我们需要从后台把查询条件再次传回前台,所以往值栈push了一个Product类对象,因此在使用JavaScript初始化函数时,就可以为$(“#pro_name”).val(“${pname}”); $(“#pro_price”).val(“${price}”);设定分页查询的属性值。这么做的原因是,直接使用Form的Submit提交是同步的,会直接刷新整个页面,那么之前文本框里的查询条件就会丢失。可以采用Chrome的F12开发者工具进行分析。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib uri="/struts-tags" prefix="s"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>

<html>
<head>
<style type="text/css">
.table1 {
    border: 1px solid #ddd;
    width: 900px;
}

thead {
    background-color: lightblue;
}
style>
head>


<%
    // 获取请求的上下文
    String context = request.getContextPath();
%>
<link href="${pageContext.request.contextPath}/css/pagination.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-1.11.3.js">script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery.pagination.js">script>
<script type="text/javascript">
// 点击分页按钮以后触发的动作
function handlePaginationClick(new_page_index, pagination_container) {
    $("#proForm").attr("action", "<%=context %>/product_findAll.action?pageNum=" + (new_page_index +1));
    $("#proForm").submit();
    return false;
}

//初始化函数
$(function(){
    $("#News-Pagination").pagination(${totalRecord}, {
        items_per_page:${pageSize}, // 每页显示多少条记录
        current_page:${currentPage} - 1, // 当前显示第几页数据
        num_display_entries:2, // 连续分页主体显示的条目数
        next_text:"下一页",
        prev_text:"上一页",
        num_edge_entries:2, // 连接分页主体,显示的条目数
        callback:handlePaginationClick, //执行的回调函数,也就是去获取新的分页数据
        load_first_page:false //防止页面一直刷新( 这条非常重要!)        
    }); 
    // 初始化时就获得后台发过来的前一次的查询参数
    $("#pro_name").val("${pname}");
    $("#pro_price").val("${price}");

});
script>


<body>
    <div>
        <form action="<%=context %>/product_findAll.action" id="proForm" method="post">
            商品名称 <input type="text" name="proName" id="pro_name" style="width: 120px" >   
            商品价格 <input type="text" name="price" id="pro_price" style="width: 120px" >   
        <input type="submit" value="查询">
        form>
    div>
<c:if test="${fn:length(dataList) gt 0 }">
<table border="1px" cellspacing="0px"
                style="border-collapse: collapse">
                <thead>
                    <tr height="30">
                <td align="center">商品编号td>
                <td align="center">商品名称td>
                <td align="center">商品价格td>
                    tr>
                thead>
                    <c:forEach items="${dataList}" var="p">
                        <tr>
                            <td><c:out value="${p.pid }">c:out>td>
                            <td><c:out value="${p.pname }">c:out>td>
                            <td><c:out value="${p.price }">c:out>td>
                        tr>
                    c:forEach>
            table>
            <br> 
            <div id="News-Pagination">div>
    c:if>

    <div>后台传来的当前页:${currentPage}div>
body>
html>

方式二:
这种方式采用了Ajax异步提交数据。在没有使用ajax-submit插件的情况下,不能直接用JQuery绑定表单的submit事件了,它会像方式1一样提交,那么查询数据就会丢失。采用Ajax的方式异步提交,只刷新页面的一部分元素,那么查询条件是不会丢失的。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib uri="/struts-tags" prefix="s"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>

<html>
<head>
<style type="text/css">
.table1 {
    border: 1px solid #ddd;
    width: 900px;
}

thead {
    background-color: lightblue;
}
style>
head>


<%
    // 获取请求的上下文
    String context = request.getContextPath();
%>
<link href="${pageContext.request.contextPath}/css/pagination.css"
    rel="stylesheet" type="text/css" />
<script type="text/javascript"
    src="${pageContext.request.contextPath}/js/jquery-1.11.3.js">script>
<script type="text/javascript"
    src="${pageContext.request.contextPath}/js/jquery.pagination.js">script>
<script type="text/javascript">


$(function(){
    Query();    
});


// JQuery-Pagination的初始化
function JPagination(result){
    $("#News-Pagination").pagination(result.totalRecord, {
        items_per_page:result.pageSize, // 每页显示多少条记录
        current_page:result.currentPage - 1, // 当前显示第几页数据
        num_display_entries:2, // 连续分页主体显示的条目数
        next_text:"下一页",
        prev_text:"上一页",
        num_edge_entries:2, // 连接分页主体,显示的条目数
        callback:handlePaginationClick, //执行的回调函数,也就是去获取新的分页数据
        load_first_page:false //防止页面一直刷新( 这条非常重要!)        
    });     
}





//点击分页按钮以后触发的动作
function handlePaginationClick(new_page_index, pagination_container) {
    //根据ID从表单中获取查询需要的参数
    var pname = $("#pro_name").val();  
    var price = $("#pro_price").val();
    var pageIndex = new_page_index + 1;
    //异步方式提交查询
     $.ajax({   
                type: "POST",
                //返回结果转换成json,方便用data.属性的方式取值
                dataType: "json",  
                url: "<%=context%>/product_findAllJSON.action" ,   
                data: "pageNum="+ pageIndex +"&proName=" + pname + "&price=" + price,  
                success: function(result) { 
                    //加载结果的JSON字符串
                    var totalRecord = result.totalRecord;
                    var currentPage = result.currentPage; // 获取当前第几页数据
                    var productList = result.dataList; // 学生记录信息                    
                    //生成表格内容                    
                    showProductData(productList);
                    //不需要再进行分页插件初始化
                        }  
           }); 
    return false;
}

//直接自定义一个button来发起查询
function Query(){   
    $("#btn2").click(function(){
        //根据ID从表单中获取查询需要的参数
        var pname = $("#pro_name").val();  
        var price = $("#pro_price").val();
        //异步方式提交查询
         $.ajax({   
                    type: "POST",
                    //返回结果转换成json,方便用data.属性的方式取值
                    dataType: "json",  
                    url: "<%=context%>/product_findAllJSON.action" ,   
                    data: "proName=" + pname + "&price=" + price,  
                    success: function(result) { 
                        //加载结果的JSON字符串
                        var totalRecord = result.totalRecord;
                        var currentPage = result.currentPage; // 获取当前第几页数据
                        var productList = result.dataList; // 学生记录信息
                        //生成表格内容                    
                        showProductData(productList);
                        //生成表格后对JQeury插件进行初始化
                        JPagination(result);
                            }  
               }); 

        });
}


//加载学生的数据
function showProductData(proList) {
        //每次加载新页面清空原来的内容
        $("#proDataBody").empty();
        var proDataHtml = "";
        $.each(proList, function(idx, obj) {
            proDataHtml += "";
            proDataHtml += "" + obj.pid + ""; // 获取商品编号的值
            proDataHtml += "" + obj.pname + ""; // 获取商品名称的值
            proDataHtml += "" + obj.price + ""; // 获取商品价格的值
            proDataHtml += "";
        });
        $("#proDataBody").append(proDataHtml);
    }
script>


<body>
    <div>
        <form id="proForm">
            商品名称 <input type="text" name="proName" id="pro_name" style="width: 120px">   
            商品价格 <input type="text" name="price" id="pro_price" style="width: 120px">  
        form>
    <button id="btn2">查找button>
    div>



    <div id="div1">
        <h2>商品信息h2>
    div>
    <br>
    
    <table border="1px" cellspacing="0px" style="border-collapse: collapse"
        id="ProductTable">
        <thead>
            <tr height="30">
                <th width="130">商品编号th>
                <th width="130">商品名称th>
                <th width="130">商品价格th>
            tr>
        thead>
        <tbody id="proDataBody">
        tbody>
    table>

<div id="News-Pagination">div>
body>
html>

九、效果演示
方式一:
SSH框架下的条件查询及查询结果分页_第3张图片
SSH框架下的条件查询及查询结果分页_第4张图片

方式二:
SSH框架下的条件查询及查询结果分页_第5张图片
SSH框架下的条件查询及查询结果分页_第6张图片
SSH框架下的条件查询及查询结果分页_第7张图片

十、项目github地址
https://github.com/kaka0509/SSH_Pagination

你可能感兴趣的:(J2EE)