牛客网后端项目实战(六):开发社区首页

开发社区首页

  • 开发流程
    • 1次请求的执行过程
  • 分步实现
    • 开发社区首页,显示前10个帖子
    • 开发分页组件,分页显示所有的帖子

我们首先开发社区首页,显示帖子,先查看一下存帖子的表,在navicat右边可以查看DDL(Data Definition Language),也就是建表语句,workbench查看的方法自行百度。根据DDL了解一下表的结构。

然后我们就可以进行开发,根据上图的依赖关系,我们从下往上进行,也就是entity-dao-service-controller的顺序。

实体类

首先是实体类,和上一节的操作一样,比较简单。根据数据库字段写好就行。其实有插件可以自动生成实体类和xml,但是在学习阶段,建议自己敲一遍熟悉一下。

package com.neu.langsam.community.entity;

import java.util.Date;

public class 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 +
                '}';
    }
}

DAO层

实体类写完后,写dao层接口,这里定义了两个方法,一个是查询帖子,另一个是查询帖子数量,可以用来实现后面的分页。需要注意的点在注释里写了。

然后需要写接口对应的mapper配置文件,和上一节内容差不多,有几个需要注意的点

  • namespace要修改成对应的接口
  • 使用了标签动态拼接sql语句,当满足if的条件时,拼接上if标签里的内容,这样我们就可以根据传入的userId的值,来判断是查询首页帖子还是查询我的帖子。
  • 另外使用了order by来排序,首先按照帖子类型倒序,再按照时间倒叙,这样就实现了精华帖置顶并且新帖在前的功能,后面还会实现按热度排序。
package com.neu.langsam.community.dao;

import com.neu.langsam.community.entity.DiscussPost;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface DiscussPostMapper {

    //首页帖子实际不用传入userId,但考虑到之后需要实现我的帖子功能,这里就整合到一起,offset偏移量表示当前是第几行,limit表示每页数量

    List<DiscussPost> selectDiscussPosts(int userId,int offset,int limit);


    //动态拼接条件,方法有且只有一个条件时,需要用@Param注解给参数取别名,也可以在属性名太长的时候取别名来简化
    int selectDiscussPostRows(@Param("userId") int userId);

}

<?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.neu.langsam.community.dao.DiscussPostMapper">

    <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>
        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>

然后测试一下,可以看到控制台输出没问题。

Service层

我们这个业务比较简单,但是也必须严格遵守层次去编写,便于我们后面的一些维护和安全性的实现。这里还有另一个问题,discusspost表里有userId字段,但是我们不可能直接给用户显示id,需要显示用户名。有两种方案:

  • 写sql的时候用id再查询一下用户名拼接到一起
  • 用之前写好的查询user的方法,和我查询到的discusspost再做一个组合

看起来第二种方法更麻烦一些,但是当我们后面用到redis缓存数据库后,这样的方法就可能效率更高了。那么我们再写一个UserService提供查询用户方法。

package com.neu.langsam.community.service;

import com.neu.langsam.community.dao.DiscussPostMapper;
import com.neu.langsam.community.entity.DiscussPost;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class DiscussPostService {

    @Autowired
    private DiscussPostMapper discussPostMapper;

    public List<DiscussPost> findDiscussPosts(int userId,int offset,int limit){
        return discussPostMapper.selectDiscussPosts(userId,offset,limit);
    }

    

    public int findDiscussPostRows(int userId){
        return discussPostMapper.selectDiscussPostRows(userId);
    }

}

package com.neu.langsam.community.service;


import com.neu.langsam.community.dao.UserMapper;
import com.neu.langsam.community.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public User findUserById(int id){
        return userMapper.selectById(id);
    }
}

导入网页

前端的网页我们使用教程模板,在我的gayhub可以找到,把template下的文件复制到项目就行。

gayhub

Controller层

首先在controller包下新建HomeController,使用注解注入Service

	@Autowired
    private DiscussPostService discussPostService;
    @Autowired
    private UserService userService;

第一个任务

首页上显示10个帖子。这里先写固定值,后续再进行改进。前面提到的显示用户名的方式,采用第二种,利用post里的id去查询对应的user,然后用HashMap装到一起,再用list处理map。

最终我们传给模板引擎的就是一个list,list里是每个帖子对应的map,每个map里有post对象和user对象,每个对象有自己的属性和get、set方法。那么利用thymeleaf的语法,去遍历这些对象,将值赋给相应的标签就行。html已经写好了,有兴趣的可以学一学。我们启动项目,在浏览器里访问。

@RequestMapping(path = "/index",method = RequestMethod.GET)
    public String getIndexPage(Model model){
        //查询10条数据
        List<DiscussPost> list=discussPostService.findDiscussPosts(0,0,10);
        //用Map把post和user装到一起
        List<Map<String,Object>> discussPosts=new ArrayList<>();
        //遍历post,用post里的id查询user
        if(list!=null){
            for (DiscussPost post:list){
                Map<String,Object> map=new HashMap<>();
                map.put("post",post);
                User user=userService.findUserById(post.getUserId());
                map.put("user",user);
                discussPosts.add(map);
            }
        }
        //把我们准备好的数据传给model
        model.addAttribute("discussPosts",discussPosts);
		//返回模板
        return "/index";
    }

第二个任务

完成第一个任务后,在这基础上完成分页的功能,我们可以自己封装一个分页类,提高代码的复用。在entity包下创建Page类。使用idea生成get和set方法,这里我们需要修改一下生成的set方法,判断一下传入的数据是否合理。比如setLimit方法,我规定limit必须大于1小于100。

    //当前页码
    private int current=1;
    //显示上限
    private int limit=10;
    //数据的总数,用于计算总页数
    private int rows;
    //查询路径,用于复用分页链接
    private String path;


    public void setLimit(int limit) {
        if (limit>=1&&limit<=100){
        this.limit = 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;
        }

    }


    /*
    *获取起始页码
    * @return
     */
    public int getFrom(){
        int from=current-2;
        return from < 1 ? 1 : from;
    }


    /*
    *获取结束页码
    * @return
     */
    public int getTo(){
        int to=current+2;
        int total=getTotal();
        return to>total?total:to;
    }

然后对controller进行一些改造。

@RequestMapping(path = "/index",method = RequestMethod.GET)
    public String getIndexPage(Model model, Page page){

        //方法调用之前,Spring MVC会自动实例化Model和Page,并讲Page注入Model
        //所以在thymeleaf中可以直接访问page对象中的数据
        page.setRows(discussPostService.findDiscussPostRows(0));
        page.setPath("/index");


        List<DiscussPost> list=discussPostService.findDiscussPosts(0,page.getOffset(),page.getLimit());

那么至此,首页完成了。

发布了12 篇原创文章 · 获赞 0 · 访问量 117

猜你喜欢

转载自blog.csdn.net/weixin_42700635/article/details/104782129
今日推荐