仿牛客社区学习笔记(四)开发社区首页-1

开发一个项目的流程

首先,web项目主要解决的就是浏览器和服务器之间的交互,开发流程就是1次请求的执行过程,任何功能都可以拆解成若干次请求。

请求步骤

仿牛客社区学习笔记(四)开发社区首页-1_第1张图片

  1. 请求提交给视图层,视图层代码主要由/Controller和/templates(模板)构成
  2. Controller处理请求期间要访问业务层,要业务组件处理具体业务
  3. 业务组件调用数据组件,访问数据库

开发步骤

按照依赖顺序Dao->Service->Controller
以实现社区首页显示前10个帖子功能为例

Dao

1.在数据库中建表

仿牛客社区学习笔记(四)开发社区首页-1_第2张图片

  1. MySQL Workbench点击表后面的i按钮,选择DDL可以查看表的字段和字段含义
  2. 字段备注可以在表上点击右键Alter
  3. Table进行修改 “content”字段类型为text不为varchar,因为帖子内容比较长

2.在/entity包下创建实体类

实体类的作用是封装表里的数据,命名为大写驼峰式,eg:DiscussPost.java

  1. 根据数据表中的字段加上对应的属性
private int userId;

PS:字段名称为user_id,属性名为userId

  1. 因为属性都是private类型,通过Idea快捷键Alt+Insert给字段生成get和set函数
  2. 为了测试打印数据方便,通过Idea快捷键Alt+Insert给字段生成toString函数
package com.nowcoder.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 +
                '}';
    }
}

3.开发数据访问组件Mapper

要实现某个功能,要在mapper接口中声明查询方法,
并在配置文件mapper.xml中写出与之有关的SQL。

  • /dao文件中创建一个DiscussPostMapper.java(接口类型)(命名大写驼峰),一般一个表对应一个mapper接口
    PS:mapper接口前@Mapper注解,才能被容器自动扫描这个接口,容器才能自动实现和装配它
@Mapper
public interface DiscussPostMapper {
    //分页查询帖子功能,返回的是多条数据的是一个集合,集合里装的是帖子的对象
    //方法名自己取,参数是userId,帖子表有userId字段
    //首页查询不需要传入userId,默认为0,当userId为0时不管,不拼到SQL中
    //不为0时,正常拼到SQL中,SQL是动态的SQL
    //分页功能:MySQL数据库实现只需要设置limit参数:起始行行号offset,这一页最多显示多少条数据limit
     List<DiscussPost> selectDiscussPosts(int userId, int offset, int limit);

     //一共有多少页:一共多少页,每页显示多少条数据
    //查询表里一共多少行数据功能

    //@Param("userId") 用于给参数起别名,比如有的参数名称比较长
    //如果只有一个参数,并且在里使用,则必须要加别名
     //如果在SQL中需要用到动态的拼接条件,条件里需要用到参数,且方法只有一个参数,参数必须要起别名
    int selectDiscussPostRows(@Param("userId") int userId);


}

  • /mapper目录中创建一个discusspost-mapper.xml配置文件
    步骤:新建一个file,将后缀写成.xml
    代码代码的模板复制于Mabatis官网入门菜单
    这里有几个字段需要学习一下
    
    <sql id="selectFields">
        id, user_id, title, content, type, status, create_time, comment_count, score
    sql>
    
    <include refid="selectFields">include>

<select id="selectDiscussPosts" resultType="DiscussPost">select>

        <if test="userId!=0">
            and user_id = #{userId}
        if>
//字段=#{参数}
user_id = #{userId}

完整代码:


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nowcoder.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>

4.在/test/…目录下创建测试类MapperTests.java

  • @Autowired 注入mapper
  • @Test编写测试函数
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class MapperTests {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private DiscussPostMapper discussPostMapper;

    @Test
    public void testSelectUser(){
        User user = userMapper.selectById(101);
        System.out.println(user);

        user = userMapper.selectByName("liubei");
        System.out.println(user);

        user = userMapper.selectByEmail("[email protected]");
        System.out.println(user);
    }

    @Test
    public void testInsertUser(){
        User user = new User();
        user.setUsername("test");
        user.setPassword("123456");
        user.setSalt("abc");
        user.setEmail("[email protected]");
        user.setHeaderUrl("https://www.nowcoder.com/101.png");
        user.setCreatTime(new Date());

        int rows = userMapper.insertUser(user);
        System.out.println(rows);
        System.out.println(user.getId());
    }

    @Test
    public void updateUser(){
        int rows = userMapper.updateStatus(150,1);
        System.out.println(rows);

        rows = userMapper.updateHeader(150,"https://www.nowcoder.com/102.png");
        System.out.println(rows);

        rows = userMapper.updatePassword(150,"hello");
        System.out.println(rows);


    }

    @Test
    public void testSelectPosts(){
        List<DiscussPost> list = discussPostMapper.selectDiscussPosts(149,0,10);
        for (DiscussPost post : list){
            System.out.println(post);
        }

        int rows = discussPostMapper.selectDiscussPostRows(149);
        System.out.println(rows);
    }
}

Service

在/service包下新建业务组件DiscussPostService.java和UserSevice.java

  1. 加入@Service注解
  2. 注入mapper接口
  3. 编写业务层方法
@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);
    }
}
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    //根据用户id查询用户的方法
    public User findUserById(int id){
        return userMapper.selectById(id);
    }
}

视图层

Controller

  1. 在/controller包下创建HomeController.java
  2. 设置@Controller注解
  3. 通过@Autowired注入DiscussPostService和UserService
  4. 通过@RequestMapping(path = “/index”, method = RequestMethod.GET)定义访问路径和方法
  5. 增加处理请求的方法
@Controller
public class HomeController {

    @Autowired
    private DiscussPostService discussPostService;

    @Autowired
    private UserService userService;

    @RequestMapping(path = "/index", method = RequestMethod.GET)
        List<DiscussPost> list = discussPostService.findDiscussPosts(0, page.getOffset(), page.getLimit());
        //遍历DiscussPost,根据每一个userId查user
        //封装DiscussPost和User对象
        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.html文件

  1. 将静态和动态页面复制到/static和/templates文件夹中
  2. 指定模板引擎
<html lang="en" xmlns:th="http://www.thymeleaf.org">
  1. 修改依赖的样式和文件,绝对路径不修改,相对路径修改

<link rel="stylesheet" th:href="@{css/global.css}" />
<link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/>
  1. 循环输出
 th:each="map:${discussPosts}"
  1. 修改头像路径
 th:src="${map.user.headerUrl}"

表示

map.get("user")->User->user.getHeaderUrl()
  1. 显示文件标题
th:utext="${map.post.title}"
  1. 满足条件才显示
th:if="${map.post.type==1}"
  1. 格式化时间
th:text="${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}"

完整代码:


<ul class="list-unstyled">
	<li class="media pb-3 pt-3 mb-3 border-bottom" th:each="map:${discussPosts}">
		<a href="site/profile.html">
			<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 href="#" 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:18b>
						<ul class="d-inline float-right">
							<li class="d-inline ml-2">赞 11li>
							<li class="d-inline ml-2">|li>
							<li class="d-inline ml-2">回帖 7li>
						ul>
					div>
				div>
			li>
		ul>

你可能感兴趣的:(仿牛客社区课程笔记,学习)