ajax简单实现分页及多条件查询功能

简单实现一个单独的分页和多条件查询,主要是运用在ssm框架(基础的三层架构)的分页和多条件查询,使用的ajax异步请求。由于有些代码是我已经封装好了的,我会尽量给全代码,如果有看不懂的或者不理解可以评论。

数据库准备

首先准备数据库,一张表内含多个字段多条数据

搭建一个Java项目

项目结构如下:

创建bean包,实体类

public class Book {
    private Integer id;
    private String name;
    private String author;
    private Double price;
    private Integer sales;
    private Integer stock;
    private String imgPath = "static/img/default.jpg";


    public Book() {
    }

    public Book(Integer id, String name, String author, Double price, Integer sales, Integer stock, String imgPath) {
        this.id = id;
        this.name = name;
        this.author = author;
        this.price = price;
        this.sales = sales;
        this.stock = stock;
        this.imgPath = imgPath;
    }

    /**
     * 获取
     * @return id
     */
    public Integer getId() {
        return id;
    }

    /**
     * 设置
     * @param id
     */
    public void setId(Integer id) {
        this.id = id;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return author
     */
    public String getAuthor() {
        return author;
    }

    /**
     * 设置
     * @param author
     */
    public void setAuthor(String author) {
        this.author = author;
    }

    /**
     * 获取
     * @return price
     */
    public Double getPrice() {
        return price;
    }

    /**
     * 设置
     * @param price
     */
    public void setPrice(Double price) {
        this.price = price;
    }

    /**
     * 获取
     * @return sales
     */
    public Integer getSales() {
        return sales;
    }

    /**
     * 设置
     * @param sales
     */
    public void setSales(Integer sales) {
        this.sales = sales;
    }

    /**
     * 获取
     * @return stock
     */
    public Integer getStock() {
        return stock;
    }

    /**
     * 设置
     * @param stock
     */
    public void setStock(Integer stock) {
        this.stock = stock;
    }

    /**
     * 获取
     * @return imgPath
     */
    public String getImgPath() {
        return imgPath;
    }

    /**
     * 设置
     * @param imgPath
     */
    public void setImgPath(String imgPath) {
        this.imgPath = imgPath;
    }

    public String toString() {
        return "Book{id = " + id + ", name = " + name + ", author = " + author + ", price = " + price + ", sales = " + sales + ", stock = " + stock + ", imgPath = " + imgPath + "}";
    }
}
前端页面

.book_cond标签所在盒子是一个表单提交框,要将查询条件传回后端,只不过此次使用的是ajax技术,所以会将数据获取到script再由ajax异步发起请求

.b_list标签所在盒子是一个模板数据,之后会对他进行克隆,展示从数据库查询到的数据。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <base th:href="@{/}">
    <style>
        img{
            width: 150px;
        }
        .b_list{
            margin: 45px;
            display: inline-block;
        }
    </style>
</head>
<body>
<div id="book">
    <div class="book_cond" style="margin-left: 0px; margin-top: 20px;">
        <form action="" method="get">
            书名:<input type="text" name="name">
            作者:<input type="text" name="author">
            价格:<input id="min" type="text" name="min" value=""> 元 -
            <input id="max" type="text" name="max" value=""> 元
            <input type="button" value="查询" id="searchId"/>
        </form>
    </div>

  <!--这里将其隐藏是因为这条数据是不展示的,只是起一个模板作用,方便jquery克隆  -->
    <div class="b_list" style="display: none">
        <div class="img_div">
            <img class="book_img" alt="" src="img/default.jpg" />
        </div>
        <div class="book_info" style="width: 150px">
            <div class="book_name">
                <span class="sp1">书名:</span>
                <span class="sp2">时间简史</span>
            </div>
            <div class="book_author">
                <span class="sp1">作者:</span>
                <span class="sp2">霍金</span>
            </div>
            <div class="book_price">
                <span class="sp1">价格:</span>
                <span class="sp2">¥30.00</span>
            </div>
            <div class="book_sales">
                <span class="sp1">销量:</span>
                <span class="sp2">230</span>
            </div>
            <div class="book_amount">
                <span class="sp1">库存:</span>
                <span class="sp2">1000</span>
            </div>
            <div class="book_add">
                <button>加入购物车</button>
            </div>
        </div>
    </div>


    <div id="page_nav" style="margin-left: 200px">
        <a href="javascript:void(0)">首页</a>
        <a href="javascript:void(0)">上一页</a>
        <a href="javascript:void(0)">下一页</a>
        <a href="javascript:void(0)">末页</a>
        共<span id="totalPageId">10</span>页,<span id="pageTotalCountId">30</span>条记录 到第<input value="4" name="pn" id="pn_input" style="width: 30px;"/>页
        <input type="button" value="确定" id="jumpId">
    </div>

</div>

</body>
</html>

我们是先将数据查询出来,ajax展示在页面上

javascript代码如下,现在还没有编写上下页功能,稍后等功能接近完整再来完善这一段代码

我们使用的是.post()异步请求BookServlet类,在这个servlet类中会接受form表单提交的参数,然后将查询出来的数据以json形式写到前端,也就是function (data){,data就是后端发送的数据。那么得到这些数据,就可以开始clone网页中的模板盒子,一个for循环,将所有Book对象数据写到div。有些参数看不懂没关系,因为后端还没讲解,后端会有一个Page工具类。

<script src="script/jquery-1.7.2.js"></script>
<script>
    var myPageNo;  //全局变量 P当前页
    var myPageTotal; // 全局变量 最大页
    $(function (){
        mypage(1)
    })

    function mypage(pageNo){
        //在查询框获取到的数据,将其表单序列化serialize()
        // serialize()可以把表单中所有表单项的内容都获取到,并以name=value&name=value 的形式进行拼接。
        var formParam = $("#book").find("form").serialize();
        //将所有内容通过ajax传给BookServlet
        $.post("BookServlet?method=searchPage",formParam+"&pageNo="+pageNo,function (data){
            $("#book").find(".b_list:gt(0)").remove();
            myPageNo = data.pageNo;
            myPageTotal = data.pageTotal;
            $("#totalPageId").html(myPageTotal)
            $("#pageTotalCountId").html(data.pageTotalCount)
            $("#pn_input").val(myPageNo)

            for (let i = 0; i < data.items.length; i++) {
                var newdiv = $("#book").find(".b_list").first().clone(); //克隆那个隐藏的
                newdiv.css("display", "");
                newdiv.find(".book_name").find(".sp2").html(data.items[i].name)
                newdiv.find(".book_author").find(".sp2").html(data.items[i].author)
                newdiv.find(".book_price").find(".sp2").html(data.items[i].price)
                newdiv.find(".book_sales").find(".sp2").html(data.items[i].sales)
                newdiv.find(".book_amount").find(".sp2").html(data.items[i].stock)
                newdiv.find(".book_img").attr("src", "/bookimg/"+data.items[i].imgPath)

                $("#book").append(newdiv)
            }
        })
    }
</script>
编写servlet类

上面有写到ajax请求的是BookServlet类,所以现在开始编写。其作用是接收客户端发送过来的请求,并响应数据给客户端。这个servlet类,大家可以根据自己想法写,我这边有封装工具类,方便做项目,但是不适合没有项目经验的人观看,所以这边用最基础的service方法。

@WebServlet("/BookServletP")
public class BookServletP extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");
        String author = req.getParameter("author");
        String min = req.getParameter("min");
        String max = req.getParameter("max");

        // pageNo当前页码
        String pageNo = req.getParameter("pageNo");
        // 当前页显示数量
        String pageSize = req.getParameter("pageSize");

        // 类型转换
        int myPageNo = 1;
        if (pageNo!="" && pageNo!=null){
            myPageNo = Integer.parseInt(pageNo);
        }

        int myPageSize = 4;
        if (pageSize!="" && pageSize!=null){
            myPageSize = Integer.parseInt(pageSize);
        }

        double mymin = -1;
        if (min!="" && min!=null){
            mymin = Double.parseDouble(min);
        }

        double mymax = -1;
        if (max!="" && max!=null){
            mymax = Double.parseDouble(max);
        }

        BookDao bookDao = new BookDaoImpl();
        Page<Book> bookPage = bookDao.searchPage(name, author, mymin, mymax, myPageNo, myPageSize);
        
        resp.setContentType("application/json;charset=utf-8");
        PrintWriter out = resp.getWriter();
        Gson gson = new Gson();
        String s = gson.toJson(bookPage);
        out.write(s);
    }
}

因为getParameter()方法得到的都是字符串类型的,所以要进行类型转换,判断是不是空,空的话赋一个默认值就可以了。然后实例化一个BookDaoImpl对象,调用方法查询出符合多个条件的数据。再将数据通过gson传给前端。

创建dao层

我们这是将分页+多条件查询的功能单拎出来,所以dao层service层我都在一起写了,也避免层次结构太复杂,反而绕晕大家。

dao层首先是一个接口类BookDao,里面定义了一个方法searchPage()

public interface BookDao {
    public Page<Book> searchPage(String name, String author, Double min, Double max, int myPageNo, int myPageSize);
}

然后BookDaoImpl类实现接口,方法就是将多条件查询的数据查询出来并返回Page实例,因为前端需要页码和总记录数。

public class BookDaoImpl extends BaseDao<Book> implements BookDao{
    @Override
    public Page<Book> searchPage(String name, String author, Double min, Double max, int myPageNo, int myPageSize) {
        //1、首先求出多条件查询的数据总量
        //2.定义一个list集合,用于存储sql查询的值,因为参数是不确定的,在前端有的值可能是空的,需要判断
        List params = new ArrayList();
        //3.定义一个字符串,存放查询数据记录数的sql语句 由于字符串会不停修改所以用StringBuilder类更好
        // where 1 = 1 这么写是因为,条件可能会动态添加,保证sql语句不出错。
        StringBuilder builder1 = new StringBuilder("select count(*) from t_book where 1 = 1");
        //4.定于builder2,存放查询所有数据的sql语句
        StringBuilder builder2 = new StringBuilder("select * from t_book where 1 = 1");

        //5.判断各个参数的值是否有效
        if (name != null && !name.equals("")){
            //模糊查询,使用concat连接字符串... '%','?','%'
            builder1.append(" and name like concat('%',?,'%')"); // 使用?占位符
            builder2.append(" and name like concat('%',?,'%')");
            params.add(name);
        }
        if (author != null && !author.equals("")){
            builder1.append(" and author = ?");
            builder2.append(" and author = ?");
            params.add(author);
        }
        if ((min !=null && min != -1) && (max != null && max != -1)){
            if (min > max){
                //如果最小金额大于最大金额 那么进行两值交换
                min = min+max;
                max = min - max;
                min = min - max;
            }
            params.add(min);
            params.add(max);
            builder1.append(" and price between ? and ?");
            builder2.append(" and price between ? and ?");


        }else if (min !=null && min != -1){
            builder1.append(" and price > ?");
            builder2.append(" and price > ?");
            params.add(min);
        }else if (max != null && max != -1){
            builder1.append(" and price < ?");
            builder2.append(" and price < ?");
            params.add(max);
        }

        //总记录数
        int totalCount = getValue(builder1.toString(), params.toArray());

        Page<Book> page = Page.getPage(myPageNo, myPageSize, totalCount);
        int begin = (page.getPageNo() - 1) * page.getPageSize();
        builder2.append(" limit ? , ?");
        params.add(begin);
        params.add(page.getPageSize());
        List<Book> bookList = getBeanList(builder2.toString(), params.toArray());
        page.setItems(bookList);
        return page;
    }
}

这里一个重要的工具类就是Page。看懂就可以,其中一个重要的方法,getPage是一些逻辑计算总页码。在此方法是将page实例赋值然后再返回。

//分页工具类
public class Page<T> {

    public static final Integer PAGE_SIZE = 4;
    // 当前页码
    private Integer pageNo;
    // 总页码
    private Integer pageTotal;
    // 当前页显示数量
    private Integer pageSize = PAGE_SIZE;
    // 总记录数
    private Integer pageTotalCount;
    // 当前页数据
    private List<T> items;

    private String url;


    public Page() {
    }

    public Page(Integer pageNo, Integer pageTotal, Integer pageSize, Integer pageTotalCount, List<T> items, String url) {
        this.pageNo = pageNo;
        this.pageTotal = pageTotal;
        this.pageSize = pageSize;
        this.pageTotalCount = pageTotalCount;
        this.items = items;
        this.url = url;
    }

//    public static <T> Page<T> getp(int i)

    /**
     * 将当前页码,当前页显示数量和显示总记录数赋值进Page对象并求出总页码,再将该对象返回
     * @param pageNo 当前页码
     * @param pageSize 当前页显示数量
     * @param totalCount 总记录数
     * @param <T> 方法泛型
     * @return
     */
    public static <T> Page<T> getPage(int pageNo, int pageSize, int totalCount){
        Page<T> page = new Page<>();
        //当前页显示数量
        page.setPageSize(pageSize);
        //当前页码
        page.setPageNo(pageNo);
        //总记录数
        page.setPageTotalCount(totalCount);
        //求出总页码
        page.setPageTotal((totalCount+pageSize-1) / pageSize);
        /*插入一段逻辑判断的代码,
        我们判断一下当前页是否小于1,如果有小于1那么就设置成第一行
        再次判断一下当前页是否大于最大页,如果大于最大页,就设置成最大页
        */
        if (page.getPageNo()<1){
            page.setPageNo(1);
        }
        if (page.getPageNo()>page.getPageTotal()){
            page.setPageNo(page.getPageTotal());//设置最大页为当前页
        }
        return page;
    }

    /**
     * 获取
     * @return pageNo
     */
    public Integer getPageNo() {
        return pageNo;
    }

    /**
     * 设置
     * @param pageNo
     */
    public void setPageNo(Integer pageNo) {
        this.pageNo = pageNo;
    }

    /**
     * 获取
     * @return pageTotal
     */
    public Integer getPageTotal() {
        return pageTotal;
    }

    /**
     * 设置
     * @param pageTotal
     */
    public void setPageTotal(Integer pageTotal) {
        this.pageTotal = pageTotal;
    }

    /**
     * 获取
     * @return pageSize
     */
    public Integer getPageSize() {
        return pageSize;
    }

    /**
     * 设置
     * @param pageSize
     */
    public void setPageSize(Integer pageSize) {
        this.pageSize = pageSize;
    }

    /**
     * 获取
     * @return pageTotalCount
     */
    public Integer getPageTotalCount() {
        return pageTotalCount;
    }

    /**
     * 设置
     * @param pageTotalCount
     */
    public void setPageTotalCount(Integer pageTotalCount) {
        this.pageTotalCount = pageTotalCount;
    }

    /**
     * 获取
     * @return items
     */
    public List<T> getItems() {
        return items;
    }

    /**
     * 设置
     * @param items
     */
    public void setItems(List<T> items) {
        this.items = items;
    }

    /**
     * 获取
     * @return url
     */
    public String getUrl() {
        return url;
    }

    /**
     * 设置
     * @param url
     */
    public void setUrl(String url) {
        this.url = url;
    }

    public String toString() {
        return "Page{PAGE_SIZE = " + PAGE_SIZE + ", pageNo = " + pageNo + ", pageTotal = " + pageTotal + ", pageSize = " + pageSize + ", pageTotalCount = " + pageTotalCount + ", items = " + items + ", url = " + url + "}";
    }

    public static void generatePageNumbers(Page page, HttpServletRequest req){
        //  分页模块中,页码展示+++++++++++++++++++++ 开始
        //情况1:如果总页码小于等于5的情况,页码的范围是:1-总页码
        if (page.getPageTotal() <= 5){
            req.setAttribute("begin",1);
            req.setAttribute("end",page.getPageTotal());
        }else{
            //情况2:总页码>5
            if (page.getPageNo()<=3){
                req.setAttribute("begin",1);
                req.setAttribute("end",5);
            }else if (page.getPageNo()>page.getPageTotal()-3){
                req.setAttribute("begin",page.getPageTotal()-4);
                req.setAttribute("end",page.getPageTotal());
            }else{
                req.setAttribute("begin",page.getPageNo()-2);
                req.setAttribute("end",page.getPageNo()+2);
            }
        }
        //  分页模块中,页码展示+++++++++++++++++++++ 结束
    }
}

那么到这里,这个功能的后端已经完成了。

现在可以继续前端的分页功能。

前端分页

在javascript代码里面添加一些逻辑,比如,定位当前页,我们要实现点击上一页下一页或首页展示不同数据,所以得给html中对应的a标签绑定点击事件。

完整javascript代码如下:

定义了一个新方法doPageEvents()作用对点击上一页下一页首页末页进行反应

在前端页面还有一个输入框,用于输入数字展示该页码对应的页面。只用绑定点击事件调用mypage()方法然后获取该框内的值就可以了。

到此,整个功能就已经完成

这是首页面,没有进行查询,展示的所有数据。

<script src="script/jquery-1.7.2.js"></script>
<script>
    var myPageNo;  //全局变量 P当前页
    var myPageTotal; // 全局变量 最大页
    $(function (){
        mypage(1)

        $('#searchId').click(function (){
            //点击查询按钮时,调用分页方法
            mypage(1);
        })

        $("#jumpId").click(function (){
            mypage($("#pn_input").val())
        })
    })

    function mypage(pageNo){
        //在查询框获取到的数据,将其表单序列化serialize()
        // serialize()可以把表单中所有表单项的内容都获取到,并以name=value&name=value 的形式进行拼接。
        var formParam = $("#book").find("form").serialize();
        //将所有内容通过ajax传给BookServlet
        $.post("BookServlet?method=searchPage",formParam+"&pageNo="+pageNo,function (data){
            $("#book").find(".b_list:gt(0)").remove();
            myPageNo = data.pageNo;
            myPageTotal = data.pageTotal;
            $("#totalPageId").html(myPageTotal)
            $("#pageTotalCountId").html(data.pageTotalCount)
            $("#pn_input").val(myPageNo)

            for (let i = 0; i < data.items.length; i++) {
                var newdiv = $("#book").find(".b_list").first().clone(); //克隆那个隐藏的
                newdiv.css("display", "");
                newdiv.find(".book_name").find(".sp2").html(data.items[i].name)
                newdiv.find(".book_author").find(".sp2").html(data.items[i].author)
                newdiv.find(".book_price").find(".sp2").html(data.items[i].price)
                newdiv.find(".book_sales").find(".sp2").html(data.items[i].sales)
                newdiv.find(".book_amount").find(".sp2").html(data.items[i].stock)
                newdiv.find(".book_img").attr("src", "/bookimg/"+data.items[i].imgPath)

                $("#book").append(newdiv)
            }
            doPageEvents();
        })
    }

    //为当前网页中的‘首页’‘上一页’‘末页’ 添加事件
    function doPageEvents(){
        var first = $("#page_nav a:contains('首页')");
        var previous = $("#page_nav a:contains('上一页')");
        var end = $("#page_nav a:contains('末页')");
        var next = $("#page_nav a:contains('下一页')");
        if(myPageNo <= 1){
            first.unbind();
            previous.unbind()
            first.css({"color":"silver", "cursor":"default"});
            previous.css({"color":"silver", "cursor":"default"})
        }else{
            first.css({"color":"", "cursor":"pointer"});
            previous.css({"color":"", "cursor":"pointer"})
            if (!(first.data('events') && first.data('events')['click'])){
                first.click(function (){
                    mypage(1)
                })
            }
            if (!(previous.data('events') && previous.data('events')['click'])){
                previous.click(function (){
                    mypage(myPageNo-1 <=0 ? 1 : myPageNo-1)
                })
            }

        }

        //判断当前是否大于最大页
        if (myPageNo >= myPageTotal){
            //将'下一页'与'首页'失去点击事件
            next.unbind()
            end.unbind()
            next.css("color", "silver");
            end.css("color", "silver")
        }else{
            next.css("color", "");
            end.css("color", "")
            if (!(next.data('events') && next.data('events')['click'])){
                next.click(function (){
                    mypage(myPageNo+1 >= myPageTotal ? myPageTotal : myPageNo + 1)
                })
            }
            if (!(end.data('events') && end.data('events')['click'])){
                end.click(function (){
                    mypage(myPageTotal)
                })
            }
        }
    }
</script>

查询后的如下:展示该条件下的所有数据。

需要源码的可以评论留言,无偿赠送和指导。因为本项目需要一些jar包,可后台私信。

  • 22
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值