分頁設計 與 高級查詢 的 結合設計

一:高級查詢+分頁查詢

操作步驟:

        0):把pageSize和currentPage封裝到QueryObject對象中(任何查詢對象都得接受用戶傳入的這兩個數據).

        1):在IProductDAO接口中定義高級查詢+分頁查詢方法.

        2):在ProductDAOImpl中實現該方法.

        3):測試代碼.

解決翻頁是丟失高級查詢數據的問題:

        造成的原因:翻頁時,會重新發一次請求,該請求和高級查詢表單沒有關係.

        解決方案:使用JS解決:在翻頁的時候:同時提交高級查詢表單參數 和 當前第幾頁(currentPage).

        <a href="javascript:go(${1})">首頁</a>
        <a href="javascript:go(${pageResult.prevPage})">上頁</a>
        <a href="javascript:go(${pageResult.nextPage})">下頁</a>
        <a href="javascript:go(${pageResult.totalPage})">末頁</a>

        跳轉的函數:
        <script type="text/javascript">
            //提交表單
            function go(pageNo){
                //把需要跳轉的頁碼設置到<input type="number" name="currentPage"/>上
                document.getElementById("currentPage").value = pageNo;
                //提交表單
                document.forms[0].submit();
            }
        </script>

DAO

public interface IProductdirDAO {
    List<Productdir> list();
}
public interface IProductDAO {
    PageResult query(ProductQueryObject qo);
}

Impl

public class BaseDAO {
    public PageResult query(Class<?> calssType,IQuery qo) {
        //默認把類名作爲表名
        String tableName = calssType.getSimpleName();
        Table table = calssType.getAnnotation(Table.class);
        if(table!=null){
            tableName = table.value();
        }
        //查詢結果總數
        String sqlCount = "SELECT count(*) from  " + tableName + qo.getQuery();
        Integer totalCount = JdbcTemplate.query(sqlCount, new ResultSetHandler<Long>() {
            public Long handle(ResultSet rs) throws Exception {
                if (rs.next()) {
                    return rs.getLong(1);
                }
                return 0L;
            }
        },qo.getParameters().toArray()).intValue();
        if(totalCount == 0){
            return PageResult.empty(qo.getPageSize());
        }
        System.out.println("sqlCount:"+sqlCount);
        System.out.println("參數="+qo.getParameters());
        //====================================================================================
        //查詢結果集
                String sqlList = "SELECT * from  " + tableName + qo.getQuery()+ " LIMIT ?,?";
                qo.getParameters().add((qo.getCurrentPage() -1)*qo.getPageSize());
                qo.getParameters().add(qo.getPageSize());
                List listdata = JdbcTemplate.query(sqlList, new BeanListHandler<>(calssType), qo.getParameters().toArray());
                System.out.println("sqlList:"+sqlList);
                System.out.println("參數="+qo.getParameters());
        return new PageResult(qo.getCurrentPage(),qo.getPageSize(),listdata,totalCount);
    }
}
public class ProductDAOImpl extends BaseDAO implements IProductDAO {
    public PageResult query(ProductQueryObject qo) {
        return super.query(Product.class, qo);
    }
}

query

//表示查詢對象的規範,約束查詢對象應該具有的方法
public interface IQuery {

    String getQuery();

    List<Object> getParameters();

    Integer getPageSize();

    Integer getCurrentPage();

}
//封裝了結果集的對象
@Getter
public class PageResult {
    private List listData;//結果集數據;通過SQL查詢
    private Integer totalCount;//結果總條數;通過SQL查詢

    private Integer currentPage = 1;//當前頁;用戶傳入
    private Integer pageSize = 5;//每頁條數;用戶傳入

    private Integer beginPage = 1;//首頁 
    private Integer prevPage;//上頁
    private Integer nextPage;//下頁
    private Integer totalPage;//末頁

    private List<Integer> pageItems = Arrays.asList(3,5,8,10);

    public static PageResult empty(Integer pageSize){
        return new PageResult(1,pageSize,Collections.EMPTY_LIST,0);
    }

    public PageResult( Integer currentPage, Integer pageSize,List listData, Integer totalCount) {
        this.listData = listData;
        this.totalCount = totalCount;
        this.currentPage = currentPage;
        this.pageSize = pageSize;
        //-------------------------------------
        this.totalPage = totalCount % pageSize == 0 ? totalCount / pageSize : totalCount / pageSize + 1;
        this.prevPage = currentPage - 1 >= 1 ? currentPage - 1 : 1;
        this.nextPage = currentPage + 1 <= totalPage ? currentPage + 1 : totalPage;
    }
}
//封裝了商品對象的查詢條件,並且它繼承了currentPage,pageSize的封裝
@Getter
@Setter
public class ProductQueryObject extends QueryObject{
    private String name;
    private BigDecimal minSalePrice; 
    private BigDecimal maxSalePrice;
    private Long dir_id = -1L;
    private String keyword;
    protected void customizedQuery() {
        if(StringUtils.isNotBlank(name)){
            super.addQuery("productName LIKE ?", "%"+name+"%");
        }
        //最低價格
        if(minSalePrice != null){
            super.addQuery("salePrice >= ?", minSalePrice);
        }
        //最高價格
        if(maxSalePrice != null){
            super.addQuery("salePrice <= ?", maxSalePrice);
        }
        if(dir_id != -1){
            super.addQuery("dir_id = ?", dir_id);
        }
        if(StringUtils.isNotBlank(keyword)){//因爲AND優先級大於OR,得使用()括起來
            super.addQuery(" (productName LIKE ? OR brand LIKE ? )", "%"+keyword+"%","%"+keyword+"%");
        }
    }
}
//封裝了商品對象的查詢條件
@Getter
@Setter
public class ProductdirQueryObject extends QueryObject{
    private String name;
    private Long parent_id = -1L;

    //自身的訂製查詢
    public void customizedQuery() {
        //商品分類名稱
        if (StringUtils.isNotBlank(name)) {
            super.addQuery("productdir LIKE ?", "%" + name + "%");
        }
        //父分類
        if (parent_id != -1) {
            super.addQuery(" parent_id = ?", parent_id);
        }
    }
}
public class QueryObject implements IQuery{

    @Getter@Setter
    private Integer currentPage = 1;//當前頁;用戶傳入
    @Getter@Setter
    private Integer pageSize = 5;//每頁條數;用戶傳入

    private List<String> conditions = new ArrayList<>();
    //封裝佔位符參數
    private List<Object> parameters = new ArrayList<>();

    private boolean isBuild = false;//是否已經構建SQL/封裝參數

    //初始化操作
    public void init(){
        if(!isBuild){
            isBuild = true;
            this.customizedQuery();
        }
    }

    //返回查詢條件,如:WHERE productName LIKE ? AND salePrice >= ?
    public String getQuery() {
        this.init();
        StringBuilder sql = new StringBuilder();
        if (conditions.size() == 0) {
            return "";
        }
        String queryString = StringUtils.join(conditions, " AND ");
        return sql.append(" Where ").append(queryString).toString();
    }
    //返回查詢條件中的佔位符參數值
    public List<Object> getParameters() {
        this.init();
        return parameters;
    }
    //暴露給子類,讓子類編寫自個的查詢方式
    protected void customizedQuery(){

    }
    //暴露給子類:讓子類在customizedQuery中調用,添加自己的查詢條件和參數
    protected void addQuery(String condition,Object...param){
        this.conditions.add(condition);
        this.parameters.addAll(Arrays.asList(param));
    }
}

Servlet

@WebServlet("/product")
public class ProductServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    private IProductDAO dao;
    private IProductdirDAO dao_dir;

    public void init() throws ServletException {
        dao = new ProductDAOImpl();
        dao_dir = new ProductdirDAOImpl();
    }

    //列表操作
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        //1:接受請求參數,封裝對象
        //2:調用業務方法處理請求
        ProductQueryObject qo = new ProductQueryObject();
        this.request2Object(req,qo);
        req.setAttribute("qo",qo);
        PageResult pageResult = dao.query(qo);
        req.setAttribute("pageResult", pageResult);

        req.setAttribute("dir", dao_dir.list());
        //3:控制界面跳轉
        req.getRequestDispatcher("/WEB-INF/views/product/product.jsp").forward(req, resp);
    }
    //下面這段代碼可以用commons-beanUtils 可以實現對象拷貝
    private void request2Object(HttpServletRequest req, ProductQueryObject qo) {
        String name = req.getParameter("name");
        String minSalePrice = req.getParameter("minSalePrice");
        String maxSalePrice = req.getParameter("maxSalePrice");
        String dir_id = req.getParameter("dir_id");
        String keyword = req.getParameter("keyword");
        String currentPage = req.getParameter("currentPage");
        String pageSize = req.getParameter("pageSize");
        if(StringUtils.isNotBlank(name)){
            qo.setName(name);
        }
        if(StringUtils.isNotBlank(minSalePrice)){
            qo.setMinSalePrice(new BigDecimal(minSalePrice));
        }
        if(StringUtils.isNotBlank(maxSalePrice)){
            qo.setMaxSalePrice(new BigDecimal(maxSalePrice));
        }if(StringUtils.isNotBlank(dir_id)){
            qo.setDir_id(Long.valueOf(dir_id));
        }if(StringUtils.isNotBlank(keyword)){
            qo.setKeyword(keyword);
        }if(StringUtils.isNotBlank(currentPage)){
            qo.setCurrentPage(Integer.valueOf(currentPage));
        }if(StringUtils.isNotBlank(pageSize)){
            qo.setPageSize(Integer.valueOf(pageSize));
        }
    }
}

jsp前端頁面

<form action="/product" method="post">
        商品名稱:<input type="text" name="name" value="${qo.name}"/>
        商品價格:<input type="text" name="minSalePrice" value="${qo.minSalePrice}"/><input type="text" name="maxSalePrice" value="${qo.maxSalePrice}"/>
        商品種類:<select name="dir_id">
                    <option value="-1">全部商品</option>
                <c:forEach items="${dir}" var="d">

                    <option value="${d.id}" ${d.id == qo.dir_id? "selected":""} >${d.name}</option>
                </c:forEach>
                </select>
            關鍵字查詢:<input type="text" name="keyword" placeholder="商品名稱或商品品牌" value="${qo.keyword}"/>

        <input type="submit" value=" 提交  " style="background-color: orange;"/>  

    <table border="1" width="80%" cellpadding="0" cellspacing="0">
        <tr style="background-color: orange">
            <th>id</th>
            <th>productName</th>
            <th>brand</th>
            <th>supplier</th>
            <th>salePrice</th>
            <th>costPrice</th>
            <th>cutoff</th>
            <th>dir_id</th>
        </tr>
        <c:forEach items="${pageResult.listData}" var="p" varStatus="s">
            <tr style='background-color:${s.count % 2 == 0? "gray":""}'>
                <td>${p.id}</td>
                <td>${p.productName}</td>
                <td>${p.brand}</td>
                <td>${p.supplier}</td>
                <td>${p.salePrice}</td>
                <td>${p.costPrice}</td>
                <td>${p.cutoff}</td>
                <td>
                    <c:choose>
                        <c:when test="${p.dir_id == 1}">無線手機</c:when>
                        <c:when test="${p.dir_id == 3}">遊戲手機</c:when>
                        <c:when test="${p.dir_id == 5}">有線手機</c:when>
                    </c:choose>
                </td>
            </tr>
        </c:forEach>
            <tr >
                <td colspan="8" align="center">
                    <%@include file="/WEB-INF/views/commons/commons_page.jsp" %>
                </td>
            </tr>
    </table>
</form>

這段代碼是重構出來的代碼,可以重複利用

        <script type="text/javascript">
            //提交表單
            function go(pageNo){
                //把需要跳轉的頁碼設置到<input type="number" name="currentPage"/>上
                document.getElementById("currentPage").value = pageNo;
                //提交表單
                document.forms[0].submit();
            }
        </script>

        <a href="javascript:go(${1})">首頁</a>
        <a href="javascript:go(${pageResult.prevPage})">上頁</a>
        <a href="javascript:go(${pageResult.nextPage})">下頁</a>
        <a href="javascript:go(${pageResult.totalPage})">末頁</a>
        當前第${pageResult.currentPage}/${pageResult.totalPage}頁,
        一共${pageResult.totalCount}條數據,
        跳轉到<input type="number" min="1" max="${pageResult.totalPage}" 
        id="currentPage" style="width: 50px" name="currentPage" value="${pageResult.currentPage}"/><input type="button" value="GO" onclick="go()"/>

        每頁
            <select name="pageSize" onchange="go();">
                <c:forEach items="${pageResult.pageItems}" var="item">
                    <option ${item == pageResult.pageSize? "selected":""}>${item}</option>
                </c:forEach>
            </select>
        條數據

domain

 * 商品對象 
@Data
@Table("product")
public class Product {
    private Long id;
    //@Comlumn("name2")當列名是name2的時候
    private String productName;
    private String brand;
    private String supplier;
    private BigDecimal salePrice;
    private BigDecimal costPrice;
    private Double cutoff;
    private Long dir_id;//分類編號
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table {
    String value() default "";
}
@Data
public class Productdir {
    private String name;
    private Long id;
    private Long parent_id;
}   

JDBC重構設計類

public class JdbcTemplate {

    private JdbcTemplate() {

    }

    /**
     * 
     *@param sql   DML各自的SQL,由調用者決定
     *@param params      DML操作需要的參數,由調用者決定
     *@return     受影響的行數
     */
    public static int update(String sql, Object... params) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtil.INSTANCE.getConn();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            return ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.INSTANCE.close(conn, ps, null);
        }
        return 0;
    }

    public static <T>T query(String sql, ResultSetHandler<T> rsh ,Object... params) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.INSTANCE.getConn();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            rs = ps.executeQuery();
            return rsh.handle(rs);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.INSTANCE.close(conn, ps, rs);
        }
        return null;
    }
}
//表示結果集中的一行數據,封裝成一個對象,專門針對結果集中只有一行數據的情況.
public class BeanHandler<T> implements ResultSetHandler<T>{
    private Class<T> classType;

    public BeanHandler(Class<T> classType){
        this.classType = classType;
    }

    /**
     * 規範:
     *      1:規定表中的列名必須和對象中的屬性名相同.
     *      2:規定表中列名的類型必須和java中的類型要匹配. decimal  --->BigDecimal/bigint --->Long 
     */
    public T handle(ResultSet rs) throws Exception {
        //1):創建對應的一個對象
        T obj = classType.newInstance();
        //2):取出結果集中當前光標所在行的某一列的數據.
        BeanInfo beanInfo = Introspector.getBeanInfo(classType,Object.class);   
        PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
        if(rs.next()){
            for (PropertyDescriptor pd : pds) {
                String columnName = pd.getName();//獲取對象的屬性名稱,屬性名和列名相同
                Object val = rs.getObject(columnName);
                //3):調用該對象的setter方法,把某一列的數據,設置進去.
                pd.getWriteMethod().invoke(obj, val);
            }
        }
        return obj;
    }

}
//表示把結果集中的多行數據,封裝成一個對象的集合(List<xx>),針對於結果集中有多行數據的.
public class BeanListHandler<T> implements ResultSetHandler<List<T>> {
    private Class<T> classType;

    public BeanListHandler(Class<T> classType) {
        this.classType = classType;
    }

    public List<T> handle(ResultSet rs) throws Exception {
        List<T> list = new ArrayList<>();
        while(rs.next()) {
            //每一行封裝成一個對象
            T obj = classType.newInstance();
            BeanInfo beanInfo = Introspector.getBeanInfo(classType, Object.class);
            PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor pd : pds) {
                //獲取對象的屬性名稱,屬性名和列名相同
                String columnName = pd.getName();
                Object val = rs.getObject(columnName);
                //3):調用該對象的setter方法,把某一列的數據,設置進去.
                pd.getWriteMethod().invoke(obj, val);
                //把每一行對應的對象,存儲到List集合中.
            }
            list.add(obj);
        }
        return list;
    }
}
//定義一個專門的約束處理結果集的接口:ResultSetHandler:結果集處理器
public interface ResultSetHandler<T> {

    T handle(ResultSet rs) throws Exception;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章