讨论区交流平台项目 - 社区首页开发

功能分析

社区首页主要是显示数据库里面的所有的帖子数据也就是 discuss_post 表里面的数据,但是由于我们数据内容较多,所以还需要进行分页处理。
而其实呢,整个过程就是一次请求的执行过程,而我们采用的是 MVC 处理流程,一次请求到达先到达 Controller 层,Controller 层调用 Server 层提供的方法,Server 层再掉 DAO 层封装好的数据操作的方法。在这里插入图片描述

开发步骤

依赖引入

首先我们知道这样的一个请求执行流程是需要连接到数据库的,我们采用的数据持久化又是 Mybatis 所以我们要引入对应的依赖:

compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.19'
compile group: 'org.mybatis.spring.boot', name: 'mybatis-spring-boot-starter', version: '2.1.1'

properties文件配置

对于 Mysql 和 Mybatis 来说,我们需要自定义一些参数配置,如 Mysql 的连接配置,Mybatis 的配置文件、对应实体类地址声明等。

# DataSourceProperties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/community?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong&allowPublicKeyRetrieval=true
spring.datasource.username=***
spring.datasource.password=***
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.maximum-pool-size=15
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=30000
# MybatisProperties
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.spring.community2.entity
mybatis.configuration.useGeneratedKeys=true
mybatis.configuration.mapUnderscoreToCamelCase=true

代码开发

首先我们先开发社区首页,显示前十个帖子。根据上面的请求执行流程,我们的开发顺序就逆顺序开发,底层的数据操作写好了再写对应的业务层,最后再写视图层。

数据访问层

因为要读取对应的 discuss_post 表里面的内容,所以我们要定义一个 entity 类,里面的参数与 Mysql 表里面的参数进行一一对应:
DiscussPost 实体:

package com.spring.community2.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;
    }
    @Override
    public String toString() {
        return "DiscussPost{" +
                "id=" + id +
                ", userId=" + userId +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", type=" + type +
                ", status=" + status +
                ", createTime=" + createTime +
                ", commentCount=" + commentCount +
                ", score=" + score +
                '}';
    }
    public Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
    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 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;
    }
}

定义好实体,我们就可以定义对应的数据操作了,因为我们是利用 Mybatis,所以我们只需要声明对应的接口,在接口里面确定对应的函数操作,具体的实现我们放在 .xml 文件中实现:
DiscussPostMapper 接口:

package com.spring.community2.dao;
import com.spring.community2.entity.DiscussPost;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface DiscussPostMapper {
    // userId 为 0 时不需要把 userId 拼到 sql 里面,此为一个动态的 sql,offset 是每一页起始的行号,limit 是每页显示多少的数据
    List<DiscussPost> selectDiscussPosts(int userId, int offset, int limit);
    // @Param 参数是用来给参数取别名;如果是一个动态的 sql,并且这个传入进来有且仅有一个参数,这个参数就必须取别名,
    int selectDiscussPostRows(@Param("userId") int userId);
}

discusspost_mapper.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.spring.community2.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>

我们看我们的 discuss_post 表可以知道,对应发帖的用户我们只存了对应的 id,而我们在前端页面展示的时候肯定不可能只展示对应的 id,而是需要展示用户的头像啊,名称啊这些内容,所以我们还要根据 id 查询到对应用户的详细信息,因此对表 User 也需要进行类型的数据操作代码开发。
User 实体:

package com.spring.community2.entity;
import java.util.Date;
public class User {
    private int id;
    private String username;
    private String password;
    private String salt;
    private String email;
    private int type;
    private int status;
    private String activationCode;
    private String headerUrl;
    private Date createTime;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    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 String getSalt() {
        return salt;
    }
    public void setSalt(String salt) {
        this.salt = salt;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    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 String getActivationCode() {
        return activationCode;
    }
    public void setActivationCode(String activationCode) {
        this.activationCode = activationCode;
    }
    public String getHeaderUrl() {
        return headerUrl;
    }
    public void setHeaderUrl(String headerUrl) {
        this.headerUrl = headerUrl;
    }
    public Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", salt='" + salt + '\'' +
                ", email='" + email + '\'' +
                ", type=" + type +
                ", status=" + status +
                ", activationCode='" + activationCode + '\'' +
                ", headerUrl='" + headerUrl + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}

UserMapper 接口:

package com.spring.community2.dao;
import com.spring.community2.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
    User selectById(int id);
    User selectByName(String username);
    User selectByEmail(String email);
    int insertUser(User user);
    int updateStatus(int id, int status);
    int updateHeader(int id, String headerUrl);
    int updatePassword(int id, String password);
}

user_mapper.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.spring.community2.dao.UserMapper">
    <sql id="insertFields">
        username, password, salt, email, type, status, activation_code, header_url, create_time
    </sql>
    <sql id="selectFields">
        id, username, password, salt, email, type, status, activation_code, header_url, create_time
    </sql>
    <select id="selectById" resultType="User">
        select <include refid="selectFields"></include>
        from user
        where id = #{id}
    </select>
    <select id="selectByName" resultType="User">
        select <include refid="selectFields"></include>
        from user
        where username = #{username}
    </select>
    <select id="selectByEmail" resultType="User">
        select <include refid="selectFields"></include>
        from user
        where email = #{email}
    </select>
    <insert id="insertUser" parameterType="User" keyProperty="id">
        insert into user (<include refid="insertFields"></include>)
        values(#{username}, #{password}, #{salt}, #{email}, #{type}, #{status}, #{activationCode}, #{headerUrl}, #{createTime})
    </insert>
    <update id="updateStatus">
        update user set status = #{status} where id = #{id}
    </update>
    <update id="updateHeader">
        update user set header_url = #{headerUrl} where id = #{id}
    </update>
    <update id="updatePassword">
        update user set password = #{password} where id = #{id}
    </update>
</mapper>

至此数据访问层代码就开发完成了。

业务层

再来继续业务层逻辑代码,因为我们的功能就是展现对应的帖子数据,所以业务逻辑只需要调用对应的数据操作就可以了。
DiscussPostService 类:

package com.spring.community2.service;
import com.spring.community2.dao.DiscussPostMapper;
import com.spring.community2.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);
    }
}

UserService 类:

package com.spring.community2.service;
import com.spring.community2.dao.UserMapper;
import com.spring.community2.entity.User;
@Service
public class UserService implements CommunityConstant {
    @Autowired
    private UserMapper userMapper;
    public User findUserById(int id) {
        return userMapper.selectById(id);
    }
}

视图层

最后就是视图层代码,我们定义请求的路径以及方式,通过执行业务层的逻辑返回得到的数据封装到 Model 里面给前端界面进行展示。
HomeController 类:

package com.spring.community2.controller;
import com.spring.community2.entity.DiscussPost;
import com.spring.community2.entity.Page;
import com.spring.community2.entity.User;
import com.spring.community2.service.DiscussPostService;
import com.spring.community2.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
public class HomeController {
    @Autowired
    private UserService userService;
    @Autowired
    private DiscussPostService discussPostService;
    @RequestMapping(path = "/index", method = RequestMethod.GET)
    public String getIndexPage(Model model) {
        List<DiscussPost> list = discussPostService.findDiscussPosts(0, 0, 10);
        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.findUserById(post.getUserId());
                map.put("user", user);
                discussPosts.add(map);
            }
        }
        model.addAttribute("discussPosts", discussPosts);
        return "/index";
    }
}

通过这样的流程开发,前端通过访问 index 路径发送请求就可以得到 discuss_post 表里面的前十条数据了。

分页组件开发

我们知道查询一段范围内的数据在 sql 语句中提供 offset(偏移量)+ limit(查询量)来实现,所以如果想分页查询表里面的数据,其实就是在传递数据的时候传过来根据页码计算得到的 offset 值,因为 limit 值一般都是固定的,所以我们要知道帖子的总数,以及如果在 url 中增加对应的参数。
这里涉及到一个参数转化的事情,如果我们在 URL 中声明的参数,SpringMVC 会自动将请求中的参数映射到Controller 方法参数上,默认按照同名规则映射,无论是基本类型与之同名,还是对象中的成员与之同名。因此我们开发一个 Page 实体封装我们分页所需要的的数据。(看实现效果有具体的说明)
Page 实体:

package com.spring.community2.entity;
public class 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;
    }
    // 获取当前页的起始行
    public int getOffset() {
        return (current - 1) * limit;
    }
    // 获取总页数
    public int getTotal() {
        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;
    }
}

又因为我们需要知道帖子的总数,数据层在上面可以看到我们已经提供了对应的操作,所以在业务层提供对应的逻辑。
DiscussPostService 类:

package com.spring.community2.service;
import com.spring.community2.dao.DiscussPostMapper;
import com.spring.community2.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 int findDiscussPostRows(int userId) {
        return discussPostMapper.selectDiscussPostRows(userId);
    }
}

同样的在视图层我们添加对应的参数类型并设置对应的参数值,其实也就是对 getIndexPage 方法重写一下。
HomeController 类:

package com.spring.community2.controller;
import com.spring.community2.entity.DiscussPost;
import com.spring.community2.entity.Page;
import com.spring.community2.entity.User;
import com.spring.community2.service.DiscussPostService;
import com.spring.community2.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
public class HomeController {
    @Autowired
    private UserService userService;
    @Autowired
    private DiscussPostService discussPostService;
    @RequestMapping(path = "/index", method = RequestMethod.GET)
    public String getIndexPage(Model model, Page page) {
        // 设置帖子的总数
        page.setRows(discussPostService.findDiscussPostRows(0));
        // 设置分页的访问路径
        page.setPath("/index");
        List<DiscussPost> list = discussPostService.findDiscussPosts(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.findUserById(post.getUserId());
                map.put("user", user);
                discussPosts.add(map);
            }
        }
        model.addAttribute("discussPosts", discussPosts);
        return "/index";
    }
}

至此整体的代码开发就结束了。

实现效果

在这里插入图片描述
这一块我们可以看到分页组件传递了 current 参数过去,这个参数在 Page 里面也定义了,而根据 SpringMVC 来说就会封装到 Page 声明的 page 参数里面的 current 属性。

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读