1.准备工作
1.1数据库
-- 建表SQL
create database if not exists java_blog_spring charset utf8mb4;use java_blog_spring;
-- 用户表
DROP TABLE IF EXISTS java_blog_spring.user_info;
CREATE TABLE java_blog_spring.user_info(`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() ON UPDATE 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_info;
CREATE TABLE java_blog_spring.blog_info (`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() ON UPDATE now(),PRIMARY KEY (id))ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '博客表';-- 新增用户信息
insert into java_blog_spring.user_info (user_name, password,github_url)values("zhangsan","123456","https://gitee.com/bubble-fish666/class-java45");
insert into java_blog_spring.user_info (user_name, password,github_url)values("lisi","123456","https://gitee.com/bubble-fish666/class-java45");insert into java_blog_spring.blog_info (title,content,user_id) values("第一篇博客","111我是博客正文我是博客正文我是博客正文",1);
insert into java_blog_spring.blog_info (title,content,user_id) values("第二篇博客","222我是博客正文我是博客正文我是博客正文",2);
1.2pom文件配置(MybatisPlus依赖)
MybatisPlus依赖:
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.5</version>
</dependency>
1.3yml文件配置(数据库配置)
记得更改password
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/java_blog_spring?characterEncoding=utf8&useSSL=falseusername: rootpassword: '123456'driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:configuration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:file:name: spring-blog.log
1.4项目架构初始化
前期的架构:
开发中, 以下命名统称为实体类:
POJO
Model
Entity
在实际开发中, 实体类的划分要细的多:
VO(value object): 表现层对象
DO(Data Object)/PO(Persistant Object): 持久层/数据层对象
DTO(Data Transfer Object): 数据传输对象
BO(Business Object): 业务对象
细分实体类, 可以实现业务代码的解耦.
详情见:实体类
2.业务实现
2.1获取博客列表
1.第一步:实现实体类 对接数据库数据
//do层数据库资源对象
@Data
@AllArgsConstructor
public class BlogInfo {@TableId(type = IdType.AUTO)private Integer id;private String title;private String content;private Integer userId;private Integer deleteFlag;private LocalDateTime createTime;private LocalDateTime updateTime;
}
@Data
public class UserInfo {@TableId(type = IdType.AUTO)private Integer id;private String userName;private String password;private String githubUrl;private Integer deleteFlag;private LocalDateTime createTime;private LocalDateTime updateTime;
}
2.实现返回对象类 对接前端
@Data
public class BlogInfoResponse {@TableId(type = IdType.AUTO)private Integer id;private String title;private String content;// @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime createTime;//重写构造方法public String getCreateTime() {return DateUtils.format(createTime);}//通过重写0content构造方法完成业务想要的返回结果 如:对字符串进行拼接等等}
BlogInfoResponse 是返回前端展示博客列表所需的数据
注意:
我们需要掌握更改日期格式的方法:
常见的日期类型有:Date LocalDateTime
在这里我们用的是LocalDateTime
那我们就先讲它的转化方法
LocalDateTime localDateTime = LocalDateTime.now();DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");String format1 = dateTimeFormatter.format(localDateTime);System.out.println(format1);
使用 DateTimeFormatter 类进行格式定义
类型格式的定义是什么呢?
查询java.text.SimpleDateFormat 官⽅⽂档可知
-
Letter Date or Time Component Presentation Examples G
Era designator Text AD
y
Year Year 1996
;96
Y
Week year Year 2009
;09
M
Month in year (context sensitive) Month July
;Jul
;07
L
Month in year (standalone form) Month July
;Jul
;07
w
Week in year Number 27
W
Week in month Number 2
D
Day in year Number 189
d
Day in month Number 10
F
Day of week in month Number 2
E
Day name in week Text Tuesday
;Tue
u
Day number of week (1 = Monday, ..., 7 = Sunday) Number 1
a
Am/pm marker Text PM
H
Hour in day (0-23) Number 0
k
Hour in day (1-24) Number 24
K
Hour in am/pm (0-11) Number 0
h
Hour in am/pm (1-12) Number 12
m
Minute in hour Number 30
s
Second in minute Number 55
S
Millisecond Number 978
z
Time zone General time zone Pacific Standard Time
;PST
;GMT-08:00
Z
Time zone RFC 822 time zone -0800
X
Time zone ISO 8601 time zone -08
;-0800
;-08:00
-
Date的转化方法:
Date date = new Date();//SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");String format = simpleDateFormat.format(date);System.out.println(format);
所以我们通过重写构造方法完成日期格式修改
最后我们通过AOP思想完成日期统一修改:
public class DateUtils {public static String format(LocalDateTime localDateTime) {DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(Constant.PATTERN);return dateTimeFormatter.format(localDateTime);}public static String format(LocalDateTime localDateTime, String pattern) {DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);return dateTimeFormatter.format(localDateTime);}
}
/*** 统一返回结果* @param <T>*/
@Data
public class Result<T> {private int code;private String errMsg;private T data;public static <T> Result success(T data) {Result<T> re = new Result<>();re.setCode(ResultCodeEnum.SUCCESS.getCode());re.setData(data);return re;}public static <T> Result fail(String errMsg) {Result<T> re = new Result<>();re.setCode(ResultCodeEnum.FAIL.getCode());re.setErrMsg(errMsg);return re;}public static <T> Result fail(String errMsg, T data) {Result<T> re = new Result<>();re.setCode(ResultCodeEnum.FAIL.getCode());re.setErrMsg(errMsg);re.setData(data);return re;}
}
Result:统一返回结果
注意:在静态方法中使用T泛型时要在static后加<T>
在这一步我们在使用code时可以选择:
1.在Constant中定义静态常亮
2.定义枚举
我选择使用枚举:
@AllArgsConstructor
public enum ResultCodeEnum {SUCCESS(200),FAIL(-1);@Getterprivate int code;}
3.Controller Service Mapper 三件套
Controller:在注入BlogService 时推荐使用@Resource 因为可能使用实现了多个对象的接口
同时注入的类型为接口类型,方便修改注入(只用修改@Resource name属性)
@Slf4j
@RestController
@RequestMapping("/blog")
public class BlogInfoController {@Resource(name = "blogService")private BlogService blogService;@RequestMapping("/getList")public List<BlogInfoResponse> getList() {log.info("获取博客列表...");return blogService.getList();}
}
Service :先定义接口,然后在impl包中实现具体类并处理逻辑
@Service
public interface BlogService {List<BlogInfoResponse> getList();
}
@Service("blogService")
public class BlogServiceImpl implements BlogService {@Resourceprivate BlogInfoMapper blogInfoMapper;@Overridepublic List<BlogInfoResponse> getList() {//1.SQL拼接QueryWrapper<BlogInfo> queryWrapper = new QueryWrapper<>();queryWrapper.lambda().eq(BlogInfo::getDeleteFlag, Constant.IS_DELETE);List<BlogInfo> blogInfos = blogInfoMapper.selectList(queryWrapper);//2.将获取的数据转化为BlogInfoResponse用于返回List<BlogInfoResponse> list = blogInfos.stream().map(blogInfo -> {BlogInfoResponse blogInfoResponse = new BlogInfoResponse();//根据属性名称进行复制 原数据 转化数据BeanUtils.copyProperties(blogInfo, blogInfoResponse);return blogInfoResponse;}).collect(Collectors.toList());//转化listreturn list;}}
注意点:
1.在 @Service中进行名称绑定
2.参数转换:
使用blogInfos.stream().map()与lambda表达式实现遍历 (或者使用for循环)
代码中的链式调用解释如下:
blogInfos.stream(): 将 List 转换为一个 stream
blogInfos.stream().map(x -> {转换规则, return y}): 对 List 中的每一个元素根据指定规则进行转换(x: 原来的元素; y: 转换后的元素)
.collect(Collectors.toList()): 将转换后的 Stream 转换为一个新的 List
3.根据属性名称进行复制 原数据 转化数据
BeanUtils.copyProperties(blogInfo, blogInfoResponse);
Mapper:因为使用了MybatisPlus,不用写SQL语句(舒服)
只用实现BaseMapper<T>接口 T为对接数据库的类,会根据该类生成SQL语句
@Mapper
public interface BlogInfoMapper extends BaseMapper<BlogInfo> {
}
使用postman校验一下:
2.2获取博客详情
前置工作已经做完了,直接写Controller Service代码就行
Controller :
@RequestMapping("/getBlogDetail")public BlogDetailResponse getBlogDetail(@NotNull(message = "blogId 不能为空") Integer blogId) {
// if(blogId == null) {
// throw new BlogException("用户id不能为空");
// }log.info("获取博客详情,id:{}",blogId);return blogService.getBlogDetail(blogId);}
@NotNull(message = "blogId 不能为空"):
jakarta.validation完成的参数校验(javax.validation 是Java Bean Validation API的包名)
如果参数为空了还要爆出异常,这时候启用我们定义的异常与统一异常处理
@Slf4j
@ResponseBody
@ControllerAdvice
public class ExceptionAdvice {@ExceptionHandlerpublic Result exceptionAdvice(Exception e) {log.error("出现异常e:", e);return Result.fail(e.getMessage());}@ExceptionHandlerpublic Result exceptionAdvice(BlogException e) {log.error("出现异常e:", e);return Result.fail(e.getErrMsg());}@ExceptionHandlerpublic Result exceptionAdvice(HandlerMethodValidationException e) {log.error("出现异常e:", e);//获取异常信息
// List<String> errors = new ArrayList<>();
// e.getAllErrors().forEach(error -> errors.add(e.getMessage()));return Result.fail("参数校验失败");}
}
在统一异常处理(@ResponseBody@ControllerAdvice@ExceptionHandler)中可以处理自定义异常与常规异常 同时打印日志
Service:
@Overridepublic BlogDetailResponse getBlogDetail(Integer blogId) {QueryWrapper<BlogInfo> queryWrapper = new QueryWrapper<>();//根据id与DeleteFlag查询queryWrapper.lambda().eq(BlogInfo::getId, blogId).eq(BlogInfo::getDeleteFlag, Constant.IS_DELETE);BlogInfo blogInfo = blogInfoMapper.selectOne(queryWrapper);BlogDetailResponse blogDetailResponse = new BlogDetailResponse();BeanUtils.copyProperties(blogInfo, blogDetailResponse);return blogDetailResponse;}
老套路查询后转化类型
postman检验一下:
没传参,返回我们定义的返回信息
正常传参: