第1章 初始Spring Boot【仿牛客网社区论坛项目】

第1章 初始Spring Boot【仿牛客网社区论坛项目】

  • 前言
  • 推荐
  • 项目总结
    • 第1章初识Spring Boot,开发社区首页
      • 1.课程介绍
      • 2.搭建开发环境
      • 3.Spring入门
        • 体验IOC容器
        • 体验Bean的生命周期
        • 体验配置类
        • 体验依赖注入
        • 体验三层架构
      • 4.SpringMVC入门
        • 配置
        • 体验响应数据
        • 体验响应Get请求
        • 体验响应Post请求
        • 体验响应HTML数据
        • 体验响应JSON数据
      • 5MyBatis入门
        • 配置
        • User
        • UserMapper
        • user-mapper.xml
        • 测试:MapperTest
        • 设置日志级别
      • 6开发社区首页
        • DiscussPost
        • DiscussPostMapper
        • discusspost-mapper.xml
        • 测试Dao层
        • DiscussPostService
        • UserService
        • 前端资源准备
        • HomeController
        • index.html
        • Page
      • 7项目调试技巧
        • 测试:日志功能
      • 8版本控制
  • 最后

前言

2023-4-30 20:42:51

以下内容源自【Java面试项目】
仅供学习交流使用

推荐

仿牛客网项目【面试】

项目总结

第1章初识Spring Boot,开发社区首页

在这里插入图片描述

在这里插入图片描述

1.课程介绍

2.搭建开发环境

创建项目,完成搭建

添加依赖

  • aspectj aop包
  • Spring Web web开发
  • Thymeleaf 模板引擎
  • Spring Boot DevTools 开发者工具
  • 其他:使用时添加
    在这里插入图片描述

配置application.properties

#ServerProperties
#端口配置
server.port=8080
#路径配置
server.servlet.context-path=/community

创建一个AlphaController

编写sayHello()

@Controller
@RequestMapping("/alpha")
public class AlphaController {
    @RequestMapping("/hello")
    @ResponseBody//返回字符串
    public String sayHello(){
        return "Hello Spring Boot.";
    }
}

访问:http://localhost:8080/community/alpha/hello

在这里插入图片描述

3.Spring入门

修改测试代码

体验IOC容器

ApplicationContext,管理Bean

package com.jsss.community;


@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
class CommunityApplicationTests implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }

    @Test
    public void testApplicationContext(){
        System.out.println(applicationContext);
    }
}

创建一个Bean
AlphaDAO

package com.jsss.community.dao;

public interface AlphaDao {
    String select();

}

两个实现类

AlphaDaoHibernateImpl

package com.jsss.community.dao;

import org.springframework.stereotype.Repository;

@Repository("alphaDaoHibernate")//容器管理 指定Bean名字
public class AlphaDaoHibernateImpl implements AlphaDao{

    @Override
    public String select() {
        return "Hibernate";
    }
}

AlphaDaoMybatisImpl

package com.jsss.community.dao;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Repository;


@Repository
@Primary//优先装配
public class AlphaDaoMybatisImpl implements AlphaDao{
    @Override
    public String select() {
        return "Mybatis";
    }
}

测试:testApplicationContext()

package com.jsss.community;

import com.jsss.community.dao.AlphaDao;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeansException;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.test.context.ContextConfiguration;


@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
class CommunityApplicationTests implements ApplicationContextAware {//SpringBoot会进行自动配置

    private ApplicationContext applicationContext;

    //配置方法
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }

    //测试容器
    @Test
    public void testApplicationContext(){
        System.out.println(applicationContext);

        AlphaDao alphaDao=applicationContext.getBean(AlphaDao.class);
        System.out.println(alphaDao.select());//Mybatis
        alphaDao= (applicationContext.getBean("alphaDaoHibernate",AlphaDao.class));
        System.out.println(alphaDao.select());//Hibernate
    }
}

体验Bean的生命周期

创建AlphaService

package com.jsss.community.service;

import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Service//容器管理
//作用域 单例 多态
//@Scope("singleton")
//@Scope("prototype")
public class AlphaService {

    @PostConstruct//IOC容器管理Bean生命周期:初始化
    public void init(){
        System.out.println("初始化AlphaService");
    }

    @PreDestroy//IOC容器管理Bean生命周期:销毁
    public void destroy(){
        System.out.println("销毁AlphaService");
    }
}

测试:testBean()

    //测试Bean的生命周期
    //修改Scope作用域
    @Test
    public void testBean(){
        AlphaService alphaService=applicationContext.getBean(AlphaService.class);
        System.out.println(alphaService);

        alphaService=applicationContext.getBean(AlphaService.class);
        System.out.println(alphaService);
    }
体验配置类

添加AlphaConfig

package com.jsss.community.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.text.SimpleDateFormat;

@Configuration
public class AlphaConfig {
    //配置类使用Bean注解添加Bean
    //方法名就是Bean名字
    @Bean
    public SimpleDateFormat simpleDateFormat(){
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    }

}

测试:getBeanConfig()

    //测试配置类
    @Test
    public void getBeanConfig(){
        SimpleDateFormat simpleDateFormat=applicationContext.getBean(SimpleDateFormat.class);
        System.out.println(simpleDateFormat.format(new Date()));
    }
体验依赖注入

容器的主动getBean

使用注解@AutoWire

测试:testDI()

    @Autowired
    @Qualifier("alphaDaoHibernate")//指定Bean名字
    private AlphaDao alphaDao;

    @Autowired
    private AlphaService alphaService;

    @Autowired
    private SimpleDateFormat simpleDateFormat;
    @Test
    public void testDI(){
        System.out.println(alphaDao);
        System.out.println(alphaService);
        System.out.println(simpleDateFormat);
    }
体验三层架构

Controller–Service–Dao

Service调用Dao

package com.jsss.community.service;

import com.jsss.community.dao.AlphaDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Service//容器管理
//作用域 单例 多态
//@Scope("singleton")
//@Scope("prototype")
public class AlphaService {

    @Autowired
    private AlphaDao alphaDao;

    @PostConstruct//IOC容器管理Bean生命周期:初始化
    public void init(){
        System.out.println("初始化AlphaService");
    }

    @PreDestroy//IOC容器管理Bean生命周期:销毁
    public void destroy(){
        System.out.println("销毁AlphaService");
    }

    public String find(){
        return alphaDao.select();
    }

}

Controller调用Service

package com.jsss.community.controller;

@Controller
@RequestMapping("/alpha")
public class AlphaController {

    @Autowired
    private AlphaService alphaService;
    
    @RequestMapping("/data")
    @ResponseBody
    public String getData(){
        return alphaService.find();
    }
}

访问:http://localhost:8080/community/alpha/data
在这里插入图片描述

4.SpringMVC入门

配置
# ThymeleafProperties
spring.thymeleaf.cache=false
体验响应数据

AlphaController.http()

	@RequestMapping("/http")
    public void http(HttpServletRequest request, HttpServletResponse response){
        //获取请求数据
        System.out.println(request.getMethod());// GET
        System.out.println(request.getServletPath());// /alpha/http
        Enumeration<String> enumeration = request.getHeaderNames();
        while (enumeration.hasMoreElements()){
            String name = enumeration.nextElement();
            String value = request.getHeader(name);
            System.out.println(name+": "+value);
        }
        System.out.println(request.getParameter("code"));//123

        // 返回响应数据
        response.setContentType("text/html;charset=utf-8");
        try (
                PrintWriter writer = response.getWriter();
        ){
            writer.write("<h1>牛客网</h1>");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

访问:http://localhost:8080/community/alpha/http?code=123

体验响应Get请求
  • 路径拼接
  • Restful风格

AlphaController.getStudents()

AlphaController.getStudent()

	// GET 请求

    // /students?current=1&limit=20
    @RequestMapping(path = "/students",method = RequestMethod.GET)
    @ResponseBody
    public String getStudents(
            @RequestParam(name = "current",required = false,defaultValue = "1") int current,
            @RequestParam(name = "limit",required = false,defaultValue = "10") int limit){
        System.out.println(current);
        System.out.println(limit);
        return "some students";
    }

    // /student/123
    @RequestMapping(path = "/student/{id}",method = RequestMethod.GET)
    @ResponseBody
    public String getStudent(@PathVariable("id") int id){
        System.out.println(id);
        return "a student";
    }

访问:http://localhost:8080/community/alpha/students?current=1&limit=20

访问:http://localhost:8080/community/alpha/student/123

体验响应Post请求

新增页面:static/html/student.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>增加学生</title>
</head>
<body>
    <form method="post" action="/community/alpha/student">
        <p>
            姓名:<input type="text" name="name">
        </p>
        <p>
            年龄:<input type="text" name="age">
        </p>
        <p>
            <input type="submit" value="保存">
        </p>
    </form>
</body>
</html>

AlphaController.save()

    //  POST请求
    @RequestMapping(path = "/student",method = RequestMethod.POST)
    @ResponseBody
    public String save(String name,int age){
        System.out.println(name);
        System.out.println(age);
        return "success";
    }

访问:http://localhost:8080/community/html/student.html
在这里插入图片描述

体验响应HTML数据

AlphaController.getTeacher()

    //响应HTML数据

    @RequestMapping(path = "/teacher",method = RequestMethod.GET)
    public ModelAndView getTeacher(){
        ModelAndView mav=new ModelAndView();
        mav.addObject("name","张三");
        mav.addObject("age",30);
        mav.setViewName("/demo/view");
        return mav;
    }

新增页面:template/demo/view.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Teacher</title>
</head>
<body>
  <p th:text="${name}"></p>
  <p th:text="${age}"></p>
</body>
</html>

访问:http://localhost:8080/community/alpha/teacher
在这里插入图片描述

AlphaController.getSchool()

    @RequestMapping(path = "/school",method = RequestMethod.GET)
    public String getSchool(Model model){
        model.addAttribute("name","北京大学");
        model.addAttribute("age","80");
        return "demo/view";
    }

访问:http://localhost:8080/community/alpha/school

在这里插入图片描述

体验响应JSON数据

AlphaController.getEmp()
AlphaController.getEmps()

	// 响应JSON数据(异步请求)
    // Java对象 -> JSON字符串 -> JS对象

    @RequestMapping(path = "/emp",method = RequestMethod.GET)
    @ResponseBody
    public Map<String,Object> getEmp(){
        Map<String,Object> emp =new HashMap<>();
        emp.put("name","张三");
        emp.put("age",23);
        emp.put("salary",8000.00);
        return emp;
    }

    @RequestMapping(path = "/emps",method = RequestMethod.GET)
    @ResponseBody
    public List<Map<String,Object>> getEmps(){
        List<Map<String,Object>> list=new ArrayList<>();
        Map<String,Object> emp =new HashMap<>();
        emp.put("name","张三");
        emp.put("age",23);
        emp.put("salary",8000.00);
        list.add(emp);

        emp =new HashMap<>();
        emp.put("name","李四");
        emp.put("age",24);
        emp.put("salary",9000.00);
        list.add(emp);

        emp =new HashMap<>();
        emp.put("name","王五");
        emp.put("age",25);
        emp.put("salary",10000.00);
        list.add(emp);

        return list;
    }

访问:http://localhost:8080/community/alpha/emp
在这里插入图片描述
访问:http://localhost:8080/community/alpha/emps
在这里插入图片描述

5MyBatis入门

配置

添加依赖

        <!--  mysql      -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>

        <!--mybatis整合spring-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>

添加配置

# 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

spring.datasource.username=root
spring.datasource.password=root
#spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.type=com.mysql.cj.jdbc.MysqlDataSource
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.jsss.community.entity
#启用自动自增主键
mybatis.configuration.useGeneratedKeys=true
#开启驼峰命名自动匹配
mybatis.configuration.mapUnderscoreToCamelCase=true
User

新建:/entity/User.java

package com.jsss.community.entity;

import java.util.Date;


import lombok.*;

@AllArgsConstructor
@NoArgsConstructor
@Data
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;


}

UserMapper

新建:/dao/UserMapper.java

package com.jsss.community.dao;

import com.jsss.community.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface UserMapper {
    User selectById(int id);

    User selectByName(String name);

    User selectByEmail (String email);

    int insertUser(User user);

    int updateStatus(@Param("id")int id, @Param("status") int status);

    int updateHeader(@Param("id")int id, @Param("headerUrl")String headerUrl);

    int updatePassword(@Param("id")int id, @Param("password")String password);
}

user-mapper.xml

新建:/mapper/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.jsss.community.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>

测试:MapperTest

新建:test:MapperTest.java

package com.jsss.community;


import com.jsss.community.dao.UserMapper;

import com.jsss.community.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;

import java.util.Date;


@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class MapperTest {
    @Autowired
    private UserMapper userMapper;

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

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

        user = userMapper.selectByEmail("nowcoder101@sina.com");
        System.out.println(user);
    }

    @Test
    public void testInsertUser() {
        User user = new User();
        user.setUsername("test");
        user.setPassword("123456");
        user.setSalt("abc");
        user.setEmail("test@qq.com");
        user.setHeaderUrl("http://www.nowcoder.com/101.png");
        user.setCreateTime(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, "http://www.nowcoder.com/102.png");
        System.out.println(rows);

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

}

设置日志级别
# logger
logging.level.com.jsss.community=debug

2023-4-30 23:33:09

6开发社区首页

DiscussPost

新增:entity/DiscussPost.java

package com.jsss.community.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
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;

}

DiscussPostMapper

新增:/dao/DiscussPostMapper.java

package com.jsss.community.dao;

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

import java.util.List;

@Mapper
public interface DiscussPostMapper {

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

    // @Param注解用于给参数取别名,
    // 如果只有一个参数,并且在<if>里使用,则必须加别名.
    int selectDiscussPostRows(@Param("userId") int userId);

}


discusspost-mapper.xml

新增:/mapper/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.jsss.community.dao.DiscussPostMapper">

    <sql id="selectFields">
        id, user_id, title, content, type, status, create_time, comment_count, score
    </sql>

    <sql id="insertFields">
        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>


测试Dao层

新增:MapperTest.testSelectPosts()

    @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);
    }
DiscussPostService

新增:/service/DiscussPostService.java

package com.jsss.community.service;


import com.jsss.community.dao.DiscussPostMapper;


import com.jsss.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);
    }


}

对于帖子:
我们要显示用户名称而不是用户id

有两种解决办法:

  • 关联查询
  • 在高层添加
UserService

新增:/service/UserService.java

package com.jsss.community.service;


import com.jsss.community.dao.UserMapper;
import com.jsss.community.entity.User;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

@Service
public class UserService{
    @Autowired
    UserMapper userMapper;
    
    public User findUserById(int id){
        return userMapper.selectById(id);
    }

}

前端资源准备

复制静态文件到static下
在这里插入图片描述

复制网页到templates
在这里插入图片描述

顺便把mail复制进来

mail:将来学习发邮件的模板

HomeController

新增:/controller/HomeController()

package com.jsss.community.controller;

import com.jsss.community.entity.DiscussPost;
import com.jsss.community.entity.User;
import com.jsss.community.service.DiscussPostService;
import com.jsss.community.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 DiscussPostService discussPostService;

    @Autowired
    private UserService userService;


    @RequestMapping(path = "/", method = RequestMethod.GET)
    public String root() {
        return "forward:/index";

    }

    @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.html

修改html标签

<html lang="en" xmlns:th="http://www.thymeleaf.org">

修改静态路径

	<!--前面-->
	<link rel="stylesheet" th:href="@{/css/global.css}" />
	<!--后面-->
	<script th:src="@{/js/global.js}"></script>
	<script th:src="@{js/index.js}"></script>

主要修改内容main的部分

修改:帖子列表的部分
静态数据改为动态数据

				<!-- 帖子列表 -->
				<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:18</b>
								<ul class="d-inline float-right">
									<li class="d-inline ml-2">赞 11</li>
									<li class="d-inline ml-2">|</li>
									<li class="d-inline ml-2">回帖 7</li>
								</ul>
							</div>
						</div>						
					</li>
				</ul>

访问:http://localhost:8080/community/index

Page

分页功能

新增:/entity/Page.java

package com.jsss.community.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;
    }

    /**
     * 获取当前页的起始行
     *
     * @return
     */
    public int getOffset() {
        // current * limit - limit
        return (current - 1) * limit;
    }

    /**
     * 获取总页数
     *
     * @return
     */
    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;//超的时候,就按最大页算
    }

}

修改:HomeController.getIndexPage()

    @RequestMapping(path = "/index", method = RequestMethod.GET)
    public String getIndexPage(Model model, Page page) {
        // 方法调用栈,SpringMVC会自动实例化Model和Page,并将Page注入Model.
        // 所以,在thymeleaf中可以直接访问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";
    }

修改:index.html:分页

				<!-- 分页 -->
				<nav class="mt-5" th:if="${page.rows>0}">
					<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>
						<li th:class="|page-item ${i==page.current?'active':''}|" th:each="i:${#numbers.sequence(page.from,page.to)}">
							<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>
			</div>
		</div>

访问:http://localhost:8080/community/index?current=1

7项目调试技巧

测试:日志功能

新增:test:LoggerTests.java

package com.jsss.community;

import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;

@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class LoggerTests {

    private static final Logger logger= LoggerFactory.getLogger(LoggerTests.class);

    @Test
    public void testLogger(){
        System.out.println(logger.getName());
        
        logger.debug("debug log");
        logger.info("info log");
        logger.warn("warn log");
        logger.error("error log");
    }
}

配置:

# logger
logging.level.com.jsss.community=debug
logging.file.name=E:/work/data/jsss/community.log

添加:/logback-spring.xml

8版本控制

git的使用

git add .
git commit -m "消息"
git push
git status
git reset HEAD

最后

这篇博客能写好的原因是:站在巨人的肩膀上

这篇博客要写好的目的是:做别人的肩膀

开源:为爱发电

学习:为我而行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/627405.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java应用程序的本地内存跟踪分析

本文将讨论本机内存跟踪 (NMT)&#xff0c;我们可以使用它来隔离在 VM 级别增长的任何异常内存。 1.什么是本机内存&#xff1f; 本机内存是指计算机系统上运行的应用程序或程序可直接访问的内存空间。它是程序在执行期间存储和操作数据的内存区域。本机内存不同于托管内存&a…

实物仿真平台设计方案:927-8路GMSL视频注入回灌的自动驾驶半实物仿真平台

8路GMSL视频注入回灌的自动驾驶半实物仿真平台 一、平台介绍 产品基于8路GMSL视频注入回灌的自动驾驶半实物仿真平台旨在提高实验室及研究生院师生在基础软件层开发、计算机视觉和深度学习方面的专业知识学习和实践能力&#xff0c;为师生提供一个稳定软件开发和多精度框…

【C++】认识C++(上)

目录 从C到C命名空间同名冲突命名空间的定义命名空间的使用 C的输入和输出缺省参数&#xff08;默认参数&#xff09; 从C到C C语言的出现是计算机科学和工程史上的一个重要里程碑&#xff0c;许多现代计算机语言都受C语言的影响。C语言是面向过程的&#xff0c;结构化和模块化…

优选算法——双指针2

题目一——有效三角形的个数 思路 先审题 举个例子&#xff0c;下面一个序列可分成4个三元组 然后我们论证哪个可以组成三角形即可 判断三个数能不能组成三角形&#xff1a;任意两边之和大于第三边 注意第一个和第四个&#xff0c;有人说&#xff0c;这不是两个相同的吗&#…

【opencv】opencv透视变换和ocr识别实验

实验环境&#xff1a;anaconda、jupyter notebook 实验用到的包opencv、numpy、matplotlib、tesseract 一、opencv透视变换 原图 图片是我拍的耳机说明书&#xff0c;哈哈哈哈&#xff0c;你也可以使用自己拍的照片&#xff0c;最好是英文内容&#xff0c;tesseract默认识别英…

JVM运行时内存整体结构一览

文章目录 Java 虚拟机 (JVM) 运行时内存由程序计时器, 堆, 方法区, 本地方法栈, 虚拟机栈,构成 Java 虚拟机 (JVM) 运行时内存布局主要包括以下几个部分&#xff1a; 程序计数器 (Program Counter Register): 每个线程都有一个程序计数器&#xff0c;它是当前线程执行的字节码…

【js逆向】易车网JS逆向案例实战手把手教学(附完整代码)

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全…

删除表空间

Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 当某个表空间中的数据不再需要时&#xff0c;或者新创建的表空间不符合要求时&#xff0c;可以考虑删除这个表空间。若要删除表空间&#xff0c;则需要用户具有 DROP TABLESP…

OpenNJet产品体验:探索无限可能

文章目录 前言一、OpenNJet是什么&#xff1f;二、OpenNJet特性和优点三、OpenNJet功能规划四、OpenNJet快速上手五、OpenNJet的使用总结 前言 现代社会网络高速发展&#xff0c;同时也迎来了互联网发展的高峰&#xff0c;OpenNJet作为一个基于NGINX的面向互联网和云原生应用提…

【C语言每日题解】三题:回文检查、刘备 关羽 张飞三人过年放鞭炮、犹太人死亡游戏(难度up,推荐⭐✨)

&#x1f970;欢迎关注 轻松拿捏C语言系列&#xff0c;来和 小哇 一起进步&#xff01;✊ &#x1f308;感谢大家的阅读、点赞、收藏和关注 &#x1f970;希望大家喜欢我本次的讲解 &#x1f31f;非常推荐最后一道题 &#x1f339; 犹太人死亡游戏&#xff0c;建议观看 &…

20240514,算法(算数生成,集合)

还有一个大案例&#xff0c;那个就不急了&#xff0c;完结撒花&#xff0c;起码C是打代码没什么大问题的完结&#xff0c;不像C&#xff0c;还要我返工/笑哭 常用算数生成算法 属于小算法&#xff0c;头文件 #include <numeric> accumulate //计算容器累计总和fill /…

考研数学|李林《880》PK李永乐《660》,你用对了吗?

建议先在强化之前做660&#xff0c;然后在强化的时候再做880。 660整体难度属于基础阶段到强化阶段。而且是选填部分的题目&#xff0c;所以还是要做一些其他题 然后说一下推荐的习题册&#xff1a;基础不好先做1800、强化之前660&#xff0c;强化可选880/1000题。但是传统习题…

FPGA - Xilinx系列高速收发器---GTX

1&#xff0c;GTX是什么&#xff1f; GT &#xff1a;Gigabit Transceiver千兆比特收发器&#xff1b; GTX &#xff1a;Xilinx 7系列FPGA的高速串行收发器&#xff0c;硬核 xilinx的7系列FPGA根据不同的器件类型&#xff0c;集成了GTP、GTX、GTH、GTZ四种串行高速收发器&am…

Ansible自动化运维中的User用户管理模块应用详解

作者主页&#xff1a;点击&#xff01; Ansible专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年5月14日14点12分 在Ansible中&#xff0c;user 模块主要用于管理系统用户账户。它可以创建、修改、删除用户&#xff0c;并管理用户的属性&#xff0c;比如密码、…

深⼊理解指针(5)

目录 1. 回调函数是什么&#xff1f;1.1 使用回调函数修改 2. qsort使⽤举例2.1 使⽤qsort函数排序整型数2.2 使⽤qsort排序结构数据按年龄排序2.3 使⽤qsort排序结构数据按名字排序2.4整体代码 3. qsort函数的模拟实现3.1 整型数组的实现3.2 结构体按名字排序实现3.3 结构体按…

Element Plus组件库使用组件自动导入后样式不生效的问题

首先按照官方文档上的介绍进行配置&#xff1a;快速开始 | Element Plus (element-plus.org) 配置完成后&#xff0c;去组件中去测试组件库中的button组件的样式是否生效 <template><el-button type"primary">Primary</el-button> </template&…

从源头到洞察:大数据时代的数据提取与分析实战指南

随着科技的飞速发展&#xff0c;大数据已经成为现代社会的核心驱动力之一。从商业决策到科学研究&#xff0c;从政策制定到个人生活&#xff0c;数据无处不在&#xff0c;影响着我们的每一个决策。然而&#xff0c;如何从海量的数据中提取有价值的信息&#xff0c;并转化为深刻…

一对一WebRTC视频通话系列(六)——部署到公网

本系列博客主要记录一对一WebRTC视频通话实现过程中的一些重点&#xff0c;代码全部进行了注释&#xff0c;便于理解WebRTC整体实现。 本专栏知识点是通过<零声教育>的音视频流媒体高级开发课程进行系统学习&#xff0c;梳理总结后写下文章&#xff0c;对音视频相关内容感…

Milvus 安装与配置

一、环境准备 在安装 Milvus 之前&#xff0c;确保你的系统满足以下要求&#xff1a; 操作系统&#xff1a;Milvus 支持 Linux 操作系统&#xff0c;如 Ubuntu、CentOS 等。硬件资源&#xff1a;推荐使用具有足够 CPU、内存和 SSD 存储的机器。对于大规模数据集&#xff0c;高…

环境光遮蔽技术在AI去衣应用中的创新探索

引言&#xff1a; 随着计算机视觉和人工智能技术的飞速发展&#xff0c;AI去衣技术逐渐走进公众视野。这一技术以其独特的应用前景和技术挑战引起了广泛的关注。在实现衣物去除的同时保持图像质量的关键技术之一&#xff0c;便是环境光遮蔽技术。本文将深入探讨环境光遮蔽技术在…