分步实现:
1、开发社区首页,显示前10个帖子;
2、开发分页组件,分页显示所有帖子。
一、加载社区首页/index
1、首页:最新/index?orderMode=0;最热:/index?orderMode=1,控制层获取orderMode参数的值,默认值设置为0:
2、分页机制Page page:传入Rows、Path
通过查询帖子总数设置Rows,通过offset、limit、userId、orderMode分页查询帖子列表,若userId等于0,则查询帖子的总数,否则根据userId查询相应自己的帖子。
3、本地缓存Caffeine:初始化热帖列表code=1
初始化帖子总数缓存,因为热帖不会总变,每5分钟计算一次帖子的分数,使用本地缓存存储热帖列表,避免每次都要去数据库进行查询,缓存帖子总数也是为了避免每次都要访问数据库。
用于减小对数据库的访问次数,频繁的访问数据库产生很大的开销,影响程序的执行效率!引入缓存,效率能几何倍递增。
4、数据结构:List>:将每个帖子/评论的post、userID,likeCount封装到map中,再加到list中,传给model,返回给index页面。
5、前端页面:
(1)使用thymeleaf模板引擎处理:xmlns:th:"http://www.thymeleaf.org"
生成动态的HTML,动态的引入css样式,js模板,将头部设置成模板:th:fragment="header",供其他页面复用;
(2)地址链接使用@{},从服务器端传来的参数引用使用${},显示帖子列表时,将传来的List集合进行遍历:
th:each="map:${discussPosts}"来遍历获取${map.user/headerUrl}等其他的用户信息,来显示在index页面上;
(3)分页:后端自动将page封装到mode中,前端直接获取page信息即可,利用总数,当前页,下一页,上一页等分页信息,来进行分页。使用th:fragment="pagination"标签,来让其他的页面复用。
6、热帖排行:从Redis中取出getPostScoreKey键对应的set集合,包含这段时间内变动的postID,通过取出每个postID,进而找到对应的帖子。根据是否精华、点赞数、评论数量计算帖子的权重;帖子分数上面加上一个距离一个固定日期的天数差,保证让新发的帖子尽可能地有争夺热帖的权利;
---------------------------------------------------------------------------------------------------------------------------------
配置数据库和Mybatis:
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
#数据库
spring:
datasource:
username: root
password: gouhanchi521
url: jdbc:mysql://localhost:3306/community?allowPublicKeyRetrieval=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
hikari:
minimum-idle: 5
maximum-pool-size: 15
idle-timeout: 30000
#mybatis
mybatis:
type-aliases-package: com.nowcoder.community.entity
mapper-locations: classpath:mapper/*.xml
configuration:
use-generated-keys: true
map-underscore-to-camel-case: true
1.编写Dao层:
package com.nowcoder.community.mapper;
import com.nowcoder.community.entity.DiscussPost;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface DiscussPostMapper {
List<DiscussPost> selectDiscussPosts(int userId,int offset,int limit);
//@Param注解用于给参数取别名
//如果只有一个参数,并且在<if>里使用,则必须加别名
int selectDiscussPostRows(@Param("userId") int userId);
}
2.编写Mapper.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nowcoder.community.mapper.DiscussPostMapper">
<select id="selectDiscussPosts" resultType="DiscussPost">
select * from discuss_post
where status!=2
<if test="userId!=0">
and user_id=#{userId}
</if>
order by type desc,create_time desc
limit #{offset},#{limit};
</select>
<select id="selectDiscussPostRows" resultType="int">
select count(id) from discuss_post
where status!=2
<if test="userId!=0">
and user_id=#{userId}
</if>
</select>
</mapper>
3.编写业务层:
package com.nowcoder.community.service;
import com.nowcoder.community.entity.DiscussPost;
import com.nowcoder.community.mapper.DiscussPostMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class DiscussPostServiceImpl implements DiscussPostService{
@Autowired
private DiscussPostMapper discussPostMapper;
@Override
public List<DiscussPost> selectDiscussPosts(int userId, int offset, int limit) {
return discussPostMapper.selectDiscussPosts(userId, offset, limit);
}
@Override
public int selectDiscussPostRows(int userId) {
return discussPostMapper.selectDiscussPostRows(userId);
}
}
4.编写Controller层:
package com.nowcoder.community.controller;
import com.nowcoder.community.entity.DiscussPost;
import com.nowcoder.community.entity.Page;
import com.nowcoder.community.entity.User;
import com.nowcoder.community.service.DiscussPostServiceImpl;
import com.nowcoder.community.service.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
public class HomeController {
@Autowired
private DiscussPostServiceImpl discussPostService;
@Autowired
private UserServiceImpl userService;
@GetMapping("/index")
public String getIndexPage(Model model, Page page)
{
page.setRows(discussPostService.selectDiscussPostRows(0));
page.setPath("/index");
List<DiscussPost> list = discussPostService.selectDiscussPosts(0, page.getOffset(), page.getLimit());
List<Map<String,Object>> discussPosts=new ArrayList<>();
if (list!=null){
for (DiscussPost post : list) {
Map<String,Object> map=new HashMap<>();
map.put("post",post);
User user = userService.selectById(post.getUserId());
map.put("user",user);
discussPosts.add(map);
}
}
model.addAttribute("discussPosts",discussPosts);
return "index";
}
}
5.分页工具类:参数(当前页码、每页的行数、数据总量、查询路径)
package com.nowcoder.community.entity;
public class Page {
//当前页码
private int currentPage=1;
//每页的数量
private int limit=10;
//数据总量
private int rows;
//查询路径
private String path;
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
if (currentPage>=1){
this.currentPage = currentPage;
}
}
public int getLimit() {
return limit;
}
public void setLimit(int limit) {
if (limit>=1&&limit<=100){
this.limit = limit;
}
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
if(rows>=0) {
this.rows = rows;
}
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
//获取当前页的起始行
public int getOffset(){
return (currentPage-1)*limit;
}
//获取总的页数
public int getTotalPage(){
if (rows%limit==0) {
return rows / limit;
}else {
return rows/limit+1;
}
}
//获取起始页
public int getFromPage(){
int from=currentPage-2;
return from<1 ? 1:from;
}
//获取结束页
public int getToPage(){
int to=currentPage+2;
int total=getTotalPage();
return to>total ? total:to;
}
}
<!-- 分页 -->
<nav class="mt-5" th:if="${page.rows>0}" th:fragment="pagination">
<ul class="pagination justify-content-center">
<li class="page-item">
<a class="page-link" th:href="@{${page.path} (currentPage=1)}">首页</a>
</li>
<li th:class="|page-item ${page.currentPage==1?'disabled':''}|">
<a class="page-link" th:href="@{${page.path} (currentPage=${page.currentPage-1})}">上一页</a>
</li>
<li th:class="|page-item ${i==page.currentPage?'active':''}|" th:each="i:${#numbers.sequence(page.fromPage,page.toPage)}">
<a class="page-link" th:href="@{${page.path} (currentPage=${i})}" th:text="${i}">1</a>
</li>
<li th:class="|page-item ${page.currentPage==page.totalPage?'disabled':''}|">
<a class="page-link" th:href="@{${page.path} (currentPage=${page.currentPage+1})}">下一页</a>
</li>
<li class="page-item">
<a class="page-link" th:href="@{${page.path} (currentPage=${page.totalPage})}">末页</a>
</li>
</ul>
</nav>