简介
本项目主要参考 B站 up 主三更的视频
B站最通俗易懂手把手SpringBoot+Vue项目实战-前后端分离博客项目-Java项目 | 三更草堂 | 哔哩哔哩
工程创建
IDEA > File > New > Project > Maven ArcheType
1 2 3 4 5 6 7 8 9 10 Name: JavaProject Location: ~\Desktop\code\Java JDK: 1.8 Catalog: Internal Archetype: org.apache.maven.archetypes:maven-archetype-quickstart version: 1.0 GroupId: com.wenxiang ArtifactId: JavaProject version: 1.0 -SNAPSHOT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > com.wenxiang</groupId > <artifactId > JavaProject</artifactId > <version > 1.0-SNAPSHOT</version > <packaging > pom</packaging > <name > JavaProject</name > <url > http://maven.apache.org</url > <modules > <module > PublicFramework</module > </modules > <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <java.version > 1.8</java.version > </properties > <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-dependencies</artifactId > <version > 2.5.0</version > <type > pom</type > <scope > import</scope > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > fastjson</artifactId > <version > 1.2.33</version > </dependency > <dependency > <groupId > io.jsonwebtoken</groupId > <artifactId > jjwt</artifactId > <version > 0.9.0</version > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.4.3</version > </dependency > <dependency > <groupId > com.aliyun.oss</groupId > <artifactId > aliyun-sdk-oss</artifactId > <version > 3.10.2</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > easyexcel</artifactId > <version > 3.0.5</version > </dependency > <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-swagger2</artifactId > <version > 2.9.2</version > </dependency > <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-swagger-ui</artifactId > <version > 2.9.2</version > </dependency > </dependencies > </dependencyManagement > <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-compiler-plugin</artifactId > <version > 3.1</version > <configuration > <source > ${java.version}</source > <target > ${java.version}</target > <encoding > ${project.build.sourceEncoding}</encoding > </configuration > </plugin > </plugins > </build > </project >
多模块开发
公共模块 PublicFramework
后台管理模块 AdminFramework
前台使用模块 UserFramework
第一个子模块-公共依赖
右键项目根目录 > New > Module > Maven ArcheType
1 2 3 4 5 6 7 8 9 10 11 Name: PublicFramework Location: ~\Desktop\code\Java\JavaProject JDK: 1.8 Parent: JavaProject Catalog: Internal Archetype: org.apache.maven.archetypes:maven-archetype-quickstart version: 1.1 GroupId: com.wenxiang ArtifactId: PublicFramework version: 1.0 -SNAPSHOT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <optional > true</optional > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-security</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > fastjson</artifactId > </dependency > <dependency > <groupId > io.jsonwebtoken</groupId > <artifactId > jjwt</artifactId > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > </dependency > <dependency > <groupId > com.aliyun.oss</groupId > <artifactId > aliyun-sdk-oss</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-aop</artifactId > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > easyexcel</artifactId > </dependency > <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-swagger2</artifactId > </dependency > <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-swagger-ui</artifactId > </dependency > </dependencies >
手动下载 jar 包
以 springfox-swagger2 为例, 在 maven repo 仓库中找到对应的版本 io/springfox/springfox-swagger2/2.9.2 , 下载 .jar
, .jar.sha1
, .pom
, .pom.sha1
为后缀的四个文件, 将其放到 maven 本地仓库对应的目录下.
UserFramework > src > main > resource > application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 server: port: 7777 spring: datasource: url: jdbc:mysql://localhost:3306/blog?characterEncoding=utf-8 username: root password: "162670" driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 2MB max-request-size: 5MB mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: delFlag logic-delete-value: 1 logic-not-delete-value: 0 id-type: auto
第二个子模块-后台管理
右键项目根目录 > New > Module > Maven ArcheType
1 2 3 4 5 6 7 8 9 10 11 Name: AdminFramework Location: ~\Desktop\code\Java\JavaProject JDK: 1.8 Parent: JavaProject Catalog: Internal Archetype: org.apache.maven.archetypes:maven-archetype-quickstart version: 1.1 GroupId: com.wenxiang ArtifactId: AdminFramework version: 1.0 -SNAPSHOT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <parent > <groupId > com.wenxiang</groupId > <artifactId > JavaProject</artifactId > <version > 1.0-SNAPSHOT</version > </parent > <artifactId > AdminFramework</artifactId > <packaging > jar</packaging > <name > AdminFramework</name > <url > http://maven.apache.org</url > <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > </properties > <dependencies > <dependency > <groupId > com.wenxiang</groupId > <artifactId > PublicFramework</artifactId > <version > 1.0-SNAPSHOT</version > </dependency > </dependencies > </project >
第三个子模块-前台用户
右键项目根目录 > New > Module > Maven ArcheType
1 2 3 4 5 6 7 8 9 10 11 Name: UserFramework Location: ~\Desktop\code\Java\JavaProject JDK: 1.8 Parent: JavaProject Catalog: Internal Archetype: org.apache.maven.archetypes:maven-archetype-quickstart version: 1.1 GroupId: com.wenxiang ArtifactId: UserFramework version: 1.0 -SNAPSHOT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <parent > <groupId > com.wenxiang</groupId > <artifactId > JavaProject</artifactId > <version > 1.0-SNAPSHOT</version > </parent > <artifactId > UserFramework</artifactId > <packaging > jar</packaging > <name > UserFramework</name > <url > http://maven.apache.org</url > <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > </properties > <dependencies > <dependency > <groupId > com.wenxiang</groupId > <artifactId > PublicFramework</artifactId > <version > 1.0-SNAPSHOT</version > </dependency > </dependencies > </project >
数据库
执行数据库脚本
连接数据库
IDEA侧栏 > DataBase > New > Data Source > MySQL
填写用户名, 密码, 名字
初次使用会提示安装组件
安装 Easy Code 插件
IDEA > File > Settings > Plugins > Easycode
配置 Easy Code 模板
IDEA > File > Settings > EasyCode > Template > Group Name: MybatisPlus > entity.java.vm
修改后的模板如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ##导入宏定义 $!{define.vm} ##保存文件(宏定义) #save("/entity", ".java") ##包路径(宏定义) #setPackageSuffix("entity") ##自动导入包(全局变量) $!{autoImport.vm} import java.io.Serializable; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; ##表注释(宏定义) #tableComment("表实体类") @SuppressWarnings("serial") @AllArgsConstructor @Data @NoArgsConstructor public class $!{tableInfo.name} { #foreach($column in $tableInfo.fullColumn) #if(${column.comment})//${column.comment}#end private $!{tool.getClsNameByFullName($column.type)} $!{column.name}; #end }
点击右上角的实时调试可以来查看修改后的效果
导入数据库中的表
IDEA侧栏 > DataBase > blog > schemas > 右键article > EasyCode > Generate Code
创建实体类 Mapper 和 service
在 ==PublicFramework == > ··· > com.wenxinag 下创建 domain 包, 将整个 entity 移入 domain 下
同时在 com.wenxiang 下创建 Mapper 包 和 service 包
在 Mapper 下新建 Java Class 命名 ArticleMapper
1 2 3 4 5 6 7 8 package com.wenxiang.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.wenxiang.domain.entity.Article;public interface ArticleMapper extends BaseMapper <Article> { }
在 service 下新建 Java Class 命名 ArticleService
1 2 3 4 5 6 7 8 package com.wenxiang.sevice;import com.baomidou.mybatisplus.extension.service.IService;import com.wenxiang.domain.entity.Article;public interface ArticleService extends IService <Article> { }
在 service 下新建 Java Class 命名 impl.ArticleServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 package com.wenxiang.sevice.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.wenxiang.domain.entity.Article;import com.wenxiang.mapper.ArticleMapper;import com.wenxiang.sevice.ArticleService;import org.springframework.stereotype.Service;@Service public class ArticleServiceImpl extends ServiceImpl <ArticleMapper, Article> implements ArticleService { }
创建 controller
在 ==UserFramework == > ··· > com.wenxinag 下创建 controller 包
在 controller 包下新建 Java Class, 命名 ArticleController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.wenxiang.controller;import com.wenxiang.domain.entity.Article;import com.wenxiang.sevice.ArticleService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController @RequestMapping("/article") public class ArticleController { @Autowired private ArticleService articleService; @GetMapping("/list") public List<Article> test () { return articleService.list(); } }
此时切到 UserFramework 下的 BlogApplication, 点击 Run, 报错如下
1 Error creating bean with name 'articleController' : Unsatisfied dependency expressed through field 'articleService' ; ...
这是由于 UserFramework 对 PublicFramework 模块的依赖还停留在老版本, 于是 PublicFramework 下新建的新建的类就没有出现在 UserFramework 的依赖中
处理方法是在 Maven 中, 选择父工程, JavaProject > Lifecycle > 双击install, 重新打包依赖
第二次运行 BlogApplication.java 报错, 此时需要添加
1 2 3 4 5 ... @MapperScan("com.wenxiang.mapper") public class BlogApplication { ... }
此时运行成功.
在浏览器地址栏中输入 localhost:7777/article/list
, 并没有出现 article, 而是加载出登录页面, 这是由于项目依赖了 spring-boot-starter-security 造成的.
当前为了方便测试, 暂时将 PublicFramework 下 pom.xml 中关于 spring-boot-starter-security 的依赖注释掉.
注释之后需要先更新 Maven, 然后由于修改了 PublicFramework 所以需要再次 Maven > JavaProject (root) > Lifecycle > 双击install, 重新打包
打包完成后重启项目, 浏览器访问 localhost:7777/article/list
, 再次报错
报错状态为 500, Console 中给出的报错信息为 Access denied for user 'root'@'localhost' (using password: YES)
, 排查为 UserFramework > src > main > resource > application.yml 中关于数据库的配置有问题, 修改 password 字段
1 2 3 4 5 6 7 8 9 server: port: 7777 spring: datasource: url: jdbc:mysql://localhost:3306/blog?characterEncoding=utf-8 username: root password: "162670" driver-class-name: com.mysql.cj.jdbc.Driver
重新运行, 刷新浏览器页面, 正常显示出 article 中的内容.
课程中出现的一个报错: Table 'sg_blog.article' doesn't exist
这是由于课程中示例的数据库 sg_blog
中的表单名称为 sg_article
, 但是对应的实体类名称为 Article
, 再没有额外指明的情况下, Java 默认认为数据的名称和实体类的名称一致, 所以去 sg_blog
数据库下查询 article
表单而没有找到, 所以返回上述信息.
解决办法: 在 PublicFramework/src/main/java/com/wenxiang/domain/entity/Article.java 实体类中添加一个注解, 让其和数据库表单的名称对应起来, 如下
1 2 3 4 5 6 7 8 ... @TableName("数据库对应的表单名称") public class Article { @TableId private Long id; ... }
而我在实际操作的时候, 数据库表单名称 article
(没有 sg_
前缀)和实体类名称 Article
是一致的, 所以没有报这个错误.
热门文章列表
Note: 在项目目录上栏右侧的 Options > Tree Appearance > 取消勾选 Compact Middle Packges 可以展开连在一起的 Package
准备工作
右键 PublicFramework/src/main/java/com/wenxiang/domain 目录, 粘贴下面的代码, IDEA 将直接创建一个 Java Class 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 package com.wenxiang.domain;import com.fasterxml.jackson.annotation.JsonInclude;import com.wenxiang.enums.AppHttpCodeEnum;import java.io.Serializable;@JsonInclude(JsonInclude.Include.NON_NULL) public class ResponseResult <T> implements Serializable { private Integer code; private String msg; private T data; public ResponseResult () { this .code = AppHttpCodeEnum.SUCCESS.getCode(); this .msg = AppHttpCodeEnum.SUCCESS.getMsg(); } public ResponseResult (Integer code, T data) { this .code = code; this .data = data; } public ResponseResult (Integer code, String msg, T data) { this .code = code; this .msg = msg; this .data = data; } public ResponseResult (Integer code, String msg) { this .code = code; this .msg = msg; } public static ResponseResult errorResult (int code, String msg) { ResponseResult result = new ResponseResult (); return result.error(code, msg); } public static ResponseResult okResult () { ResponseResult result = new ResponseResult (); return result; } public static ResponseResult okResult (int code, String msg) { ResponseResult result = new ResponseResult (); return result.ok(code, null , msg); } public static ResponseResult okResult (Object data) { ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS, AppHttpCodeEnum.SUCCESS.getMsg()); if (data!=null ) { result.setData(data); } return result; } public static ResponseResult errorResult (AppHttpCodeEnum enums) { return setAppHttpCodeEnum(enums,enums.getMsg()); } public static ResponseResult errorResult (AppHttpCodeEnum enums, String msg) { return setAppHttpCodeEnum(enums,msg); } public static ResponseResult setAppHttpCodeEnum (AppHttpCodeEnum enums) { return okResult(enums.getCode(),enums.getMsg()); } private static ResponseResult setAppHttpCodeEnum (AppHttpCodeEnum enums, String msg) { return okResult(enums.getCode(),msg); } public ResponseResult<?> error(Integer code, String msg) { this .code = code; this .msg = msg; return this ; } public ResponseResult<?> ok(Integer code, T data) { this .code = code; this .data = data; return this ; } public ResponseResult<?> ok(Integer code, T data, String msg) { this .code = code; this .data = data; this .msg = msg; return this ; } public ResponseResult<?> ok(T data) { this .data = data; return this ; } public Integer getCode () { return code; } public void setCode (Integer code) { this .code = code; } public String getMsg () { return msg; } public void setMsg (String msg) { this .msg = msg; } public T getData () { return data; } public void setData (T data) { this .data = data; } }
在 PublicFramework/src/main/java/com/wenxiang 目录下创建 Package 命名为 enums, 右键 enums 粘贴下面的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.wenxiang.enums;public enum AppHttpCodeEnum { SUCCESS(200 ,"操作成功" ), NEED_LOGIN(401 ,"需要登录后操作" ), NO_OPERATOR_AUTH(403 ,"无权限操作" ), SYSTEM_ERROR(500 ,"出现错误" ), USERNAME_EXIST(501 ,"用户名已存在" ), PHONENUMBER_EXIST(502 ,"手机号已存在" ), EMAIL_EXIST(503 , "邮箱已存在" ), REQUIRE_USERNAME(504 , "必需填写用户名" ), LOGIN_ERROR(505 ,"用户名或密码错误" ); int code; String msg; AppHttpCodeEnum(int code, String errorMessage){ this .code = code; this .msg = errorMessage; } public int getCode () { return code; } public String getMsg () { return msg; } }
代码实现
UserFramework/src/main/java/com/wenxiang/controller/ArticleController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.wenxiang.controller;import com.wenxiang.domain.ResponseResult;import com.wenxiang.domain.entity.Article;import com.wenxiang.sevice.ArticleService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController @RequestMapping("/article") public class ArticleController { @Autowired private ArticleService articleService; @GetMapping("/hotArticleList") public ResponseResult hotArticleList () { ResponseResult result = articleService.hotArticleList(); return result; } }
PublicFramework/src/main/java/com/wenxiang/sevice/ArticleService.java
1 2 3 4 5 6 7 8 9 10 package com.wenxiang.sevice;import com.baomidou.mybatisplus.extension.service.IService;import com.wenxiang.domain.ResponseResult;import com.wenxiang.domain.entity.Article;public interface ArticleService extends IService <Article> { ResponseResult hotArticleList () ; }
PublicFramework/com/wenxiang/sevice/impl/ArticleServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.wenxiang.sevice.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.wenxiang.domain.ResponseResult;import com.wenxiang.domain.entity.Article;import com.wenxiang.mapper.ArticleMapper;import com.wenxiang.sevice.ArticleService;import org.springframework.stereotype.Service;import java.util.List;@Service public class ArticleServiceImpl extends ServiceImpl <ArticleMapper, Article> implements ArticleService { @Override public ResponseResult hotArticleList () { LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(Article::getStatus, 0 ); queryWrapper.orderByDesc(Article::getViewCount); Page<Article> page = new Page (1 , 10 ); page(page, queryWrapper); List<Article> articles = page.getRecords(); return ResponseResult.okResult(articles); } }
Postman 测试
Run BlogApplication.java, 正常运行
在 Postman Desktop 中使用 GET 访问 http://localhost:7777/article/hotArticleList
Note: 使用 Postman Desktop 版本进行测试, 使用网页版不能正确得到 Response, 给出下面提示:
Could not send request
Cloud Agent Error: Can not send requests to localhost. Select a different agent. | Use Postman's Desktop Agent
前端工程联调
安装依赖和启动
切到前端工程 blog-vue 目录下, 调出 cmd
1 2 3 4 5 # install dependencies npm install # serve with hot reload at localhost: 8080 npm run dev
直接只用 npm install 报错, 请按照下面步骤使用 cnpm 安装依赖
清除缓存
删除项目中的 node_modules 文件夹
安装 cnpm
1 npm install -g cnpm --registry=https:
用 cnpm 安装依赖
测试联调
解决跨域访问问题
PublicFramework/src/main/java/com/wenxiang/ 下新建 config 包, 右键粘贴下面代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.wenxiang.config;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.CorsRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings (CorsRegistry registry) { registry.addMapping("/**" ) .allowedOriginPatterns("*" ) .allowCredentials(true ) .allowedMethods("GET" , "POST" , "DELETE" , "PUT" ) .allowedHeaders("*" ) .maxAge(3600 ); } }
Maven 下重新 install 整个项目, 重启项目. 此时刷新前端页面, 可以看到 “热门文章” 栏.
可以 F12 (或者鼠标右键>检查) 调出开发者工具, 切换到 Network, 点击页面地址栏左边的刷新, 筛选选择 Fetch/XHR, 可以看到 hotArticleList, 点击 Preview 可以看到返回的信息.
VO 优化
VO 即 View Object
在 PublicFramework/src/main/java/com/wenxiang/domain 目录下新建 Package 命名为 vo, 在 vo 下新建 Java Class 命名为 HotArticleVo
PublicFramework/src/main/java/com/wenxiang/domain/vo/HotArticleVo.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.wenxiang.domain.vo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data @NoArgsConstructor @AllArgsConstructor public class HotArticleVo { private Long id; private String title; private String viewCount; }
PublicFramework/src/main/java/com/wenxiang/sevice/impl/ArticleServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.wenxiang.sevice.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.fasterxml.jackson.databind.util.BeanUtil;import com.wenxiang.domain.ResponseResult;import com.wenxiang.domain.entity.Article;import com.wenxiang.domain.vo.HotArticleVo;import com.wenxiang.mapper.ArticleMapper;import com.wenxiang.sevice.ArticleService;import org.springframework.beans.BeanUtils;import org.springframework.stereotype.Service;import java.util.ArrayList;import java.util.List;@Service public class ArticleServiceImpl extends ServiceImpl <ArticleMapper, Article> implements ArticleService { @Override public ResponseResult hotArticleList () { LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(Article::getStatus, 0 ); queryWrapper.orderByDesc(Article::getViewCount); Page<Article> page = new Page (1 , 10 ); page(page, queryWrapper); List<Article> articles = page.getRecords(); List<HotArticleVo> articleVos = new ArrayList <>(); for (Article article : articles) { HotArticleVo vo = new HotArticleVo (); BeanUtils.copyProperties(article, vo); articleVos.add(vo); } return ResponseResult.okResult(articleVos); } }
字面量处理
PublicFramework/src/main/java/com/wenxiang/constants/SystemConstants.java
1 2 3 4 5 6 7 8 9 10 11 12 public class SystemConstants { public static final int ARTICLE_STATUS_DRAFT = 1 ; public static final int ARTICLE_STATUS_NORMAL = 0 ; }
将 PublicFramework/src/main/java/com/wenxiang/sevice/impl/ArticleServiceImpl.java 中的 0 修改为 SystemConstants.ARTICLE_STATUS_NORMAL
1 2 3 4 5 6 7 8 ... @Override public ResponseResult hotArticleList () { ... queryWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL); ... } ...
Bean 拷贝工具类封装
PublicFramework\src\main\java\com\wenxiang\utils\BeanCopyUtils.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.wenxiang.utils;import com.wenxiang.constants.SystemConstants;import com.wenxiang.domain.entity.Article;import com.wenxiang.domain.vo.HotArticleVo;import org.springframework.beans.BeanUtils;import java.sql.SQLOutput;import java.util.List;import java.util.stream.Collectors;public class BeanCopyUtils { private BeanCopyUtils () { } public static <V> V copyBean (Object source, Class<V> clazz) { V result = null ; try { result = clazz.newInstance(); BeanUtils.copyProperties(source, result); } catch (Exception e) { e.printStackTrace(); } return result; } public static <O, V> List<V> copyBeanList (List<O> list, Class<V> clazz) { return list.stream() .map(o -> copyBean(o, clazz)) .collect(Collectors.toList()); } public static void main (String[] args) { Article article = new Article (); article.setId(1L ); article.setTitle("ss" ); HotArticleVo hotArticleVo = copyBean(article, HotArticleVo.class); System.out.println(hotArticleVo); } }
PublicFramework\src\main\java\com\wenxiang\sevice\impl\ArticleServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.wenxiang.sevice.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.fasterxml.jackson.databind.util.BeanUtil;import com.wenxiang.constants.SystemConstants;import com.wenxiang.domain.ResponseResult;import com.wenxiang.domain.entity.Article;import com.wenxiang.domain.vo.HotArticleVo;import com.wenxiang.mapper.ArticleMapper;import com.wenxiang.sevice.ArticleService;import com.wenxiang.utils.BeanCopyUtils;import org.springframework.beans.BeanUtils;import org.springframework.stereotype.Service;import java.util.ArrayList;import java.util.List;@Service public class ArticleServiceImpl extends ServiceImpl <ArticleMapper, Article> implements ArticleService { @Override public ResponseResult hotArticleList () { LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL); queryWrapper.orderByDesc(Article::getViewCount); Page<Article> page = new Page (1 , 10 ); page(page, queryWrapper); List<Article> articles = page.getRecords(); List<HotArticleVo> vs = BeanCopyUtils.copyBeanList(articles, HotArticleVo.class); return ResponseResult.okResult(vs); } }
分类列表查询
EasyCode 代码模板
IDEA > File > Settings > EasyCode > Template > Group Name: MybatisPlus > entity.java.vm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 ##导入宏定义 $!{define.vm} ##保存文件(宏定义) #save("/entity", ".java") ##包路径(宏定义) #setPackageSuffix("entity") ##自动导入包(全局变量) $!{autoImport.vm} import java.io.Serializable; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; ##表注释(宏定义) #tableComment("表实体类") @SuppressWarnings("serial") @Data @AllArgsConstructor @NoArgsConstructor @TableName("$!{tableInfo.obj.name}") public class $!{tableInfo.name} { #foreach($column in $tableInfo.pkColumn) #if(${column.comment})//${column.comment}#end @TableId private $!{tool.getClsNameByFullName($column.type)} $!{column.name}; #end #foreach($column in $tableInfo.otherColumn) #if(${column.comment})//${column.comment}#end private $!{tool.getClsNameByFullName($column.type)} $!{column.name}; #end }
IDEA > File > Settings > EasyCode > Template > Group Name: MybatisPlus > dao.java.vm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ##导入宏定义 $!{define.vm} ##设置表后缀(宏定义) #setTableSuffix("Mapper") ##保存文件(宏定义) #save("/mapper", "Mapper.java") ##包路径(宏定义) #setPackageSuffix("mapper") import com.baomidou.mybatisplus.core.mapper.BaseMapper; ##表注释(宏定义) #tableComment("表数据库访问层") public interface $!{tableName} extends BaseMapper<$!tableInfo.name> { }
IDEA > File > Settings > EasyCode > Template > Group Name: MybatisPlus > service.java.vm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ##导入宏定义 $!{define.vm} ##设置表后缀(宏定义) #setTableSuffix("Service") ##保存文件(宏定义) #save("/service", "Service.java") ##包路径(宏定义) #setPackageSuffix("service") import com.baomidou.mybatisplus.extension.service.IService; ##表注释(宏定义) #tableComment("表服务接口") public interface $!{tableName} extends IService<$!tableInfo.name> { }
IDEA > File > Settings > EasyCode > Template > Group Name: MybatisPlus > serviceImpl.java.vm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ##导入宏定义 $!{define.vm} ##设置表后缀(宏定义) #setTableSuffix("ServiceImpl") ##保存文件(宏定义) #save("/service/impl", "ServiceImpl.java") ##包路径(宏定义) #setPackageSuffix("service.impl") import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; ##表注释(宏定义) #tableComment("表服务实现类") @Service("$!tool.firstLowerCase($tableInfo.name)Service") public class $!{tableName} extends ServiceImpl<$!{tableInfo.name}Mapper, $!{tableInfo.name}> implements $!{tableInfo.name}Service { }
IDEA侧栏 > DataBase > blog > schemas > 右键category > EasyCode > Generate Code
这个是直接在 java 目录下创建的, 要将 mapper, entity, service 和 serviceImpl 分别移入到各自的目录下
分类列表代码实现
UserFramework\src\main\java\com\wenxiang\controller\CategoryController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.wenxiang.controller;import com.wenxiang.domain.ResponseResult;import com.wenxiang.sevice.CategoryService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController @RequestMapping("/category") public class CategoryController { @Autowired private CategoryService categoryService; @GetMapping("/getCategoryList") public ResponseResult getCategoryList () { return categoryService.getCategoryList(); } }
PublicFramework\src\main\java\com\wenxiang\sevice\CategoryService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.wenxiang.sevice;import com.baomidou.mybatisplus.extension.service.IService;import com.wenxiang.domain.ResponseResult;import com.wenxiang.domain.entity.Category;public interface CategoryService extends IService <Category> { ResponseResult getCategoryList () ; }
PublicFramework\src\main\java\com\wenxiang\sevice\impl\CategoryServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 package com.wenxiang.sevice.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.wenxiang.constants.SystemConstants;import com.wenxiang.domain.ResponseResult;import com.wenxiang.domain.entity.Article;import com.wenxiang.domain.entity.Category;import com.wenxiang.domain.vo.CategoryVo;import com.wenxiang.mapper.CategoryMapper;import com.wenxiang.sevice.ArticleService;import com.wenxiang.sevice.CategoryService;import com.wenxiang.utils.BeanCopyUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;import java.util.Set;import java.util.function.Function;import java.util.stream.Collectors;import java.util.stream.Stream;@Service("categoryService") public class CategoryServiceImpl extends ServiceImpl <CategoryMapper, Category> implements CategoryService { @Autowired private ArticleService articleService; @Override public ResponseResult getCategoryList () { LambdaQueryWrapper<Article> articleWrapper = new LambdaQueryWrapper <>(); articleWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL); List<Article> articleList = articleService.list(articleWrapper); Set<Long> categoryIds = articleList.stream() .map(article -> article.getCategoryId()) .collect(Collectors.toSet()); List<Category> categories = listByIds(categoryIds); categories = categories.stream() .filter(category -> SystemConstants.STATUS_NORMAL.equals(category.getStatus())) .collect(Collectors.toList()); List<CategoryVo> categoryVos = BeanCopyUtils.copyBeanList(categories, CategoryVo.class); return ResponseResult.okResult(categoryVos); } }
PublicFramework\src\main\java\com\wenxiang\domain\vo\CategoryVo.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.wenxiang.domain.vo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data @NoArgsConstructor @AllArgsConstructor public class CategoryVo { private Long id; private String name; }
PublicFramework\src\main\java\com\wenxiang\constants\SystemConstants.java 中添加常量
1 public static final String STATUS_NORMAL = "0" ;
查询文章列表
查询文章列表代码实现
在 UserFramework\src\main\java\com\wenxiang\controller\ArticleController.java
中添加 articleList 方法
1 2 3 4 5 6 7 8 9 10 11 12 ... @RestController @RequestMapping("/article") public class ArticleController { ... @GetMapping("/articleList") public ResponseResult articleList (Integer pageNum, Integer pageSize, Long categoryId) { return articleService.articleList(pageNum, pageSize, categoryId); } }
articleList 的具体实现是在 articleService 中实现的
Note: 光标放到标红的 articleList 上 Alt+Enter,
Create method 'articleList' in 'ArticleService'
+ Enter
可以快速在 ArticleService 中创建该方法
PublicFramework\src\main\java\com\wenxiang\sevice\ArticleService.java
1 2 3 4 5 6 7 ... public interface ArticleService extends IService <Article> { ... ResponseResult articleList (Integer pageNum, Integer pageSize, Long categoryId) ; }
Note: Alt+Ctrl+鼠标左键点击 ArticleService 可以快速跳转到实现类 ArticleServiceImpl
PublicFramework\src\main\java\com\wenxiang\sevice\impl\ArticleServiceImpl.java
Note: 光标放到标红色波浪线的地方 Alt+Enter > Implement methods + Enter > articleList(…) + Enter 可以快速在 ArticleServiceImpl 中创建 articleList() 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 ... @Service public class ArticleServiceImpl extends ServiceImpl <ArticleMapper, Article> implements ArticleService { ... @Override public ResponseResult articleList (Integer pageNum, Integer pageSize, Long categoryId) { LambdaQueryWrapper<Article> lambdaQueryWrapper = new LambdaQueryWrapper <>(); lambdaQueryWrapper.eq(Objects.nonNull(categoryId)&&categoryId>0 , Article::getCategoryId, categoryId); lambdaQueryWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL); lambdaQueryWrapper.orderByDesc(Article::getIsTop); Page<Article> page = new Page <>(pageNum, pageSize); page(page, lambdaQueryWrapper); List<ArticleListVo> articleListVos = BeanCopyUtils.copyBeanList(page.getRecords(), ArticleListVo.class); PageVo pageVo = new PageVo (articleListVos, page.getTotal()); return ResponseResult.okResult(pageVo); } }
PublicFramework\src\main\java\com\wenxiang\domain\vo\ArticleListVo.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.wenxiang.domain.vo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.util.Date;@Data @NoArgsConstructor @AllArgsConstructor public class ArticleListVo { private Long id; private String title; private String content; private String summary; private Long categoryName; private String thumbnail; private Long viewCount; private Date createTime; }
PublicFramework\src\main\java\com\wenxiang\domain\vo\PageVo.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.wenxiang.domain.vo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.util.List;@Data @NoArgsConstructor @AllArgsConstructor public class PageVo { private List rows; private Long total; }
http://localhost:7777/article/articleList
报错, 返回状态码 500
1 2 3 4 5 6 { "timestamp" : "2023-05-29T03:39:38.178+00:00" , "status" : 500 , "error" : "Internal Server Error" , "path" : "/article/articleList" }
传入参数
MybatisPlus 分页支持配置
PublicFramework\src\main\java\com\wenxiang\config\MbatisPlusConfig.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Configuration public class MbatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor (); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor ()); return mybatisPlusInterceptor; } }
配置完成后重新 install, Postman 中再次检测, 分页正常
分类名称问题解决-普通循环方式
添加 categoryName 字段, 并且要加上注释
PublicFramework\src\main\java\com\wenxiang\domain\entity\Article.java
1 2 3 4 5 6 7 ... public class Article { ... @TableField(exist = false) private String categoryName; ... }
主体代码
PublicFramework\src\main\java\com\wenxiang\sevice\impl\ArticleServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ... @Service public class ArticleServiceImpl extends ServiceImpl <ArticleMapper, Article> implements ArticleService { ... @Override public ResponseResult articleList (...) { ... ... List<Article> articles = page.getRecords(); for (Article article : articles) { Category category = categoryService.getById(article.getCategoryId()); article.setCategoryName(category.getName()); } ... } }
测试没有正常显示 categoryName 字段, 修改 categoryName 的属性为 String 类型
PublicFramework\src\main\java\com\wenxiang\domain\vo\ArticleListVo.java
1 2 3 4 5 6 7 8 ... public class ArticleListVo { ... private String categoryName; ... }
再次测试, 成功
http://localhost:7777/article/articleList?pageNum=1&pageSize=10&categoryId=1
分类名称问题解决-Stream流
PublicFramework\src\main\java\com\wenxiang\sevice\impl\ArticleServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ... @Service public class ArticleServiceImpl extends ServiceImpl <ArticleMapper, Article> implements ArticleService { ... @Override public ResponseResult articleList (Integer pageNum, Integer pageSize, Long categoryId) { ... List<Article> articles = page.getRecords(); articles.stream() .map(article -> article.setCategoryName(categoryService.getById(article.getCategoryId()).getName())) .collect(Collectors.toList()); ... } }