开发流程
(1)1次请求的执行过程
分步实现
(1)开发社区首页,显示前10个帖子
(2)开发分页组件,分页显示所有的帖子
新建DiscussPost类,
private int id;
private int userId;
private String title;
private String content;
private int type;
private int status;
private Date createTime;
private int commentCount;
private double score;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public int getCommentCount() {
return commentCount;
}
public void setCommentCount(int commentCount) {
this.commentCount = commentCount;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String toString() {
return "DiscussPost{" +
"id=" + id +
", userId=" + userId +
", title='" + title + '\'' +
", content='" + content + '\'' +
", type=" + type +
", status=" + status +
", createTime=" + createTime +
", commentCount=" + commentCount +
", score=" + score +
'}';
}
新建DiscussPostMapper接口类,
List<DiscussPost> selectDiscussPosts(int userId,int offset,int limit, int orderMode); //offset是起始行行号,limit是每页显示多少条数据
//@Param用于给参数取别名
// 如果这个方法只有一个参数,并且在<if>里使用(动态sql),则必须加别名
int selectDiscussPostRows(@Param("userId")int userId);
新建discusspost-mapper.xml,
<sql id="selectFields">
id,user_id,title,content,type,status,create_time,comment_count,score
</sql>
<select id="selectDiscussPosts" resultType="DiscussPost">
select <include refid="selectFields"></include>
from discuss_post
where status != 2
<if test="userId!=0">
and user_id = #{userId}
</if>
<if test="orderMode==0">
order by type desc, create_time desc
</if>
<if test="orderMode==1">
order by type desc, score desc, create_time desc
</if>
limit #{offset},#{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>
在MapperTests类里,添加
@Test
public void testSelectPosts() {
List<DiscussPost> list = discussPostMapper.selectDiscussPosts(149,0,10,0);
for(DiscussPost post : list) {
System.out.println(post);
}
int rows = discussPostMapper.selectDiscussPostRows(149); //查行数
System.out.println(rows);
}
接着开发业务层,在service包下新建DiscussPostService类(注意,为了安全性和可扩展性,不要让controller直接去调mapper)。
public List<DiscussPost> findDiscussPosts(int userId,int offset,int limit,int orderMode) {
return discussPostMapper.selectDiscussPosts(userId,offset,limit,orderMode);
}
public int findDiscussPostRows(int userId) {
return discussPostMapper.selectDiscussPostRows(userId);
}
新建UserService类,
public User findUserById(int id) {
// return userMapper.selectById(id);
// User user = getCache(id); //先尝试从缓存读数据
// if(user==null){
// user = initCache(id);
// }
return user;
}
把静态资源和网页拷贝到resources包下的static和template下。新建HomeController类,
@RequestMapping(path = "/index",method = RequestMethod.GET)
public String getIndexPage(Model model, Page page,
@RequestParam(name="orderMode",defaultValue="0") int orderMode) {
//方法调用前,SpringMVC会自动实例化Model和Page,并将Page注入Model,
//所以,在thymeleaf中可以直接访问Page对象中的数据
page.setRows(discussPostService.findDiscussPostRows(0));
page.setPath("/index?orderMode="+orderMode);
List<DiscussPost> list = discussPostService
.findDiscussPosts(0,page.getOffset(),page.getLimit(),orderMode);
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 user = userService.findUserById(post.getUserId());
map.put("user",user);
long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST,post.getId());
map.put("likeCount",likeCount);
discussPosts.add(map);
}
}
model.addAttribute("discussPosts",discussPosts);
model.addAttribute("orderMode", orderMode); //装上送给模板以判断点亮(active)“最新”还是“最热”
return "/index";
}
修改index.html相应地方
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<link rel="stylesheet" th:href="@{/css/global.css}"/>
<ul class="list-unstyled">
<li class="media pb-3 pt-3 mb-3 border-bottom" th:each="map:${discussPosts}"> <!--HomeController里提供的discussPosts-->
<a th:href="@{|/user/profile/${map.user.id}|}">
<img th:src="${map.user.headerUrl}" class="mr-4 rounded-circle" alt="用户头像" style="width:50px;height:50px;">
</a>
<div class="media-body">
<h6 class="mt-0 mb-3">
<a th:href="@{|/discuss/detail/${map.post.id}|}" th:utext="${map.post.title}">备战春招,面试刷题跟他复习,一个月全搞定!</a>
<span class="badge badge-secondary bg-primary" th:if="${map.post.type==1}">置顶</span>
<span class="badge badge-secondary bg-danger" th:if="${map.post.status==1}">精华</span>
</h6>
<div class="text-muted font-size-12">
<u class="mr-3" th:utext="${map.user.username}">寒江雪</u> 发布于 <b th:text="${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</b>
<ul class="d-inline float-right">
<li class="d-inline ml-2">赞 <span th:text="${map.likeCount}">11</span></li>
<li class="d-inline ml-2">|</li>
<li class="d-inline ml-2">回帖 <span th:text="${map.post.commentCount}">7</span></li>
</ul>
</div>
</div>
</li>
</ul>
thymeleaf中读取HashMap的值的方法:
为了分页功能,新建Page类,
//当前页码
private int current = 1;
//显示上限
private int limit = 10;
//数据总数(用于计算总的页数)
private int rows;
//查询路径(用于复用分页链接)
private String path;
public int getCurrent() {
return current;
}
public void setCurrent(int current) {
if (current >=1) {
this.current = current;
}
}
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;
}
/**
* 获取当前页的起始行(因为数据库中查询用的是起始行数offset和limit)
*/
public int getOffset() {
//current * limit - limit
return (current -1) * limit;
}
/**
* 获取总页数
*/
public int getTotal() {
//rows/limit [+1]
if (rows % limit == 0) {
return rows/limit;
} else {
return rows/limit + 1;
}
}
/**
* 获取起始页码(显示的时候用)
*/
public int getFrom() {
int from = current - 2;
return from < 1 ? 1 : from;
}
/**
* 获取结束页码
*/
public int getTo() {
int to = current +2;
int total = getTotal();
return to > total ? total : to;
}
再把index.html里分页逻辑写好
<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}(current=1)}">首页</a>
</li>
<!--固定值+变量,外面双竖线-->
<li th:class="|page-item ${page.current==1?'disabled':''}|">
<a class="page-link" th:href="@{${page.path}(current=${page.current-1})}">上一页</a>
</li>
<!--如果i=当前页,就点亮active-->
<li th:class="|page-item ${i==page.current?'active':''}|" th:each="i:${#numbers.sequence(page.from,page.to)}"> <!--#用thymeleaf自带的numbers.sequence工具-->
<a class="page-link" th:href="@{${page.path}(current=${i})}" th:text="${i}">1</a>
</li>
<li th:class="|page-item ${page.current==page.total?'disabled':''}|" >
<a class="page-link" th:href="@{${page.path}(current=${page.current+1})}">下一页</a>
</li>
<li class="page-item">
<a class="page-link" th:href="@{${page.path}(current=${page.total})}">末页</a>
</li>
</ul>
</nav>