基于Spring+SpringMVC+MyBatis开发书评网图书分页查询模块采用Ajax动态加载图书信息和实现图书多条件动态查询

Posted 被雨遗忘的夏天

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于Spring+SpringMVC+MyBatis开发书评网图书分页查询模块采用Ajax动态加载图书信息和实现图书多条件动态查询相关的知识,希望对你有一定的参考价值。

一、 图书分页查询

概述:

1、 采用Ajax动态加载图书信息

2、 实现图书多条件动态查询

 

作用:

不用一次加载全部图书信息,防止更新的时候数据丢失,相对于采用Freemaker加载,后者复杂些,需要加入javascript 来控制请求的提交和返回数据的处理

 

二、 开发流程

1、 创建图书实体类

package com.imooc.reader.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

/**
 * @ClassName Book
 * @Description 图书实体类
 * @date 2021/5/9 19:18
 * @Param
 * @return
 */
// 映射相应的数据库表
@TableName("book")
public class Book {
    // 图书编号
    // 映射相应主键,并设置为自增
    @TableId(type = IdType.AUTO)
    private Long bookId;
    // 书名
    private String bookName;
    // 子标题
    private String subTitle;
    // 作者
    private String author;
    // 封面图片URL
    private String cover;
    // 图书详情
    private String description;
    // 分类编号
    private Long categoryId;
    // 图书评分
    private float evaluationScore;
    // 评价数量

    private Integer evaluationQuantity;

    public Long getBookId() {
        return bookId;
    }

    public void setBookId(Long bookId) {
        this.bookId = bookId;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public String getSubTitle() {
        return subTitle;
    }

    public void setSubTitle(String subTitle) {
        this.subTitle = subTitle;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getCover() {
        return cover;
    }

    public void setCover(String cover) {
        this.cover = cover;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Long getCategoryId() {
        return categoryId;
    }

    public void setCategoryId(Long categoryId) {
        this.categoryId = categoryId;
    }

    public float getEvaluationScore() {
        return evaluationScore;
    }

    public void setEvaluationScore(float evaluationScore) {
        this.evaluationScore = evaluationScore;
    }

    public Integer getEvaluationQuantity() {
        return evaluationQuantity;
    }

    public void setEvaluationQuantity(Integer evaluationQuantity) {
        this.evaluationQuantity = evaluationQuantity;
    }
}

 

2、 创建Mapper接口类

package com.imooc.reader.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.imooc.reader.entity.Book;

/**
 * @ClassName BookMapper
 * @Description 图书接口类
 * @date 2021/5/9 19:25
 * @Param
 * @return
 */
public interface BookMapper extends BaseMapper<Book> {
    /**
     * 更新图书评分/评价数量
     */
    public void updateEvaluation();
}

 

3、 创建mapper.xml

<?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.imooc.reader.mapper.BookMapper">
    <update id="updateEvaluation">
        update book b SET evaluation_score = (
            select ifnull(avg(score),0) from evaluation where book_id = b.book_id and state='enable'
            ),evaluation_quantity = (
            select ifnull(count(*),0) from evaluation where book_id = b.book_id and state='enable'
            )
    </update>
</mapper>

 

4、 创建Service层的服务接口BookService

package com.imooc.reader.service;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.imooc.reader.entity.Book;

/**
 * @ClassName BookService
 * @Description TODO
 * @date 2021/5/9 19:28
 * @Param
 * @return
 */
public interface BookService {
    /**
     * 分页查询图书
     * @param categoryId 分类编号
     * @param order 排序方式
     * @param page 页号
     * @param rows 每页记录数
     * @return 分页对象
     */
    public IPage<Book> paging(Long categoryId , String order , Integer page,Integer rows);
}

 

5、 实现服务接口BookService

package com.imooc.reader.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.imooc.reader.entity.Book;
import com.imooc.reader.mapper.BookMapper;
import com.imooc.reader.service.BookService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * @ClassName BookServiceImp
 * @Description TODO
 * @date 2021/5/9 19:32
 * @Param
 * @return
 */
@Service("bookService")
// 声明式事务注解
@Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly = true)
public class BookServiceImpl implements BookService {
    @Resource
    private BookMapper bookMapper;

    /**
     * 分页查询图书
     * @param categoryId 分类编号
     * @param order 排序方式
     * @param page 页号
     * @param rows 每页记录数
     * @return 分页对象
     */
    public IPage<Book> paging(Long categoryId , String order ,Integer page, Integer rows) {
        // p: 查询的页数
        Page<Book> p = new Page<Book>(page, rows);
        // queryWrapper: 条件构造器
        QueryWrapper<Book> queryWrapper = new QueryWrapper<Book>();

        // 表示从前台传入了有效的编号
        if (categoryId != null && categoryId != -1) {
            queryWrapper.eq("category_id", categoryId);
        }
        // 排序方式
        if (order != null) {
            // quantity: 按评价人数进行排序
            if (order.equals("quantity")) {
                queryWrapper.orderByDesc("evaluation_quantity");
            } else if (order.equals("score")) {
                // score: 按评分进行排序
                queryWrapper.orderByDesc("evaluation_score");
            }
        }
        // 核心方法 selectPage:实现分页查询
        // 两个参数:p   queryWrapper
        IPage<Book> pageObject = bookMapper.selectPage(p, queryWrapper);
        return pageObject;
    }
}

 

6、 创建测试用例

package com.imooc.reader.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.imooc.reader.entity.Book;
import com.imooc.reader.service.BookService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;
import java.util.List;

// IOC容器初始化
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class BookServiceImplTest {

    @Resource
    private BookService bookService;

    @Test
    public void paging() {
        IPage<Book> pageObject = bookService.paging(2l,"quantity" ,2, 10);
        // getRecords: 分页对象记录列表
        List<Book> records = pageObject.getRecords();
        // 增强for循环
        for(Book b:records){
            System.out.println(b.getBookId() + ":" + b.getBookName());
        }
        System.out.println("总页数:" + pageObject.getPages());
        System.out.println("总记录数:" + pageObject.getTotal());
    }
}

 

7、 controller 层配置

package com.imooc.reader.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.imooc.reader.entity.Book;
import com.imooc.reader.entity.Category;
import com.imooc.reader.service.BookService;
import com.imooc.reader.service.CategoryService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import java.util.List;

/**
 * @ClassName BookController
 * @Description 图书控制类,接收前端请求
 * @date 2021/5/8 21:54
 * @Param
 * @return
 */
@Controller
public class BookController {
    @Resource
    private CategoryService categoryService;

    @Resource
    private BookService bookService;

    /**
     * 显示首页
     * @return
     */
    @GetMapping("/")

    public ModelAndView showIndex() {
        ModelAndView mav = new ModelAndView("/index");
        List<Category> categoryList = categoryService.selectAll();
        mav.addObject("categoryList", categoryList);
        return mav;
    }

    /**
     * 分页查询图书列表
     * @param categoryId 分类编号
     * @param order 排序方式
     * @param p 页号
     * @return 分页对象
     */
    // 绑定URL
    @GetMapping("/books")
    // 使用springMVC对这个IPage对象进行JSON序列化输出
    @ResponseBody
    public IPage<Book> selectBook(Long categoryId , String order ,Integer p) {
        // 容错处理: 若前台没有传入页号,默认第一页
        if (p == null) {
            p = 1;
        }
        // p: 前台传过来的页号
        IPage<Book> pageObject = bookService.paging(categoryId , order , p, 10);
        return pageObject;
    }
}

 

8、 JavaScript + Ajax 结合开发前端

<!DOCTYPE html>
<html lang="en"><head>
    <meta charset="UTF-8">
    <title>书评网</title>
    <meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0,user-scalable=no">
    <link rel="stylesheet" href="./resources/bootstrap/bootstrap.css">
    <link rel="stylesheet" href="./resources/raty/lib/jquery.raty.css">
    <script src="./resources/jquery.3.3.1.min.js"></script>
    <script src="./resources/bootstrap/bootstrap.min.js"></script>
    <script src="./resources/art-template.js"></script>
    <script src="./resources/raty/lib/jquery.raty.js"></script>
    <style>
        .highlight {
            color: red !important;
        }
        a:active{
            text-decoration: none!important;
        }
    </style>


    <style>
        .container {
            padding: 0px;
            margin: 0px;
        }

        .row {
            padding: 0px;
            margin: 0px;
        }

        .col- * {
            padding: 0px;
        }
    </style>
    <#--定义模板-->
    <script type="text/html" id="tpl">
        <a href="/book/{{bookId}}" style="color: inherit">
            <div class="row mt-2 book">
                <div class="col-4 mb-2 pr-2">
                    <img class="img-fluid" src="{{cover}}">
                </div>
                <div class="col-8  mb-2 pl-0">
                    <h5 class="text-truncate">{{bookName}}</h5>

                    <div class="mb-2 bg-light small  p-2 w-100 text-truncate">{{author}}</div>


                    <div class="mb-2 w-100">{{subTitle}}</div>

                    <p>
                        <span class="stars" data-score="{{evaluationScore}}" title="gorgeous"></span>
                        <span class="mt-2 ml-2">{{evaluationScore}}</span>
                        <span class="mt-2 ml-2">{{evaluationQuantity}}人已评</span>
                    </p>
                </div>
            </div>
        </a>

        <hr>
    </script>

    <script>
        $.fn.raty.defaults.path ="./resources/raty/lib/images";
        // loadMore()加载更多数据
        // isReset参数设置为true,代表从第一页开始查询,否则按nextPage查询后续页
        function loadMore(isReset){
            if(isReset == true){
                $("#bookList").html("");
                $("#nextPage").val(1);
            }
            var nextPage = $("#nextPage").val();
            var categoryId= $("#categoryId").val();
            var order = $("#order").val();

            $.ajax({
                url : "/books" ,
                data : {p:nextPage,"categoryId":categoryId , "order":order},
                type : "get" ,
                dataType : "json" ,
                success : function(json){
                    console.info(json);
                    var list = json.records;
                    for(var i = 0 ; i < list.length ; i++){
                        var book = json.records[i];
                        // var html = "<li>" + book.bookName + "</li>";
                        // 将数据结合tpl模板,生成html
                        var html = template("tpl" , book);
                        console.info(html);
                        $("#bookList").append(html);
                    }
                    // 显示星型评价组件
                    $(".stars").raty({readOnly:true});

                    // 判断是否到最后一页
                    if(json.current < json.pages){
                        $("#nextPage").val(parseInt(json.current) + 1);
                        $("#btnMore").show();
                        $("#divNoMore").hide();
                    }else{
                        $("#btnMore").hide();
                        $("#divNoMore").show();
                    }
                }
            })
        }
        $(function(){
            /*$.ajax({
                url : "/books" ,
                data : {p:1},
                type : "get" ,
                dataType : "json" ,
                success : function(json){
                    console.info(json);
                    var list = json.records;
                    for(var i = 0 ; i < list.length ; i++){
                        var book = json.records[i];
                        // var html = "<li>" + book.bookName + "</li>";
                        // 将数据结合tpl模板,生成html
                        var html = template("tpl" , book);
                        console.info(html);
                        $("#bookList").append(html);
                    }
                    // 显示星型评价组件
                    $(".stars").raty({readOnly:true});
                }
            })*/
            loadMore(true);
        })

        // 绑定加载更多按钮单击事件
        $(function(){
            $("#btnMore").click(function(){
                loadMore();
            })

            $(".category").click(function () {
                $(".category").removeClass("highlight");
                $(".category").addClass("text-black-50");
                $(this).addClass("highlight");
                var categoryId = $(this).data("category");
                $("#categoryId").val(categoryId);
                loadMore(true);
            })

            $(".order").click(function(){
                $(".order").removeClass("highlight");
                $(".order").addClass("text-black-50");
                $(this).addClass("highlight");
                var order = $(this).data("order");
                $("#order").val(order);
                loadMore(true);
            })
        })
    </script>
</head>
<body>
<div class="container">
    <nav class="navbar navbar-light bg-white shadow mr-auto">
        <ul class="nav">
            <li class="nav-item">
                <a href="/">
                    <img src="https://m.imooc.com/static/wap/static/common/img/logo2.png" class="mt-1" style="width: 100px">
                </a>
            </li>

        </ul>
        <#if loginMember??>
            <h6 class="mt-1">
                <img style="width: 2rem;margin-top: -5px" class="mr-1" src="./images/user_icon.png">${loginMember.nickname}
            </h6>
        <#else>
            <a href="/login.html" class="btn btn-light btn-sm">
                <img style="width: 2rem;margin-top: -5px" class="mr-1" src="./images/user_icon.png">登录
            </a>
        </#if>

    </nav>
    <div class="row mt-2">


        <div class="col-8 mt-2">
            <h4>热评好书推荐</h4>
        </div>

        <div class="col-8 mt-2">
            <span data-category="-1" style="cursor: pointer" class="highlight  font-weight-bold category">全部</span>
            |
            <#list categoryList as category>
                <a style="cursor: pointer" data-category="${category.categoryId}" class="text-black-50 font-weight-bold category">${category.categoryName}</a>
                <#if category_has_next>|</#if>
            </#list>


        </div>

        <div class="col-8 mt-2">
            <span data-order="quantity" style="cursor: pointer" class="order highlight  font-weight-bold mr-3">按热度</span>

            <span data-order="score" style="cursor: pointer" class="order text-black-50 mr-3 font-weight-bold">按评分</span>
        </div>
    </div>
    <div class="d-none">
        <input type="hidden" id="nextPage" value="2">
        <input type="hidden" id="categoryId" value="-1">
        <input type="hidden" id="order" value="quantity">
    </div>

    <div id="bookList">



    </div>
    <button type="button" id="btnMore" data-next-page="1" class="mb-5 btn btn-outline-primary btn-lg btn-block">
        点击加载更多...
    </button>
    <div id="divNoMore" class="text-center text-black-50 mb-5" style="display: none;">没有其他数据了</div>
</div>

</body></html>

 

三、 效果图

以上是关于基于Spring+SpringMVC+MyBatis开发书评网图书分页查询模块采用Ajax动态加载图书信息和实现图书多条件动态查询的主要内容,如果未能解决你的问题,请参考以下文章

spring与mybati整合方法

自己开发的音乐视频网站

Spring Boot整合Mybati之逆向工程

抽奖活动啦!5本SpringMVC+MyBatis相关3本Android Studio相关6本Kafka相关

spring+mybati java config配置引起的bean相互引用日志报警告问题

Spring基于注解及SpringMVC