导航
[react] Hooks
[React 从零实践01-后台] 代码分割
[React 从零实践02-后台] 权限控制
[React 从零实践03-后台] 自定义hooks
[React 从零实践04-后台] docker-compose 部署react+egg+nginx+mysql
[React 从零实践05-后台] Gitlab-CI使用Docker自动化部署
[源码-webpack01-前置知识] AST抽象语法树
[源码-webpack02-前置知识] Tapable
[源码-webpack03] 手写webpack - compiler简单编译流程
[源码] Redux React-Redux01
[源码] axios
[源码] vuex
[源码-vue01] data响应式 和 初始化渲染
[源码-vue02] computed 响应式 - 初始化,访问,更新过程
[源码-vue03] watch 侦听属性 - 初始化和更新
[源码-vue04] Vue.set 和 vm.$set
[源码-vue05] Vue.extend
[源码-vue06] Vue.nextTick 和 vm.$nextTick
[部署01] Nginx
[部署02] Docker 部署vue项目
[部署03] gitlab-CI
[数据结构和算法01] 二分查找和排序
[深入01] 执行上下文
[深入02] 原型链
[深入03] 继承
[深入04] 事件循环
[深入05] 柯里化 偏函数 函数记忆
[深入06] 隐式转换 和 运算符
[深入07] 浏览器缓存机制(http缓存机制)
[深入08] 前端安全
[深入09] 深浅拷贝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模块化
[深入13] 观察者模式 发布订阅模式 双向数据绑定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手写Promise
[深入20] 手写函数
[深入21] 数据结构和算法 - 二分查找和排序
[深入22] js和v8垃圾回收机制
[深入23] JS设计模式 - 代理,策略,单例
[前端学java01-SpringBoot实战] 环境配置和HelloWorld服务
[前端学java02-SpringBoot实战] mybatis + mysql 实现歌曲增删改查
(一) 前置知识
(1) 一些单词
pet 宠物
configuration 配置
assistant 助理,售货员
resources 资源,系统资源文件夹
amend 修正,修订
authorize 授权v
authorization 授权n
bio 个人描述
refactor 重构
extract 提取 // extract common part from 'if' 从if语句中提取出相同的部分
mapper 映射器,变换器
persistent 持久的
judge 判断
sign in 登入
sign out 登出
sign up 注册
(2) 如何给类或方法添加注释
- 把光标停在 ( 类名或者方法名 ) 上,然后
Alt+Enter
,出现几个选项,选择Add Javadoc
就OK了
(3) @Controller
- 当给类添加了
@Controller注解
时,SpringBoot就会自动识别扫描该类,把它作为bean
去管理,同时也识别该类是一个controller
- 重点注意 controller server mapper 三者的关系
(4) IDEA常用快捷键
-
ctrl+p
方法参数提示 -
ctrl+alt+l
代码格式化 -
ctrol+alt+o
删除没有使用到的引入的文件或依赖 -
sout
System.out.println()
(5) SpringBoot工程文件说明
-
src/main/java
所有的java代码 -
src/main/resources
配置文件和静态文件-
resources/templates
模板文件 -
resources/static
静态文件,比如css,img文件 -
resources/application.properties
配置文件,当然也可以改成application.yml
-
(6) GitHub 授权登陆
- 1.
点击头像 => settings => Developer settings => OAuth Apps => Register a new OAuth application => ...
- 2.官网授权登陆教程 - [ 获取 Client ID 和 Authorization callback URL ]
- 3.通过上面的12两步,可以获取到
Client ID
和Authorization callback URL
- 4.官网授权登陆教程 - [ 具体的跳转流程 ]
- a. 携带3获得的相关parameter请求授权登陆地址,成功会扭转到redirect_uri地址,同时返回 ( code ) 和 ( state )
- b. 用 ( code ) 换取 ( access_token )
- c. 用 ( access_token ) 访问github的 ( user ) 接口,获取用户信息
- 5.测试:
https://api.github.com/user?access_token=xxx
(7) IDEA 找不到 Database 选项卡
- 搜索
Plugins => Database Navigator
并安装该插件 - 这里我没用这个插件,而是使用
Navicat
来设计管理数据库
(8) mysql的一些基础知识
- 安装-免费的社区版本-最底部-MySQL Community Server
- 教程
- 常用命令教程
- 注意点: 最后面的分号一定要加
- 常用命令
- 连接数据库
mysql -h主机地址 -u用户名 -p用户密码
- 退出mysql
exit
- 查看数据库
show databases;
- 创建数据库
create database 数据库名;
- 删除数据库
drop database 数据库名;
- 使用数据库
use 数据库名;
- 查看当前使用的数据库
select database();
- 查看表
show tables;
- 创建表
create table 表名 (dde列名 类型);
- 删除表
droop table 表名;
- 显示表结构
desc 表名;
- 修改密码
mysqladmin -uroot -p123456 password 12345678;
- 查看数据库的安装路径
show variables like "%char%";
- 连接数据库
(9) @Autowired 和 new 的区别?
-
@Autowired
- 注解方式是由spring创建的对象 ( 单例 ) 对象 作用域是整个项目 项目一启动就创建了
-
new
- new出来的对象作用域只在你对应的类中 每个调用的时候都是会创建一个新的对象,是 ( 多例 )
(10) po dto entity bean 等文件夹的区别
- 相关资料
-
po
entity bean
三个类似,都是持久化对象persistent,即数据库中的一条记录对象
-
dto
是经过处理后的po,和ui层即前端页面对应
(11) controller层,server层,mapper层,之间的关系
-
controller
- 负责http请求,将参数等信息传递给server层,并将结果返回给客户端
-
server
- 负责处理逻辑
- 当需要同时处理多个表的关系时,一般都交给sever来组装处理,比如合并多个model
- 接收controller传递的参数,并调用mapper
-
mapper
( DAO )- 负责对数据库增删改查
(12) java中的 ( 数组 )
- 使用大括号包裹的成员
int[] a = {1, 2, 3, 4}
- 刚开始把我搞蒙蔽,注意不是ts中的
let a: number[] = [1,2,3,4]
int[] a = {1, 2, 3, 4}
(13) RESTful API 接口规范
- RESTful API
(14) Map
-
Map
中键和值一一映射,可以通过键来获取值
-
Object put(Object k, Object v)
存 -
Object get(Object k)
取
public class CollectionsDemo {
public static void main(String[] args) {
Map m1 = new HashMap(); // new HashMap()
m1.put("Zara", "8");
m1.put("Mahnaz", "31");
System.out.println(" Map Elements");
System.out.print("\t" + m1);
}
}
结果:
Map Elements
{Mahnaz=31, Zara=8}
(14) springboot中如何解析前端传来的json类型的参数
- 因为:前端在
POST
请求中,一般设置的header中的Content-Type: application/json
- 所以:
POST
请求出入的参数被放在body
中,以JSON
形式传给后端 - 解决: 利用
@RequestBody
获取到body对象,然后通过Map
做映射,通过get
获取属性
// 新增
@PostMapping("/musics")
public void addMusics(
@RequestBody Map body
) {
String name = (String) body.get("name");
String album = (String) body.get("album");
String singer = (String) body.get("singer");
musicService.add(name, album, singer);
}
(15) String -> Timestamp -> datetime 三者的转换
-
java中的Timestamp
相当于数据库中的datatime
Timestamp.valueOf(String v)
public Boolean edit(Map body) {
int id = (int) body.get("id");
String name = (String) body.get("name");
String album = (String) body.get("album");
String singer = (String) body.get("singer");
// ( Timestamp ) 类型对应的是数据库中的 ( datetime ) 类型
Timestamp startTime = Timestamp.valueOf((String) body.get("startTime"));
// Timestamp.valueOf(string v) 将string转成timestamp
Timestamp endTime = Timestamp.valueOf((String) body.get("endTime"));
int status = musicMapper.editMusic(name, album, singer, startTime, endTime, id);
Boolean aBoolean = judgeIsSuccess(status);
return aBoolean;
}
(16) 热更新
- 当每次修改java代码时都必须手动 run Application 很麻烦
-
spring-boot-devtools
可以实现热更新 - 设置过程
- 1.引入
spring-boot-devtools
的maven包 - 2.
setting => Build,Execution,Deployment => Compiler => 勾上Build project automaticallly
- 第2步有个缺点,就是必须在没有running或debugging的情况才热更新
- 3.
ctrl + shift + a
调出action
- 4.在
action
中搜索Registry
- 5.在
Registry
中勾选compiler.autoMake.allow.when.app.running
- 6.如果前端展示是直接写的html,则还可以添加一个浏览器插件
LiveReload
- 如果是vue或者react的单页项目就不需要了
- 注意:(一般情况,请忽略该注意 )
- 描述:当只是修改html时,是不会再重启java程序的,因为本身并没有去修改server相关的代码
- 非要解决:需要配置如下
application.properties => spring.devtools.restart.exclude=static/**,public/**
- 1.引入
org.springframework.boot
spring-boot-devtools
true
true
(二) 从零开始一个项目
(1) 新建一个SpringBoot项目 ( Spring Assistant)
- 安装插件:
file => setting => plugins => 在Marketplace中搜索并安装插件 => Spring Assistant
- 新建项目:
file => New Project => Spring Assistant => Next => Web => Spring Web => Finish
- 使用 Spring Assistant 和使用 maven 创建项目的区别?
- 主要就是Spring Assistant配置好了web开发的 pom.xml 中的配置项
- 使用maven需要自己配置
parent
spring-boot-starter-web
spring-boot-starter-thymeleaf
等
-
也可以勾选下面这四个
(2) 配置文件的分离
- 怎么样根据不同的环境,来读取不同的配置文件?? ( 类似于前端的环境变量,比如process.env.NODE_ENV )
- springboot的配置文件有以下
- application.properties
- application-dev.properties 开发环境
- application-prod.properties 运行环境
- application-test.properties 测试环境
在不同环境的配置文件中,设置同步变量的不同值即可
如下:
-------
server.port = 7777
# 第一段的github起到分类的作用,便于识别
# 1. 比如: server 和 github 就是不同的两种类型
# 2. 使用时需要使用 @Value 注解来引入这里定义的属性变量
github.client.client_id = 982573d16925889ea84c
github.client.client_secret = 9a2e9710af69531e51254e6902ab834905a7a755
github.client.redirect_uri = http://localhost:7777/callback
(3) 利用 mybatis 操作 mysql 实现CURD
1. 安装相关的maven依赖
- 因为:是通过
Spring Assistant
创建的项目 - 所以:自动配好了
parent spring-boot-starter-web spring-boot-starter
等maven依赖
pom.xml
-------
mysql
mysql-connector-java
8.0.21
runtime
org.springframework.boot
spring-boot-starter-jdbc
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.3
2. 在 application.properties 中配置数据源组件配置项
application.properties
-------
# 定义数据源组件
# 已弃用 spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# 注意 spring.datasource.url="jdbc:mysql://localhost:3306/数据库名称?时区信息"
# 注意 上面的时区信息不能少,不然会报错
# 分别是 ( 数据库url ) ( 数据库驱动 ) ( 用户名 ) ( 密码 )
spring.datasource.url=jdbc:mysql://localhost:3306/7-community-java?serverTimezone=GMT%2B8&useSSL=false
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
3. 插入数据到数据库
3-1 新建model,通过new Model()将数据绑定到实例属性上,然后将实例传入mapper的insert方法中
-
model
主要是用于数据库操作相关
的对象
,专门命名为model - model 会在 mapper 或 controller 中用到
model
-------
public class UserModel {
private Integer id;
private String name;
private String accountId;
private String token;
private Long gmtCreate;
private Long gmtModified;
getter adnd setter and toString...
}
3-2 新建mapper通过 @Mapper @Insert 将数据插入数据库
-
mapper
主要用于操作数据库 - 注意
- mapper 是
interface
- mapper中的
UserMapper.insert()
方法是在controller
中被调用的
- mapper 是
mapper
-------
@Mapper
public interface UserMapper {
@Insert("insert into user (name, account_id, token, gmt_create, gmt_modified) values (#{name}, #{accountId}, #{token}, #{gmtCreate}, #{gmtModified})")
void insert(UserModel user);
}
3-3 新建 controller,通过 @Autowired 引入mapper,并调用mapper中的insert()方法
controller
-------
import com.example.demo.model.UserModel;
@Controller
public class AuthorizeController {
@Autowired
private UserMapper userMapper;
@GetMapping("/callback")
public String callback() {
UserModel user = new UserModel(); // 注意区分@Autowired 和 new 的区别
user.setToken(UUID.randomUUID().toString());
user.setName(gitHubUser.getName());
...
userMapper.insert(user);
// 1. 登陆后获取用户信息后,将数据写入user对象中
// 2. 调用 mapper 中的 userMapper.insert(user) 将user存入数据库
}
}
4. 增删改查数据库数据
- 通过4-123步就能通过接口访问到music列表数据
-
数据库表如下图
4.1 新建model - MusicModel
model - MusicModel
-------
// model
// model 专门用来做数据库相关的模型对象
@Data
public class MusicModel {
private Integer id;
private String name;
private String album;
private String singer;
private String endTime;
private String startTime;
getter sertter toString 等
}
4.2 新建 mapper - MusicMapper
mapper - MusicMapper
-------
// mapper
// 1. mapper文件夹主要是用来映射数据库的相关操作
// 2. 注意:mapper 是 interface
@Mapper
public interface MusicMapper {
@Select(
"SELECT * from music where"
+ " name like CONCAT('%',#{searchKey},'%') or "
+ " album like CONCAT('%',#{searchKey},'%') or "
+ " singer like CONCAT('%',#{searchKey},'%')"
+ " limit #{offset},#{pageSize}"
)
public List selectMusics(Integer pageSize, Integer offset, String searchKey); // 查找 - 分页 + 条件模糊查询
@Select("SELECT * from music limit #{offset},#{pageSize}")
public List selectMusicsOnlyPagination(Integer pageSize, Integer offset); // 查找 - 分页查询
@Select("SELECT * from music")
public List selectAllMusics(); // 查找 - 总数据
@Insert("insert into music (name, album, singer, startTime, endTime) values (#{name}, #{album}, #{singer}, #{startTime}, #{endTime})")
public int insertMusic(String name, String album, String singer, Date startTime, Date endTime); // 插入
@Update("update music set name=#{name},album=#{album},singer=#{singer},startTime=#{startTime},endTime=#{endTime} where id=#{id}")
public int editMusic(String name, String album, String singer, Timestamp startTime, Timestamp endTime, int id);
@Delete("delete from music where id=#{id}")
public int deleteMusic(int id); // 删除
}
4.3 新建 service - MusicService
ervice - MusicService
-------
// service
// 1. 主要用于处理获取请求参数后的处理逻辑
// 2. server 是连接 controller 和 mapper(DAO) 的桥梁
@Service
public class MusicService {
@Autowired
private MusicMapper musicMapper;
// 逻辑抽离 - 判断操作是否成功,比如添加,删除
private Boolean judgeIsSuccess(int status) {
if (status > 0) {
System.out.println("操作成功");
return true;
} else {
System.out.println("操作失败");
return false;
}
}
// 列表
public PaginationDTO list(Integer pageSize, Integer current, String searchKey) {
Integer offset = pageSize * (current - 1); // 分页的偏移量
List musicModels;
if (searchKey != null) { // searchKey 是否存在
musicModels = musicMapper.selectMusics(pageSize, offset, searchKey); // 分页 + 条件
} else {
musicModels = musicMapper.selectMusicsOnlyPagination(pageSize, offset); // 分页
}
List totalMusics = musicMapper.selectAllMusics(); // 总数据是为了获取 total
PaginationDTO paginationDTO = new PaginationDTO(); // 这里之所以要new,是因为其他方法中也可能会用到paginationDTO,所以必须不能是单列
paginationDTO.musics = musicModels;
paginationDTO.total = totalMusics.toArray().length;
paginationDTO.current = current;
paginationDTO.pageSize = pageSize;
return paginationDTO;
}
// 新增
public Boolean add(String name, String album, String singer) {
Date startTime = new Date(); // 注意:数据库中的data是 ( datetime ) 格式
Date endTime = new Date();
int status = musicMapper.insertMusic(name, album, singer, startTime, endTime);
Boolean aBoolean = judgeIsSuccess(status);
return aBoolean;
}
// 修改
public Boolean edit(Map body) {
int id = (int) body.get("id");
String name = (String) body.get("name");
String album = (String) body.get("album");
String singer = (String) body.get("singer");
Timestamp startTime = Timestamp.valueOf((String) body.get("startTime")); // ( Timestamp ) 类型对应的是数据库中的 ( datetime ) 类型
Timestamp endTime = Timestamp.valueOf((String) body.get("endTime"));
int status = musicMapper.editMusic(name, album, singer, startTime, endTime, id);
Boolean aBoolean = judgeIsSuccess(status);
return aBoolean;
}
// 删除
public Boolean delete(int id) {
int status = musicMapper.deleteMusic(id);
Boolean aBoolean = judgeIsSuccess(status);
return aBoolean;
}
}
4.4 新建 controller - Musics
controller - Musics
-------
//@Controller
//@ResponseBody
@RestController // 是上面两个注解的合集
public class MusicController {
@Autowired
MusicService musicService;
// 列表
// @RequestMapping(value="/musics", method = {RequestMethod.GET})
@GetMapping("/musics")
public PaginationDTO getMusics(
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
@RequestParam(name = "current", defaultValue = "1") Integer current,
@RequestParam(name = "searchKey", required = false) String searchKey
) {
PaginationDTO musics = musicService.list(pageSize, current, searchKey);
System.out.println(musics);
return musics;
}
// 新增
@PostMapping("/musics/add")
public Map addMusics(
@RequestBody Map body
) {
String name = (String) body.get("name");
String album = (String) body.get("album");
String singer = (String) body.get("singer");
Boolean isSuccess = musicService.add(name, album, singer);
body.put("isSuccess", isSuccess); // 是否成功
return body;
}
// 修改
@PostMapping("/musics/edit")
public Map editMusics(
@RequestBody Map body
) {
Boolean isSuccess = musicService.edit(body);
body.put("isSuccess", isSuccess); // 是否成功
return body;
}
// 删除
@PostMapping("/musics/delete")
public Map deleteMusics(
@RequestBody Map body
) {
int id = (int) body.get("id");
Boolean isSuccess = musicService.delete(id);
body.put("isSuccess", isSuccess); // 是否成功
return body;
}
}
项目源码
- 项目源码
- 线上访问地址-歌曲增删改查
资料
- 注解 https://juejin.cn/post/6844903813631983623
- 常用快捷键 https://zhuanlan.zhihu.com/p/61690346