多条件分页查询
首先给BookService.java的paging方法添加两个参数
package com.ql.reader.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ql.reader.entity.Book;
/**
* 图书服务
*/
public interface BookService {
/**
* 分页查询图书
* @param categoryId 分类编号
* @param order 排序方式
* @param page 页号
* @param rows 每页记录数
* @return 分页对象
*/
public IPage<Book> paging(Long categoryId, String order, Integer page, Integer rows);
}
然后修改它实现类BookServiceImpl
package com.ql.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.ql.reader.entity.Book;
import com.ql.reader.mapper.BookMapper;
import com.ql.reader.service.BookService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@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) {
Page<Book> p = new Page<Book>(page, rows);
QueryWrapper<Book> queryWrapper = new QueryWrapper<Book>();
if(categoryId!=null && categoryId!=-1){
queryWrapper.eq("category_id", categoryId);
}
if(order!=null){
if(order.equals("quantity")){
queryWrapper.orderByDesc("evaluation_quantity");
}else if(order.equals("score")){
queryWrapper.orderByDesc("evaluation_score");
}
}
IPage<Book> pageObject = bookMapper.selectPage(p, queryWrapper);
return pageObject;
}
}
然后修改测试用例进行测试
package com.ql.reader.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ql.reader.entity.Book;
import com.ql.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;
import static org.junit.Assert.*;
@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);
List<Book> records = pageObject.getRecords();
for (Book book: records){
System.out.println(book.getBookId()+":"+book.getBookName());
}
System.out.println("总页数:"+pageObject.getPages());
System.out.println("总记录数"+pageObject.getTotal());
}
}
然后修改BookController控制器的代码
/**
* 分页查询图书列表
* @param categoryId 分类编号
* @param order 排序方式
* @param p 页号
* @return 分页对象
*/
@GetMapping("/books")
@ResponseBody
public IPage<Book> selectBook(Long categoryId, String order, Integer p){
if(p==null){
p=1;
}
IPage<Book> pageObject = bookService.paging(categoryId, order, p, 10);
return pageObject;
}
前端调用代码为
<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(){
//选择分类事件
$(".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>
运行项目测试
实现图书详情页
实现图书详情页图书内容
首先在BookService接口中添加根据图书编号查询图书对象的方法
/**
* 根据图书编号查询图书对象
* @param bookId 图书编号
* @return 图书对象
*/
public Book selectById(Long bookId);
然后在它的实现类BookServiceImpl中添加方法实现
/**
* 根据图书编号查询图书对象
* @param bookId 图书编号
* @return 图书对象
*/
public Book selectById(Long bookId) {
Book book = bookMapper.selectById(bookId);
return book;
}
然后在BookController中添加跳转图书详细页的方法
@GetMapping("/book/{id}")
public ModelAndView showDetail(@PathVariable("id") Long id){
Book book = bookService.selectById(id);
ModelAndView mav = new ModelAndView("/detail");
mav.addObject("book", book);
return mav;
}
在src/main/webapp/WEB-INF/ftl目录中新增detail.ftl文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>${book.bookName}</title>
<meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0,user-scalable=no">
<meta name="referrer" content="no-referrer">
<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>
.container {
padding: 0px;
margin: 0px;
}
.row {
padding: 0px;
margin: 0px;
}
.alert {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.col- * {
padding: 0px;
}
.description p {
text-indent: 2em;
}
.description img {
width: 100%;
}
.highlight {
background-color: lightskyblue !important;
}
</style>
<script>
$.fn.raty.defaults.path = '/resources/raty/lib/images';
$(function () {
$(".stars").raty({readOnly: true});
})
$(function () {
<#if memberReadState ??>
// 重选阅读状态
$("*[data-read-state='${memberReadState.readState}']").addClass("highlight");
</#if>
<#if !loginMember ??>
$("*[data-read-state],#btnEvaluation,*[data-evaluation-id]").click(function(){
// 未登录情况下提示"需要登录"
$("#exampleModalCenter").modal("show");
})
</#if>
<#if loginMember ??>
/**
* 更新会员阅读状态
*/
$("*[data-read-state]").click(function () {
// 会员阅读状态
var readState = $(this).data("read-state");
// 发送请求
$.post("/update_read_state", {
memberId:${loginMember.memberId},
bookId:${book.bookId},
readState: readState
}, function (json) {
if (json.code == "0") {
$("*[data-read-state]").removeClass("highlight");
$("*[data-read-state='" + readState + "']").addClass("highlight");
}
}, "json")
});
$("#btnEvaluation").click(function(){
// 转换为星型组件
$("#score").raty({});
// 显示短评对话框
$("#dlgEvaluation").modal("show");
})
// 评论对话框提交数据
$("#btnSubmit").click(function(){
// 获取评分
var score = $("#score").raty("score");
var content = $("#content").val();
if(score == 0 || $.trim(content) == ""){
return;
}
$.post("/evaluate" , {
score : score,
bookId : ${book.bookId},
memberId : ${loginMember.memberId},
content : content
},function(json){
if(json.code = "0"){
// 刷新当前页面
window.location.reload();
}
},"json")
})
// 评论点赞
$("*[data-evaluation-id]").click(function(){
var evaluationId = $(this).data("evaluation-id");
$.post("/enjoy",{evaluationId:evaluationId},function(json){
if(json.code == "0"){
$("*[data-evaluation-id='" + evaluationId + "'] span").text(json.evaluation.enjoy);
}
},"json")
})
</#if>
})
</script>
</head>
<body>
<!--<div style="width: 375px;margin-left: auto;margin-right: auto;">-->
<div class="container ">
<nav class="navbar navbar-light bg-white shadow mr-auto">
<ul class="nav">
<li class="nav-item">
<a href="/" style="color: #aaa;font-weight: bold">
书评网
</a>
</li>
</ul>
</nav>
<div class="container mt-2 p-2 m-0" style="background-color:rgb(127, 125, 121)">
<div class="row">
<div class="col-4 mb-2 pl-0 pr-0">
<img style="width: 110px;height: 160px"
src="${book.cover}">
</div>
<div class="col-8 pt-2 mb-2 pl-0">
<h6 class="text-white">${book.bookName}</h6>
<div class="p-1 alert alert-warning small" role="alert">
${book.subTitle}
</div>
<p class="mb-1">
<span class="text-white-50 small">${book.author}</span>
</p>
<div class="row pl-1 pr-2">
<div class="col-6 p-1">
<button type="button" data-read-state="1" class="btn btn-light btn-sm w-100">
<img style="width: 1rem;" class="mr-1"
src="https://img3.doubanio.com/f/talion/cf2ab22e9cbc28a2c43de53e39fce7fbc93131d1/pics/card/ic_mark_todo_s.png"/>想看
</button>
</div>
<div class="col-6 p-1">
<button type="button" data-read-state="2" class="btn btn-light btn-sm w-100">
<img style="width: 1rem;" class="mr-1"
src="https://img3.doubanio.com/f/talion/78fc5f5f93ba22451fd7ab36836006cb9cc476ea/pics/card/ic_mark_done_s.png"/>看过
</button>
</div>
</div>
</div>
</div>
<div class="row" style="background-color: rgba(0,0,0,0.1);">
<div class="col-2"><h2 class="text-white">${book.evaluationScore}</h2></div>
<div class="col-5 pt-2">
<span class="stars" data-score="${book.evaluationScore}"></span>
</div>
<div class="col-5 pt-2"><h5 class="text-white">${book.evaluationQuantity}人已评</h5></div>
</div>
</div>
<div class="row p-2 description">
${book.description}
</div>
<div class="alert alert-primary w-100 mt-2" role="alert">短评
<button type="button" id="btnEvaluation" class="btn btn-success btn-sm text-white float-right"
style="margin-top: -3px;">
写短评
</button>
</div>
<div class="reply pl-2 pr-2">
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-body">
您需要登录才可以操作哦~
</div>
<div class="modal-footer">
<a href="/login.html" type="button" class="btn btn-primary">去登录</a>
</div>
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="dlgEvaluation" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-body">
<h6>为"从 0 开始学爬虫"写短评</h6>
<form id="frmEvaluation">
<div class="input-group mt-2 ">
<span id="score"></span>
</div>
<div class="input-group mt-2 ">
<input type="text" id="content" name="content" class="form-control p-4" placeholder="这里输入短评">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" id="btnSubmit" class="btn btn-primary">提交</button>
</div>
</div>
</div>
</div>
</body>
</html>
运行项目并测试
显式评论列表
首先在com.ql.reader.entity包下创建Member会员实体类
package com.ql.reader.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.Date;
@TableName("member")
public class Member {
@TableId(type = IdType.AUTO)
private Long memberId;
private String username;
private String password;
private Integer salt;
private String nickname;
private Date createTime;
public Long getMemberId() {
return memberId;
}
public void setMemberId(Long memberId) {
this.memberId = memberId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getSalt() {
return salt;
}
public void setSalt(Integer salt) {
this.salt = salt;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
然后在com.ql.reader.mapper下创建MemberMapper.java接口
package com.ql.reader.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ql.reader.entity.Book;
import com.ql.reader.entity.Member;
public interface MemberMapper extends BaseMapper<Member> {
}
然后在src/main/resources/mappers目录下创建member.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.ql.reader.mapper.MemberMapper">
</mapper>
在com.ql.reader.entity包下创建Evaluation评论实体类
package com.ql.reader.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.Date;
@TableName("evaluation")
public class Evaluation {
@TableId(type = IdType.AUTO)
private Long evaluationId;
private Long bookId;
private String content;
private Integer score;
private Long memberId;
private Date createTime;
private Integer enjoy;
private String state;
private String disableReason;
private Date disableTime;
@TableField(exist = false)//说明book属性没有对应字段,不会参与到sql自动生成
private Book book;
@TableField(exist = false)
private Member member;
public Long getEvaluationId() {
return evaluationId;
}
public void setEvaluationId(Long evaluationId) {
this.evaluationId = evaluationId;
}
public Long getBookId() {
return bookId;
}
public void setBookId(Long bookId) {
this.bookId = bookId;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Integer getScore() {
return score;
}
public void setScore(Integer score) {
this.score = score;
}
public Long getMemberId() {
return memberId;
}
public void setMemberId(Long memberId) {
this.memberId = memberId;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Integer getEnjoy() {
return enjoy;
}
public void setEnjoy(Integer enjoy) {
this.enjoy = enjoy;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getDisableReason() {
return disableReason;
}
public void setDisableReason(String disableReason) {
this.disableReason = disableReason;
}
public Date getDisableTime() {
return disableTime;
}
public void setDisableTime(Date disableTime) {
this.disableTime = disableTime;
}
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
public Member getMember() {
return member;
}
public void setMember(Member member) {
this.member = member;
}
}
然后在com.ql.reader.mapper下创建EvaluationMapper接口
package com.ql.reader.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ql.reader.entity.Evaluation;
public interface EvaluationMapper extends BaseMapper<Evaluation> {
}
然后在src/main/resources/mappers目录下创建evaluation.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.ql.reader.mapper.EvaluationMapper">
</mapper>
在com.ql.reader.service包下创建接口EvaluationService,且在com.ql.reader.service.impl创建它的实现类
package com.ql.reader.service;
import com.ql.reader.entity.Evaluation;
import java.util.List;
public interface EvaluationService {
/**
* 按图书编号查询有效短评
* @param bookId 图书编号
* @return 评论列表
*/
public List<Evaluation> selectByBookId(Long bookId);
}
package com.ql.reader.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ql.reader.entity.Evaluation;
import com.ql.reader.mapper.EvaluationMapper;
import com.ql.reader.service.EvaluationService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
@Service("evaluationService")
@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
public class EvaluationServiceImpl implements EvaluationService {
@Resource
private EvaluationMapper evaluationMapper;
/**
* 按图书编号查询有效短评
* @param bookId 图书编号
* @return 评论列表
*/
@Override
public List<Evaluation> selectByBookId(Long bookId) {
QueryWrapper<Evaluation> queryWrapper = new QueryWrapper<Evaluation>();
queryWrapper.eq("book_id", bookId);
queryWrapper.eq("state", "enable");
queryWrapper.orderByDesc("create_time");
List<Evaluation> evaluationList = evaluationMapper.selectList(queryWrapper);
return evaluationList;
}
}
然后修改BookController中的获取图书详细页方法
@GetMapping("/book/{id}")
public ModelAndView showDetail(@PathVariable("id") Long id){
Book book = bookService.selectById(id);
List<Evaluation> evaluationList = evaluationService.selectByBookId(id);
ModelAndView mav = new ModelAndView("/detail");
mav.addObject("book", book);
mav.addObject("evaluationList", evaluationList);
return mav;
}
前端部分为,图书详情页detail.ftl文件的短评部分
<div class="alert alert-primary w-100 mt-2" role="alert">短评
<button type="button" id="btnEvaluation" class="btn btn-success btn-sm text-white float-right"
style="margin-top: -3px;">
写短评
</button>
</div>
<div class="reply pl-2 pr-2">
<#-- 利用freemake的list标签,进行遍历 -->
<#list evaluationList as evaluation>
<div>
<div>
<span class="pt-1 small text-black-50 mr-2">${evaluation.createTime?string('MM-dd')}</span>
<span class="mr-2 small pt-1">${evaluation.member.nickname}</span>
<span class="stars mr-2" data-score="${evaluation.score}"></span>
<button type="button" data-evaluation-id="${evaluation.evaluationId}"
class="btn btn-success btn-sm text-white float-right" style="margin-top: -3px;">
<img style="width: 24px;margin-top: -5px;" class="mr-1"
src="https://img3.doubanio.com/f/talion/7a0756b3b6e67b59ea88653bc0cfa14f61ff219d/pics/card/ic_like_gray.svg"/>
<span>${evaluation.enjoy}</span>
</button>
</div>
<div class="row mt-2 small mb-3">
${evaluation.content}
</div>
<hr/>
</div>
</#list>
</div>
运行项目测试