1.创建项目
导入以下依赖
2.项目介绍
使⽤SSM框架实现⼀个简单的博客系统
共5个页面
2.1 前端页面展示
2.1.1 用户登录
2.1.2 博客发表页
2.1.3 博客编辑页
2.1.4 博客列表页
2.1.5博客详情页
2.2 功能描述:
⽤⼾登录成功后, 可以查看所有⼈的博客.
点击 <<查看全⽂>> 可以查看该博客的正⽂内容.
如果该博客作者为当前登录⽤⼾, 可以完成博客的修改和删除操作, 以及发表新博客
3.准备工作
3.1 数据准备
3.1.1 建立SQL
-- 建表SQL
create database if not exists java_blog_spring charset utf8mb4;
USE java_blog_spring;
-- 用户表
DROP TABLE IF EXISTS java_blog_spring.user;
CREATE TABLE java_blog_spring.user(
`id` INT NOT NULL AUTO_INCREMENT,
`user_name` VARCHAR ( 128 ) NOT NULL,
`password` VARCHAR ( 128 ) NOT NULL,
`github_url` VARCHAR ( 128 ) NULL,
`delete_flag` TINYINT ( 4 ) NULL DEFAULT 0,
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now(),
PRIMARY KEY ( id ),
UNIQUE INDEX user_name_UNIQUE ( user_name ASC )) ENGINE = INNODB DEFAULT
CHARACTER
SET = utf8mb4 COMMENT = '用户表';
-- 博客表
drop table if exists java_blog_spring.blog;
CREATE TABLE java_blog_spring.blog (
`id` INT NOT NULL AUTO_INCREMENT,
`title` VARCHAR(200) NULL,
`content` TEXT NULL,
`user_id` INT(11) NULL,
`delete_flag` TINYINT(4) NULL DEFAULT 0,
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now(),
PRIMARY KEY (id))
ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '博客表';
-- 新增用户信息
insert into java_blog_spring.user (user_name, password,github_url)values
("lay","107","https://www.weibo.com/u/2706896955?c=spr_qdhz_bd_360ss_weibo_mr");
insert into java_blog_spring.user (user_name, password,github_url)
values("muyierf","123456","https://gitee.com/muyierf");
insert into java_blog_spring.blog (title,content,user_id) values("第1篇博
客","lay的筑梦之旅",1);
insert into java_blog_spring.blog (title,content,user_id) values("第1篇博
客","muyierf的第一篇博客",2);
创建成功!!!
3.2 前端代码引入项目中
3.3 配置配置文件
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/java_blog_spring?characterEncoding=utf8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration:
map-underscore-to-camel-case: true #配置驼峰⾃动转换
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql语句
mapper-locations: classpath:mapper/**Mapper.xml
# 设置⽇志⽂件的⽂件名
logging:
file:
name: spring-book.log
3.4 测试项目能否启动成功
成功!!!
3.5 开发前的准备
3.5.1 项目流程
1.需求分析
2.技术方案设计
1)UML图,流程图,ER图2)数据库设计
3)接口设计(我们需要完成的工作)
3.开发(我们需要完成的工作)
4.自测(我们需要完成的工作)
5.测试(QA)
6.联调
7. 验收
8.上线
3.5.2 数据库表的创建
数据库表通常分两类:
1.实体表->对象 (员工表,部门表,图书表,博客表..)2.关系表->实体之间的关系
我们的项目较为简单,所以我们只需要两个实体表即可。
用户表:用户名,密码,照片,昵称,github地址
1.根据用户名,查询用户信息
2.根据用户ID,查询用户信息
博客表: 标题,日期,内容,作者, 分类..
1.查询博客列表
2.根据博客ID, 查询博客信息
3.根据博客ID, 修改博客信息4.插入博客
3.5.3 接口设计
1.用户登录
根据用户名和密码, 判断是否正确->根据用户名,查询用户信息,对比密码是否正确2.查询用户信息
根据用户ID,查询用户信息->根据用户ID,去查询用户信息3.博客列表
查询所有博客->查询所有博客
4.查询作者信息
1)根据博客, 拿到作者ID
2)根据作者ID,获取作者信息->根据用户ID,去查询用户信息
5.查询博客详情
->根据博客ID, 查询博客信息
6.修改博客->根据博客ID, 修改博客信息
7.添加博客->插入博客信息
8.删除博客
1)物理删除
2)逻辑删除->根据博客ID,修改博客信息
3.5.4 建包
- controller
- service
- mapper
- model
- config
- constants(里面写枚举类和常量类都可以)
4.代码部分
4.1. 实体类(model包)
//要与数据库一致
package com.example.demo.model;
import lombok.Data;
import java.util.Date;
@Data
public class BlogInfo {
private Integer id;
private String title;
private String content;
private Integer userId;
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}
package com.example.demo.model;
import lombok.Data;
import java.util.Date;
@Data
public class UserInfo {
private Integer id;
private String userName;
private String password;
private String githubUrl;
private Byte deleteFlag;
private Date createTime;
private Date updateTime;
}
4.2 公共模块
包括实体类和同一功能处理<1.拦截器2.统一功能处理3.统一结果返回>
4.2.1. 统一返回结果实体类
a. code: 业务状态码
▪ 200: 业务处理成功
▪ -1 : 业务处理失败
▪ -2 : ⽤⼾未登录
▪ 后续有其他异常信息, 可以再补充.
b. msg: 业务处理失败时, 返回的错误信息
c. data: 业务返回数据
定义业务状态码//我们将这些确定的状态码存入Constants包中,防止修改
package com.example.demo.model;
import com.example.demo.constants.Constant;
import lombok.Data;
@Data
public class Result {
private int code;
private String msg;
private Object data;
//业务执行时返回的方法
public static Result success(Object data){
Result result=new Result();
result.setData(Constant.RESULT_CODE_SUCCESS);
result.setMsg("");
result.setData(data);
return result;
}
public static Result fail(int code,String msg){
Result result=new Result();
result.setData(Constant.RESULT_CODE_FAIL);
result.setMsg(msg);
result.setData("");
return result;
}
public static Result unlogin(int code,String msg,Object data){
Result result=new Result();
result.setData(Constant.RESULT_CODE_UNLOGIN);
result.setMsg("用户未登录");
result.setData(data);
return result;
}
}
4.2.2 统一返回结果
4.2.2.1 统一功能处理
package com.example.demo.model.result;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
public class ResponseAdvice implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
if(body instanceof Result){
return body;
}
if(body instanceof String){
ObjectMapper objectMapper=new ObjectMapper();
try {
return objectMapper.writeValueAsString(Result.success(body));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
return Result.success(body);
}
}
记得加此注解!!!
统⼀的数据返回格式使⽤ @ControllerAdvice 和 ResponseBodyAdvice 的⽅式实现
@ControllerAdvice 表⽰控制器通知类
添加类 ResponseAdvice , 实现 ResponseBodyAdvice 接⼝, 并在类上添加@ControllerAdvice 注解
4.2.2.2 统一异常处理
@ExceptionHandler 是异常处理器,两个结合表⽰当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件
package com.example.demo.model.result;
import com.example.demo.constants.Constant;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {
@ExceptionHandler
public Result exceptionAdvice(Exception e){
return Result.fail(Constant.RESULT_CODE_FAIL,e.getMessage());
}
}
4.2.2.3 拦截器
4.3 业务代码
4.3.1 持久层(mapper)
根据需求, 先⼤致计算有哪些DB相关操作, 完成持久层初步代码, 后续再根据业务需求进⾏完善
1. 用户登录页
- 根据用户名查询用户信息(select语句)
2. 博客列表⻚
- 根据id查询user信息(select语句->通过id查询用户表)
- 获取所有博客列表(select语句->通过id查询博客表)
3. 博客详情⻚
- 根据博客ID查询博客信息
- 根据博客ID删除博客(修改delete_flag=1,update)
4. 博客修改⻚
- 根据博客ID修改博客信息(update)
5. 发表博客
- 插⼊新的博客数据(insert)
1.BlogInfoMapper
package com.example.demo.mapper;
import com.example.demo.model.BlogInfo;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface BlogMapper {
//a.获取所有博客列表
@Select("select * from blog where delete_flag=0 order by create_time desc")
List<BlogInfo> selectAllBlog();
//b.根据博客id查询它的博客
@Select("select * from blog where delete_flag=0 and id=#{id}")
BlogInfo selectByIdBlog();
//c.插⼊新的博客数据(insert)
@Insert("insert into blog (title,content,user_id) values (#{title},#{content},#{userId})")
Integer insertNewBlog(BlogInfo blogInfo);
//d.博客修改页和博客删除页(update)-动态mybatis->xml
Integer updateBlog(BlogInfo blogInfo);
}
<?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.example.demo.mapper.BlogInfoMapper">
<update id="updateBlog">
update blog
<set>
<if test="title!=null">
title=#{title},
</if>
<if test="content!=null">
content=#{content},
</if>
<if test="deleteFlag !=null">
delete_flag = #{deleteFlag}
</if>
</set>
where id=#{id}
</update>
</mapper>
2.UserInfoMapper
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserInfoMapper {
//1.根据id查询用户的信息
@Select("select * from user where id = #{id}")
UserInfo selectById(Integer id);
//2.根据用户名查询用户的信息,登陆操作时用
@Select("select * from user where user_name = #{userName}")
UserInfo selectByName(String name);
}
4.3.1.1 测试持久层
package com.example.demo.mapper;
import com.example.demo.model.BlogInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class BlogInfoMapperTest {
@Autowired
private BlogInfoMapper blogInfoMapper;
@Test
void selectAllBlog() {
System.out.println(blogInfoMapper.selectAllBlog());
}
@Test
void selectByIdBlog() {
System.out.println( blogInfoMapper.selectByIdBlog(1));
}
@Test
void insertNewBlog() {
BlogInfo blogInfo=new BlogInfo();
blogInfo.setTitle("测试接口");
blogInfo.setContent("单元测试测试接⼝测试接⼝");
blogInfo.setUserId(5);
blogInfoMapper.insertNewBlog(blogInfo);
}
@Test
void updateBlog() {
BlogInfo blogInfo=new BlogInfo();
blogInfo.setId(3);
blogInfo.setTitle("测试接口ing");
blogInfo.setDeleteFlag(1);
blogInfoMapper.updateBlog(blogInfo);
}
}
测试成功!!!
测试结果
数据库也成功录入信息!!!
4.3.2 实现博客列表
4.3.2.1 约定前后端交互接口
[请求]
/blog/getlist
[响应]
{
"code": 200,
"msg": "",
"data": [{
"id": 1,
"title": "第⼀篇博客",
"content": "111我是博客正⽂我是博客正⽂我是博客正⽂",
"userId": 1,
"deleteFlag": 0,
"createTime": "2023-10-21 16:56:57",
"updateTime": "2023-10-21T08:56:57.000+00:00"
},
.....
]
}
客户端给服务器发送⼀个 /blog/getlist 这样的 HTTP 请求, 服务器给客⼾端返回了⼀个 JSON 格
式的数据.
4.3.2.2 实现服务器代码
1.service包
2.controller包
3.后端测试
失败!!!
与预想结果不符,我想要的code为200.经过检查,set错误,导致其为默认值。
修改之后重新测试
成功!!!
4.3.2.3 前后端交互->实现客户端代码
4.3.2.3.1 前端测试
成功!!!
4.3.2.3.2 相关部署(时间的返回格式确定)
此时⻚⾯的⽇期显⽰为时间戳, 我们从后端也⽇期进⾏处理
SimpleDateFormat 格式参考官⽅⽂档
我们建立一个utils包,进行相关的部署
重写获取博客创建时间
重新测试
成功!!!
4.3.3 实现博客详情
4.3.3.1 约定前后端交互接⼝
[请求]
/blog/getBlogDetail?blogId=1[ 响应 ]{"code" : 200,"msg" : "" ,"data" : {"id" : 1,"title" : " 第⼀篇博客 " ,"content" : "111 我是博客正⽂我是博客正⽂我是博客正⽂ " ,"userId" : 1,"deleteFlag" : 0,"createTime" : "2023-10-21 16:56:57" ,"updateTime" : "2023-10-21T08:56:57.000+00:00"}}
4.3.3.2 实现服务器代码
1.service包
2.controller包
3.后端测试
成功!!!
4.3.3.3 前后端交互
首先清除写死的代码
后续将获得的内容赋值到上面