准备工作
使用idea创建springboot项目
导入相关坐标依赖
<!--web相关依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--thymeleaf模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--thymeleaf中支持java8的时间类型(LocalDate和LocalDateTime)-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
<!--mybatisplus依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!--mybatisplus代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.2</version>
</dependency>
<!--MySQL数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
<scope>runtime</scope>
</dependency>
<!--Druid数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<!--lombok插件依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
使用mybatisplus代码生成器快速生成代码
页面部分效果
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/43049a6f61bf3ac351b273578f370cac.png#pic_center)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/5b7253a110dad18c663efe447442a49c.png#pic_center)
代码实现
编写分页插件
@Configuration
public class MybatisPlusPageConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); //添加分页插件
return interceptor;
}
}
编写thymeleaf分页模板
<!DOCTYPE html>
<!--
th:fragment="page(pageInfo, prevText, nextText, prefix1,prefix2,prefix3)
page:片段名称(类似方法名)
pageInfo:分页数据的对象名称
prefix1:父映射名称
prefix2:子映射名称
prefix3:评论对应文章的id(动态获取)
-->
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" th:fragment="page(pageInfo,prefix1,prefix2,prefix3)">
<head>
<meta charset="UTF-8">
<title>分页模板</title>
<link th:href="@{/bootstrap/css/bootstrap.min.css}" rel="stylesheet">
</head>
<style>
.pagination {
display: inline-block;
padding-left: 0;
margin: 20px 0;
border-radius: 5px;
}
.pagination a {
display: inline;
border-radius: 3px;
}
</style>
<body>
<nav>
<ul class="pagination pagination-lg">
<!--首页-->
<th:block th:if="${pageInfo.pages>0}">
<li>
<a th:if="${pageInfo.current>1 && #strings.isEmpty(prefix3)}"
th:href="@{'/'+${prefix1}+'/'+${prefix2}+'/'+1}" th:text="首页"></a>
<a th:if="${pageInfo.current>1 && not #strings.isEmpty(prefix3)}"
th:href="@{'/'+${prefix1}+'/'+${prefix2}+'/'+${prefix3}+'/'+1}" th:text="首页"></a>
</li>
</th:block>
<!--上一页-->
<th:block th:if="${pageInfo.hasPrevious()}">
<li>
<a th:if="${pageInfo.current>1 && #strings.isEmpty(prefix3)}"
th:href="@{'/'+${prefix1}+'/'+${prefix2}+'/'+${(pageInfo.current)-1}}" th:text="上一页"
aria-label="Previous">
</a>
<a th:if="${pageInfo.current>1 && not #strings.isEmpty(prefix3)}"
th:href="@{'/'+${prefix1}+'/'+${prefix2}+'/'+${prefix3}+'/'+${(pageInfo.current)-1}}"
th:text="上一页"
aria-label="Previous">
</a>
</li>
</th:block>
<!--页码数动态显示-->
<th:block th:each="pageNum :${#numbers.sequence(1, pageInfo.pages)}">
<th:block th:if="${pageInfo.pages>0 && pageInfo.pages<=5}">
<li th:class="${pageInfo.current ==pageNum ? 'active':''}">
<a th:if="${#strings.isEmpty(prefix3)}" th:href="@{'/'+${prefix1}+'/'+${prefix2}+'/'+${pageNum}}"
th:text="${pageNum}">
</a>
<a th:if="${not #strings.isEmpty(prefix3)}"
th:href="@{'/'+${prefix1}+'/'+${prefix2}+'/'+${prefix3}+'/'+${pageNum}}"
th:text="${pageNum}">
</a>
</li>
</th:block>
<th:block th:if="${pageInfo.pages>5}">
<li th:if="${pageInfo.current<=3 && pageNum<=5}"
th:class="${pageInfo.current ==pageNum ? 'active':''}">
<a th:if="${#strings.isEmpty(prefix3)}" th:href="@{'/'+${prefix1}+'/'+${prefix2}+'/'+${pageNum}}"
th:text="${pageNum}">
</a>
<a th:if="${not #strings.isEmpty(prefix3)}"
th:href="@{'/'+${prefix1}+'/'+${prefix2}+'/'+${prefix3}+'/'+${pageNum}}"
th:text="${pageNum}">
</a>
</li>
<li th:if="${pageInfo.current>=(pageInfo.pages)-2 && pageNum>(pageInfo.pages)-5}"
th:class="${pageInfo.current ==pageNum ? 'active':''}">
<a th:if="${#strings.isEmpty(prefix3)}" th:href="@{'/'+${prefix1}+'/'+${prefix2}+'/'+${pageNum}}"
th:text="${pageNum}">
</a>
<a th:if="${not #strings.isEmpty(prefix3)}"
th:href="@{'/'+${prefix1}+'/'+${prefix2}+'/'+${prefix3}+'/'+${pageNum}}"
th:text="${pageNum}">
</a>
</li>
<li th:if="${pageInfo.current>=4 && pageInfo.current<=(pageInfo.pages)-3 && pageNum>=(pageInfo.current)-2 && pageNum<=(pageInfo.current)+2}"
th:class="${pageInfo.current ==pageNum ? 'active':''}">
<a th:if="${#strings.isEmpty(prefix3)}" th:href="@{'/'+${prefix1}+'/'+${prefix2}+'/'+${pageNum}}"
th:text="${pageNum}">
</a>
<a th:if="${not #strings.isEmpty(prefix3)}"
th:href="@{'/'+${prefix1}+'/'+${prefix2}+'/'+${prefix3}+'/'+${pageNum}}"
th:text="${pageNum}">
</a>
</li>
</th:block>
</th:block>
<!--下一页-->
<th:block th:if="${pageInfo.hasNext()}">
<li>
<a th:if="${pageInfo.current<pageInfo.pages && #strings.isEmpty(prefix3)}"
th:href="@{'/'+${prefix1}+'/'+${prefix2}+'/'+${(pageInfo.current)+1}}"
th:text="下一页" aria-label="Next">
</a>
<a th:if="${pageInfo.current<pageInfo.pages && not #strings.isEmpty(prefix3)}"
th:href="@{'/'+${prefix1}+'/'+${prefix2}+'/'+${prefix3}+'/'+${(pageInfo.current)+1}}"
th:text="下一页" aria-label="Next">
</a>
</li>
</th:block>
<!--尾页-->
<th:block th:if="${pageInfo.pages>0}">
<li>
<a th:if="${pageInfo.current < pageInfo.pages && #strings.isEmpty(prefix3)}"
th:href="@{'/'+${prefix1}+'/'+${prefix2}+'/'+${pageInfo.pages}}"
th:text="尾页">
</a>
<a th:if="${pageInfo.current < pageInfo.pages && not #strings.isEmpty(prefix3)}"
th:href="@{'/'+${prefix1}+'/'+${prefix2}+'/'+${prefix3}+'/'+${pageInfo.pages}}"
th:text="尾页">
</a>
</li>
</th:block>
</ul>
</nav>
</body>
</html>
文章分页
1. controller层代码
@Controller
@RequestMapping("/article")
public class ArticleController {
@Autowired
private ArticleService articleService;
@RequestMapping("/page/{pageNum}")
public String page(@PathVariable("pageNum") Integer pageNum, @RequestParam(defaultValue = "5") Integer pageSize, Model model) {
Page<Article> articles = articleService.findPage(pageNum, pageSize);
model.addAttribute("articles", articles);
return "client/index";
}
}
2. service接口层代码
public interface ArticleService extends IService<Article> {
Page<Article> findPage(Integer pageNum, Integer pageSize);
}
3. service实现类层代码
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {
@Autowired
private ArticleMapper articleMapper;
QueryWrapper<Article> wrapper = new QueryWrapper<>();
@Override
public Page<Article> findPage(Integer pageNum, Integer pageSize) {
Page<Article> page = new Page<>(pageNum, pageSize);
//按照文章发布时间降序
wrapper.orderByDesc("created");
return articleMapper.selectPage(page, wrapper);
}
}
4. mapper层代码
@Mapper
public interface ArticleMapper extends BaseMapper<Article> {
}
5. 文章部分页面
<div class="am-u-md-8 am-u-sm-12">
<!-- 文章信息展示 -->
<div th:each="article: ${articles.records}">
<article class="am-g blog-entry-article">
<div class="am-u-lg-6 am-u-md-12 am-u-sm-12 blog-entry-img">
<!--文章图片-->
<!--
${#numbers.formatInteger(final Number target, final Integer minIntegerDigits)
第一个参数:目标数值(结合随机数:[0,1)),四舍五入
第二个参数:最小的整数位数(不足会自动补0)
-->
<!--让每篇文章的图片可以在25张图片中随机显示-->
<img width="100%" class="am-u-sm-12"
th:src="@{'/client/img/rand/'+${#numbers.formatInteger(T(java.lang.Math).floor(T(java.lang.Math).random()*count)+1,1)}+'.png'}"/>
</div>
<div class="am-u-lg-6 am-u-md-12 am-u-sm-12 blog-entry-text">
<!-- 文章分类 -->
<span class="blog-color" style="font-size: 15px;" th:text="'所属分类 '+${article.categories}"/>
<span> </span>
<!-- 发布时间 -->
<span style="font-size: 15px;" th:text="'发布于 '+${#temporals.format(article.created,'yyyy-MM-dd')}"/>
<h2>
<div>
<!--文章标题(可以点击查询文章详情)-->
<a style="color: #0f9ae0;font-size: 20px;" th:text="${article.title}"
th:href="@{'/article/articleDetail/'+${article.id}}"/>
</div>
</h2>
<!-- 文章摘要(只显示100个字符)-->
<div style="font-size: 16px;" th:utext="${#strings.abbreviate(article.content,100)}"/>
</div>
</article>
</div>
<!--显示分页信息-->
<div th:replace="commons/pagination::page(${articles},'article','page','')"></div>
</div>
评论分页
1. controller层代码
@RequestMapping("/articleDetail/{articleId}/{pageNum}")
public String detail(@PathVariable("articleId") Integer articleId, @PathVariable("pageNum") Integer pageNum, @RequestParam(defaultValue = "3") Integer pageSize, Model model) {
//根据文章id查询对应的评论
Page<Comment> comments = commentService.findPageById(articleId, pageNum, pageSize);
model.addAttribute("comments", comments);
return "/client/articleDetail";
}
2. service接口层代码
public interface CommentService extends IService<Comment> {
Page<Comment> findPageById(Integer articleId, Integer pageNum, Integer pageSize);
}
3. service实现类层代码
@Service
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements CommentService {
@Autowired
private CommentMapper commentMapper;
@Override
public Page<Comment> findPageById(Integer articleId, Integer pageNum, Integer pageSize) {
Page<Comment> page = new Page<>(pageNum, pageSize);
QueryWrapper<Comment> wrapper = new QueryWrapper<>();
//根据文章id(article_id)查询并且以created降序
wrapper.eq("article_id", articleId);
wrapper.orderByDesc("created");
return commentMapper.selectPage(page, wrapper);
}
}
4. mapper层代码
@Mapper
public interface CommentMapper extends BaseMapper<Comment> {
}
5. 评论部分页面
<!--文章存在-->
<div th:if="${article!=null}">
<div class="comment-container">
<div id="comments" class="clearfix">
<!--文章允许评论-->
<div th:if="${article.allowComment}">
<div class="response">
<th:block>
请先<a th:href="@{/user/toLogin}" data-no-instant=""> 登录 </a>后 ,发表评论或查看评论!
</th:block>
</div>
<div>
<form id="comment-form" class="comment-form" onsubmit="return publishComment()">
<input type="hidden" name="articleId" id="articleId" th:value="${article.id}">
<textarea name="content" id="textarea" class="form-control"
placeholder="评论不能为空哦!"
required="required" minlength="5" maxlength="2000"></textarea>
<button type="submit" class="submit" id="submit">评论</button>
</form>
</div>
</div>
<!-- 显示评论内容 -->
<div th:if="${comments!=null}">
<ol class="comment-list">
<th:block th:each="comment: ${comments.records}">
<li class="comment-body comment-parent comment-odd">
<div class="comment-view">
<div class="comment-header">
<!--人物头像和名称-->
<img class="avatar" th:src="@{/assets/img/avatars.jpg}" height="50"/>
<span class="comment-author" rel="external nofollow"
th:text="${comment.author}"></span>
<!-- 评论日期 -->
<span class="comment-time"
th:text="${#temporals.format(comment.created,'yyyy-MM-dd')}"></span>
</div>
<!-- 评论内容 -->
<div class="comment-content">
<span class="comment-author-at"
th:utext="${#strings.abbreviate(comment.comment,50)}"></span>
</div>
</div>
</li>
</th:block>
</ol>
<!-- 评论分页显示 -->
<div th:replace="commons/pagination::page(${comments},'article','articleDetail',${article.id})"></div>
</div>
</div>
</div>
</div>
总结
thymeleaf模板是本次的重点核心
- 使用的mybatisplus框架+bootstrap样式
- 自定义片段函数(pageInfo实际上就是mybatisplus中的Page对象)
- 在每个block代码块中有两个a标签(文章分页和文章对应的评论分页)
- 感谢你的阅读,希望对你有所帮助
- 后面会不断的发表心的文章
- 有什么好的建议可以在评论区一起讨论