HTML & CSS

入门程序

image-20250326103944029
1
2
<h1>HTML入门程序<h1>
<img src="img/1.png">

HTML快速入门

1
2
3
4
5
6
7
8
9
10
11
<html>
<head>

// 这里写的是给浏览器看的,如css
</head>

<body>

//这里写的是给用户看的,如文字图片
</body>
</html>

新手程序:

1
2
3
4
5
6
7
8
9
10
<html>
<head>
<title>HTML快速入门</title>
</head>

<body>
<h1>Hello HTML</h1>
<img src="D:\blog\cotton-star\source\image\1.jpg" width="300">
</body>
</html>

tip:注释:ctrl+/

央视新闻

1. 标题及超链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>中东部气温骤降 对春耕春管影响几何?如何应对?</title>
</head>
<body>
<!-- 定义一个标题,标题内容: 中东部气温骤降 对春耕春管影响几何?如何应对?-->
<h1>中东部气温骤降 对春耕春管影响几何?如何应对?</h1>

<!-- 定义一个超链接,里面展示 央视网 -->
<a href="https://www.cctv.com/">央视网</a> 2025年03月28日 00:14:44

</body>
</html>

a: 超链接标签:

1
2
3
4
href: 链接地址 -url地址
target: 打开方式:
_blank:新窗口打开
_self: 本窗口打开

2. CSS样式

image-20250328091621852
  1. 行内样式

    1
    2
    <!-- 方式一:行内样式 -->
    <span style="color:gray">2025年03月28日 00:14:44</span>
  2. 内部样式

    image-20250328092140421
  3. 外部样式

    先创建css文件

    image-20250328092258133

    把内部样式里的拷贝过来,style不要

    最后在html加上

    1
    <link rel="stylesheet" href="css/news.css">
  4. 颜色描述形式

    image-20250328092923285

3. 选择器

  1. 元素选择器

    1
    2
    3
    4
    5
    <style>
    span{
    color: gray;
    }
    </style>
  2. 类选择器

    1
    2
    3
    4
    5
    <style>
    .cls{
    color: #ff0000;
    }
    </style>
  3. ID选择器

    1
    2
    3
    4
    5
    6
    <style>
    #time{
    color: #0000ff;
    }

    </style>

优先级:ID->类->元素

4. 去除超链接下方的下划线

image-20250331084224517

  • 换行:

    1
    <br>

5. 引入视频/音频

image-20250331091214436
1
2
3
<p>
新的一段
</p>

6. 加粗 缩进

加粗 strong和b

image-20250331092332946

缩进

&nbsp : 空格

&lt : 小于

&rt : 大于

image-20250331092802838 image-20250331092951432

7. 盒子模型

image-20250331095500760

JavaScript

基础代码

  • 引入方式:
image-20250331101301137
  1. 内部脚本:
1
2
3
4
5
6
<body>
<script>
// 1. 内部脚本
alert('Hello JavaScript');
</script>
</body>
  • alert效果 :

    image-20250331101641385
  1. 外部脚本:

    image-20250331102221010

    创建js脚本,再加上

    1
    <script src="demo.js"></script>

基础语法

变量与常量

image-20250331102431716 image-20250331102558308
1
2
3
4
5
6
7
8
9
10
<script>
let a = 10;
a="hello";
a = true;
alert(a);


const PI = 3.14;
alert(PI);
</script>>

把值写进body

1
document.write("PI");

数据类型

image-20250331104137540 image-20250331104653405

函数

image-20250331105334972

自定义对象

image-20250331110453744

Json

image-20250331111115045
1
2
3
4
5
6
7
8
9
<script>
let person={
name:"张三",
age:18,
gender:"男"
}

alert(JSON.stringify(person));
</script>
image-20250331111605740

DOM

image-20250331141918561
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<h1 id="title1">11111</h1>
<h1>22222</h1>
<h1>33333</h1>


<script>
// 1. 修改第一个<h1>的值

// 1.1 获取DOM对象
let h1=document.querySelector('#title1');

// 1.2 调用DOM对象中的属性或方法
h1.innerHTML='Hello World';
</script>
</body>

事件监听

image-20250331151706648
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<input type="button" id="btn1"value="点我一下试试1">
<input type="button" id="btn2"value="点我一下试试2">


<script>
//事件监听
// 设置按钮二为开启浏览器
document.querySelector('#btn2').addEventListener('click',function(){
window.open('https://www.yuanshen.com')
})
document.querySelector('#btn1').addEventListener('click',function(){
alert('btn1被点击了')
})

</script>
image-20250331153250554

MAVEN

创建MAVEN项目

作用:

  1. 描述好需要的jar包,maven会自动下载并导入
  2. 方便配置编译
  3. 统一项目结构

全局配置:

在项目中点击关闭项目,点击自定义

image-20250407142108425

最底下所有设置

image-20250407142128829

把自己的maven写入

image-20250407142155374

就可以了

内容:

主函数和测试函数

image-20250407142248191

在运行后会自动生成一个target字节码文件

image-20250407142337630

MAVEN 坐标

定义:

image-20250407142543844

组成:

image-20250407142744613 image-20250407142757832

导入MAVEN项目

image-20250407143037582

点击加号,导入pom文件即可

依赖配置

image-20250407143905754

https://mvnrepository.com/

image-20250407143350437
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
<?xml version="1.0" encoding="UTF-8"?>
<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>org.example</groupId>
<artifactId>maven-project1</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>18</maven.compiler.source>
<maven.compiler.target>18</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<!--配置依赖-->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.22</version>
</dependency>
</dependencies>
</project>

排除依赖

image-20250407144103100

生命周期

image-20250407144211992 image-20250407144350224 image-20250407144437897

JUnit

单元测试工具 断言的使用(测试业务方法结果有没有问题) 单元测试覆盖率

image-20250407154232615

Springboot

Springboot入门程序

在IDEA创建第一个springboot项目

image-20250408084305947

下一步勾选springwebimage-20250408084322327

之后删除多余文件,只需要这两个

image-20250408085248668

在main里:image-20250408085451436

这个是启动类,要运行spring只要启动这个类就好了

  • resources里:

    image-20250408085614913
    1
    2
    3
    static里存的是静态文件:如css这类的
    templates存的是模板文件
    第三个则是核心配置文件
  • 创建helloController

    请求处理类一般叫Controller

    image-20250408085845485

    同时加上

    image-20250408085933284
  • 入门程序

    1
    2
    3
    4
    5
    6
    7
    8
    @RestController//表示当前类是一个请求处理类
    public class HelloController {
    @RequestMapping("/hello")//表示网址
    public String hello(String name){
    System.out.println("name:"+name);
    return "hello"+name+"~";
    }
    }

HTTP协议

请求协议

具有请求行 请求头 请求体

获取一下http协议的具体内容

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
@RequestMapping("/request")
public String request(HttpServletRequest request){
//1. 获取请求方式
String method = request.getMethod();
System.out.println("请求方式 "+method);

//2. 获取请求url地址
String url = request.getRequestURL().toString();//http://localhost:8080/request
System.out.println("请求url地址 "+url);

String uri= request.getRequestURI();// /request
System.out.println("请求uri地址 "+ uri);

//3. 获取请求协议
String protocol = request.getProtocol();
System.out.println("请求协议 "+protocol);

//4. 获取请求参数 -name -age
String name = request.getParameter("name");
String age = request.getParameter("age");
System.out.println("name="+name+" age="+age);

//5. 获取请求头 - Accept
String accept = request.getHeader("Accept");
System.out.println("请求头 Accept "+accept);

return "OK";
}

效果:

image-20250408103826134 image-20250408103838159

响应协议

image-20250408104938941

设置状态码 响应头 响应体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RequestMapping("/response")
public void response(HttpServletResponse response) {
//1. 设置相应状态码
response.setStatus(401);

//2. 设置响应头
response.setHeader("name", "cl");

//3. 设置响应体
try {
response.getWriter().println("<h1>Hello Response</h1>");
} catch (Exception e) {
e.printStackTrace();
}
}

1
2
3
4
@RequestMapping("/response2")
public ResponseEntity<String> response2(){
return ResponseEntity.status(401).header("name", "cl").body("<h1>Hello Response</h1>");
}

springboot实例

  • 首先把写的前端代码放入resource中的static

    image-20250408143153510
  • 由user.txt我们要创建一个对象类,把数据在里面都有表示

    image-20250408143319898
    1
    2
    3
    @Data ---不需要每个进行get set
    @No.... --- 无参构造
    @All... --- 有参构造
  • hutool:为了更好的解析txt里的文字

    1
    2
    3
    4
    5
    <dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.27</version>
    </dependency>

最终代码:

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
@RestController
public class UserController {

@RequestMapping("/list")
public List<User> list() throws Exception {
//1. 加载并读取user.txt文件,获取用户数据
//InputStream in=new FileInputStream(new File("F:\\study\\java\\webproject\\springboot-web-01\\src\\main\\resources\\user.txt"));

InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
ArrayList<String> lines = IoUtil.readLines(in, "UTF-8", new ArrayList<>());


//2. 解析用户信息, 封装为User对象 -> List集合
List<User> userList = lines.stream().map(line -> {
String[] parts = line.split(",");
Integer id = Integer.parseInt(parts[0]);
String username = parts[1];
String password = parts[2];
String name = parts[3];
Integer age = Integer.parseInt(parts[4]);
LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
return new User(id, username, password, name, age, updateTime);
}).toList();

//3. 返回数据(json)
return userList;
}
}
image-20250408154328361

报错可能是因为端口被占用

image-20250409094634377

kill掉就好了

1
netstat -ano | findstr "8080"
1
taskkill /PID 15664 /F

三层架构改造实例

按照控制层,服务层,持久层规范创建

image-20250409090234093

操作文件,操作数据库—->dao 持久层

业务逻辑—->服务层

接受请求,响应数据—–>控制层

举例控制层:

1
2
3
4
5
6
7
8
9
10
11
private UserService userService=new UserServiceImpl(); //这个之后需要改
@RequestMapping("/list")
public List<User> list() throws Exception {


//2. 获取数据
List<User> userList = userService.findAll();

//3. 返回数据(json)
return userList;
}

解耦

image-20250409093139599

利用容器实现分层解耦

image-20250409093235018
  • @Component —> 交给IOC容器管理
image-20250409095845311
  • @Autowired —> 自动从容器寻找赋值,自动查询该类型的bean对象并赋值
image-20250409094840670

需要将dao层和service层交给IOC容器管理

image-20250409094954376

再在需要的对象上加上@Autowired即可

image-20250409104918095

MYSQL

以前学过,记点复习

char固定 varchar不固定

DDL

创建表

image-20250409110429854 image-20250409110600630

自增

image-20250409111028334
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 自建一个表
CREATE table user(
id int UNSIGNED PRIMARY KEY auto_increment COMMENT 'ID,主键',
username varchar(20) not NULL UNIQUE COMMENT '用户名',
name VARCHAR(10) not NULL COMMENT '姓名',
gender tinyint unsigned not null comment '性别 1 男 2 女',
phone char(11) not NULL unique comment'手机号',
job tinyint unsigned comment '职位 1.班主任 2.讲师 3.学工主管 4.校验主管',
salary int unsigned comment'薪资',
entry_date date comment'入职日期',
image varchar(255) COMMENT'图像',
PASSWORD varchar(32) DEFAULT(123456) COMMENT'密码字段',
create_time datetime comment '创建时间',
update_time datetime comment '修改时间'
) comment '员工表';

查询修改删除表:

image-20250410100722427

查看表

1
2
3
4
5
6
7
8
9
-- 查询所有表
show tables;

-- 查看表结构
desc user;

-- 查询建表语句
show create table user;

修改表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 字段: 添加字段qq varchar(13)
alter table user add qq varchar(13) comment'qq';

-- 字段: 修改字段qq varchar(15)
alter table user modify qq VARCHAR(15) COMMENT'qq';

-- 字段: 修改字段名qq->qq_num
alter table user change qq qq_num varchar(15) COMMENT'qq';

-- 字段: 删除字段qq_num
alter table user drop column qq_num;

-- 修改表名
alter table user rename to employee;

-- 删除表
drop table employee

图形化更方便,基本用不到

DML

insert:

image-20250410102110835
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- DML : 数据操作语言
-- DML : 插入数据 - insert
-- 1. 为 emp 表的 username, password, name, gender, phone 字段插入值
insert into user(username, password, name, gender, phone) values ('songjiang','12345678','宋江',1,'13333333333');



-- 2. 为 emp 表的 所有字段插入值
insert into user values(null,'likui','李逵',1,'13233333333',1,6000,'2020-10-01','1.jpg','12345678',now(),now());



-- 3. 批量为 emp 表的 username, password, name, gender, phone 字段插入数据
insert into user(username, password, name, gender, phone) values ('zhangsan','12345678','张三',1,'13333333334'),('lisi','12345678','李四',1,'13333333335');

update

image-20250410104030156
1
2
3
4
5
6
7
8
-- DML : 更新数据 - update
-- 1. 将 emp 表的ID为1员工 用户名更新为 'zhangsan', 姓名name字段更新为 '张三'
update user set name = '王五',username='wangwu' where ID = 1;


-- 2. 将 emp 表的所有员工的入职日期更新为 '2010-01-01'
update user set entry_date ='2010-01-01';

delete

image-20250410104503046
1
2
3
4
5
6
-- DML : 删除数据 - delete
-- 1. 删除 emp 表中 ID为1的员工
delete from user where ID=1;

-- 2. 删除 emp 表中的所有员工
delete from user;

DQL

image-20250410105334918

基本查询

image-20250410105600120
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
--  =================== DQL: 基本查询 ======================
-- 1. 查询指定字段 name,entry_date 并返回
select name,entry_date from user;

-- 2. 查询返回所有字段
-- 方式1: 推荐
select id, username, password, name, gender, phone, job, salary, entry_date, image, create_time, update_time from user;
-- 方式2: 不推荐
select * from user;

-- 3. 查询所有员工的 name,entry_date, 并起别名(姓名、入职日期)
select name as 姓名 ,entry_date as 入职日期 FROM user

select name 姓名, entry_date 入职日期 from user;

-- 4. 查询已有的员工关联了哪几种职位(不要重复) - distinct
select distinct job FROM user;

条件查询

image-20250410110525564 image-20250410110605384
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
--  =================== DQL: 条件查询 ======================
-- 1. 查询 姓名 为 柴进 的员工
select * from user where name= '柴进';

-- 2. 查询 薪资小于等于5000 的员工信息
select * from user where salary<=5000;

-- 3. 查询 没有分配职位 的员工信息
SELECT * from user where job is null;

-- 4. 查询 有职位 的员工信息
select * from user where job is not null;

-- 5. 查询 密码不等于 '123456' 的员工信息
SELECT * from user where password !='123456';



-- 6. 查询 入职日期 在 '2000-01-01' (包含) 到 '2010-01-01'(包含) 之间的员工信息
select * from user where entry_date between '2000-01-01' and '2010-01-01';

-- select * from emp where entry_date between '最小值' and '最大值';

-- 7. 查询 入职时间 在 '2000-01-01' (包含) 到 '2010-01-01'(包含) 之间 且 性别为女 的员工信息
select * from user where entry_date between '2000-01-01' and '2010-01-01' and gender=2;


-- 8. 查询 职位是 2 (讲师), 3 (学工主管), 4 (教研主管) 的员工信息
select * from user where job =2 or job=3 or job=4;


select * from user where job in (2,3,4);



-- 9. 查询 姓名 为两个字的员工信息 (_: 单个字符; % 任意个字符)
SELECT * from user where name LIKE '__';

-- 10. 查询 姓 '李' 的员工信息
select * from user where name like '李%';

-- 11. 查询 姓名中包含 '二' 的员工信息
select * from user where name like '%二%';

分组查询

聚合函数:

image-20250411080552338
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
--  =================== DQL: 分组查询 ======================
-- 聚合函数
-- 注意: 所有的聚合函数不参与null的统计

-- 1. 统计该企业员工数量 - count
-- count(字段)
select count(id) from user;

-- count(*) : 推荐
select count(*) from user;

-- count(常量): 推荐
select count(1) from user;

-- 2. 统计该企业员工的平均薪资 - avg
select avg(salary) from user;

-- 3. 统计该企业员工的最低薪资 - min
select min(salary) from user;

-- 4. 统计该企业员工的最高薪资 - max
select max(salary) from user;

-- 5. 统计该企业每月要给员工发放的薪资总额(薪资之和) - sum
select sum(salary) from user;
image-20250411081639651
1
2
3
4
5
6
7
-- 分组
-- 注意: 分组之后, select后的字段列表不能随意书写, 能写的一般是 分组字段 + 聚合函数;
-- 1. 根据性别分组 , 统计男性和女性员工的数量
select gender,count(*) from user group by gender ;

-- 2. 先查询入职时间在 '2015-01-01' (包含) 以前的员工 , 并对结果根据职位分组 , 获取员工数量大于等于2的职位
select job,count(*) from user where entry_date <= '2015-01-01' GROUP BY job having count(*) >=2

排序查询 分页查询

image-20250411083229322
1
2
3
4
5
6
7
8
--  =================== 排序查询 ======================
-- 1. 根据入职时间, 对员工进行升序排序 - asc
select * from user order by entry_date asc;
-- 2. 根据入职时间, 对员工进行降序排序 - desc
select * from user order by entry_date desc;
-- 3. 根据 入职时间 对公司的员工进行 升序排序 , 入职时间相同 , 再按照 更新时间 进行降序排序
select * from user order by entry_date,update_time desc;

image-20250411084136224
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
--  =================== 分页查询 ======================
-- 1. 从起始索引0开始查询员工数据, 每页展示5条记录
select * from user limit 5;

-- 2. 查询 第1页 员工数据, 每页展示5条记录
select * from user limit 0,5;

-- 3. 查询 第2页 员工数据, 每页展示5条记录
select * from user limit 5,5;

-- 4. 查询 第3页 员工数据, 每页展示5条记录
select * from user limit 10,5;

-- 页码
-- 起始索引 = (页码 - 1) * 每页展示记录数

Mybatis

入门程序

image-20250411090442293

还是老样子,先创建工程,在SQL里勾选mabatis framework 以及 Mysql Driver

image-20250414083709411

把多余的删除,留下src和pom

  • 在application.properties里填写配置信息
image-20250414083746867
1
2
3
4
5
6
7
spring.application.name=springboot-mybatis-quickstart

#配置数据库的连接信息
spring.datasource.url=jdbc:mysql://localhost:3306/user
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
  • 创建实体类包和Mapper包

    image-20250414084236204

  • 最后在测试下编写单元测试代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @SpringBootTest // 单元测试
    class SpringbootMybatisQuickstartApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testFindAll() {
    List<User> userList = userMapper.findAll();
    userList.forEach(System.out::println);
    }
    }
  • 最终测试

    image-20250414090210748

日志输出

1
2
#配置mybatis的日志输出
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
image-20250414090912838

增删改查

删除操作

直接在mapper接口中写下, Mapper中#{}参数占位符,用于安全地插入动态参数到 SQL 语句中。

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 根据id删除用户
*/
@Delete("delete from user where id = #{id}")
public void DeleteById(Integer id);


/**

假如用Integer , 就会显示影响的行数

*/

测试:

1
2
3
4
@Test
public void testDeleteById() {
userMapper.DeleteById(5);
}
image-20250414102441320

增加操作

要求:

image-20250414102531681

mapper:

1
2
3
4
5
/**
* 添加用户
*/
@Insert("insert into user(username,password,name,age) values(#{username},#{password},#{name},#{age})")
public void Insert(User user);

test:

1
2
3
4
5
@Test
public void testInsert() {
User user = new User(null, "admin", "admin", "管理员", 18);
userMapper.Insert(user);
}

修改操作

要求:

image-20250414104158800

mapper:

1
2
3
4
5
/**
* 更新信息
*/
@Update("update user set username = #{username}, password=#{password},name=#{name},age=#{age} where id=#{id}")
public void UpdateById(User user);

test:

1
2
3
4
5
@Test
public void testUpdateById() {
User user=new User(1,"zhouyu","123456","周瑜",18);
userMapper.UpdateById(user);
}

查询操作

image-20250414105753255

:

1
当有多个参数要传递时,#{}分不清谁是谁,需要@Param("username")像这样在参数前面加上注解,不加也行,sprintboot有插件

mapper:

1
2
3
4
5
6
/**
* 根据用户名密码查询用户
*/
@Select("select * from user where username=#{username} and password = #{password}")
public User findByUsernameAndPassword(@Param("username") String username, @Param("password")String password);
}

test:

1
2
3
4
5
@Test
public void testFindByUsernameAndPassword() {
User user = userMapper.findByUsernameAndPassword("admin", "admin");
System.out.println(user);
}

XML映射配置

规则:

image-20250414140402841 image-20250414140501112
  • 第一步:同包同名(mapper也要)

    image-20250414140802525

    或者指定位置,利用辅助配置

    image-20250414143043948
  • 第二步:初始配置

    1
    2
    3
    4
    5
    6
    7
    8
    <?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="">



    </mapper>

    namespace里填写:

    image-20250414141336017

    UserMapper接口的路径

  • 第三步:快速使用

    在里面 id和方法名一致 resultType是返回的一条记录

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?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.cl.springbootmybatisquickstart.mapper.UserMapper">

    <select id="findAll" resultType="com.cl.springbootmybatisquickstart.pojo.User">
    select * from user
    </select>


    </mapper>

Springboot项目配置文件

image-20250414145717129

yml中定义常见类型:

image-20250414145631691

用yml代替properties

image-20250414150414963

案例实战

Restful

image-20250414162643855

Result

image-20250414170222568

配置

  1. 首先创建项目:

    勾选

    1
    2
    3
    4
    lombok
    spring web
    mybatis framework
    mysql driver
  2. 创建数据库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    CREATE TABLE dept (
    id int unsigned PRIMARY KEY AUTO_INCREMENT COMMENT 'ID, 主键',
    name varchar(10) NOT NULL UNIQUE COMMENT '部门名称',
    create_time datetime DEFAULT NULL COMMENT '创建时间',
    update_time datetime DEFAULT NULL COMMENT '修改时间'
    ) COMMENT '部门表';

    INSERT INTO dept VALUES (1,'学工部','2024-09-25 09:47:40','2024-09-25 09:47:40'),
    (2,'教研部','2024-09-25 09:47:40','2024-09-09 15:17:04'),
    (3,'咨询部','2024-09-25 09:47:40','2024-09-30 21:26:24'),
    (4,'就业部','2024-09-25 09:47:40','2024-09-25 09:47:40'),
    (5,'人事部','2024-09-25 09:47:40','2024-09-25 09:47:40'),
    (6,'行政部','2024-11-30 20:56:37','2024-09-30 20:56:37');
    image-20250415102159704
    • 表当中四个字段相当于实体类中四个属性
  3. 配置idea

    image-20250415101633570

    创建三层架构的包以及实体类的包,配置application.yml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    spring:
    application:
    name: tlias-web-management
    # 数据库连接信息(注意缩进对齐到spring层级)
    datasource:
    url: jdbc:mysql://localhost:3306/tlias
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456

    # MyBatis配置
    mybatis:
    configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 注意是短横线 `

    Mapper层

    image-20250415102711809

    定义一个接口,用来实现和数据库的**”交流”**

    Service层:

    image-20250415102804177

    用来实现业务逻辑,定义接口是为了避免更改,只要把impl修改即可impl中需要加上**@Service**,被标注的类,在应用启动时会被Spring扫描到,并由IOC容器自动创建其对象(即Bean),并管理它的生命周期(如依赖注入、初始化、销毁等)。通过@Autowired,容器会自动解决这些依赖关系。

    Controller层:

    image-20250415103330427

    用来实现和页面的**”交流”**

    1
    @RestController:自动将返回值转为json

    部门管理

部门管理

1.1 部门列表查询

看需求

先查看接口文档以及需求说明:

image-20250415141505615 image-20250415141821806

在数据库中先编写sql语句,按修改时间降序排列:

1
SELECT * FROM dept ORDER BY update_time desc;

项目中将*改成具体的内容

1
SELECT id,name,create_time,update_time FROM dept ORDER BY update_time desc;

之后开始进行idea的代码实现,按照上面三层架构以及解耦,需要在controller层中调用service层,在service层中调用mapper层来实现

看pojo中的result

image-20250415142822433

一共有三种返回类型,成功无返回,成功有返回和失败,这个查询显然要有返回,也就要带上返回值

代码

控制层代码:

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class DeptController {

@Autowired
private DeptService deptService;
@GetMapping("/depts") // 请求路径方式
public Result findAll(){
List<Dept> deptList = deptService.findAll();
return Result.success(deptList);
}
}

服务层代码:

1
2
3
4
5
6
7
8
9
@Service
public class DeptServiceimpl implements DeptService {
@Autowired
private DeptMapper deptMapper;
@Override
public List<Dept> findAll() {
return deptMapper.findAll();
}
}

持久层代码:

1
2
3
4
5
@Mapper
public interface DeptMapper {
@Select("SELECT id,name,create_time,update_time FROM dept ORDER BY update_time desc;")
public List<Dept> findAll();
}

在apifox中的快捷请求中测试

image-20250415144301979

成功实现

查询优化

在上面我们的createTime 和 updateTime并没有成功封装,

image-20250415144555730

接下来优化一下

第一种方案:

  • 手动结果映射:通过@Results及@Result进行手动结果映射

    打开Mapper接口

1
2
3
4
5
6
7
8
9
10
@Mapper
public interface DeptMapper {

@Results({
@Result(column = "create_time",property = "createTime"),
@Result(column = "update_time",property = "updateTime")
})
@Select("SELECT id,name,create_time,update_time FROM dept ORDER BY update_time desc;")
public List<Dept> findAll();
}
image-20250415144948890

成功查询

第二种方案:

image-20250415145101438

第三种方案:

开启驼峰命名

image-20250415145635388

1.2 部门删除

从url中获取数据

通过需求我们需要从url中获取数据:

  1. @PathVariable
1
2
3
@DeleteMapping("/depts/{id}")
public Result deleteById(@PathVariable Integer id){
System.out.println("根据ID删除部门: "+id);

这样子我们的路径就要写成:

1
http://localhost:8080/depts/1
  1. @RequestParam如果前端传来的参数名与变量名相同,可以省略@RequestParam

    1
    2
    3
    4
    5
    @DeleteMapping("/depts")
    public Result deleteById(@RequestParam("id") Integer id){
    System.out.println("根据ID删除部门: "+id);
    return Result.success();
    }

    这样子的路径必须为

    1
    http://localhost:8080/depts?id=1
代码

控制层:

1
2
3
4
5
@DeleteMapping("/depts")
public Result deleteById(@RequestParam("id") Integer id){
deptService.deleteById(id);
return Result.success();
}

服务层:

1
2
3
4
@Override
public void deleteById(Integer id) {
deptMapper.deleteById(id);
}

持久层:

1
2
@Delete("delete from dept where id = #{id}")
public void deleteById(Integer id);

1.3 新增部门

注:@RequestBody
1
将一个json格式的请求参数直接封装在一个对象中
代码:

控制层

1
2
3
4
5
@PostMapping("/depts")
public Result add(@RequestBody Dept dept){
deptService.add(dept);
return Result.success();
}

服务层

1
2
3
4
5
6
7
8
@Override
public void add(Dept dept) {
//1. 补全基础属性
dept.setCreateTime(LocalDateTime.now());
dept.setUpdateTime(LocalDateTime.now());
//2. 调用mapper接口
deptMapper.add(dept);
}

持久层

1
2
@Insert("insert into dept(name,create_time,update_time) values(#{name},#{createTime},#{updateTime}) ")
public void add(Dept dept);

1.4 修改部门

查询回显

为了能点击修改时显示当前名字便于更改

控制层

1
2
3
4
5
@GetMapping("/depts/{id}")
public Result searchById(@PathVariable Integer id){
Dept dept=deptService.searchById(id);
return Result.success(dept);
}

服务层

1
2
3
4
@Override
public Dept searchById(Integer id) {
return deptMapper.searchById(id);
}

持久层

1
2
@Select("SELECT id,name,create_time,update_time FROM dept where id=#{id}")
Dept searchById(Integer id);
修改数据

还是先写sql语句:

1
update dept set name='',update_time='' where id=#{id}

注意!:

1
2
@RequestBody 只能用于单个参数,不能拆分到多个参数
当JSON无法正确绑定到参数时,Spring会返回HTTP 400

因此我们就不能收两个参数name和id,需要封装成一个DTO,这里属性较少就不封装了,直接用dept传输

代码:

1
2
3
4
5
@PutMapping("/depts")
public Result updateData(@RequestBody Dept dept){
deptService.updateData(dept);
return Result.success();
}
1
2
3
4
5
6
@Override
public void updateData(Dept dept) {
dept.setUpdateTime(LocalDateTime.now());
deptMapper.updateData(dept);
}

1
2
@Update("update dept set name=#{name},update_time=#{updateTime} where id=#{id}")
public void updateData(Dept dept);
RequestMapping

可以在类开头加上

1
@RequestMapping("/depts")

后面的get put post等就有了公共的路径,只要加上自己需要的就行了

日志技术

System.out.println:

image-20250421105716424

logback快速入门

image-20250421110431367

logback.xml:固定格式,直接复制

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
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d 表示日期,%thread 表示线程名,%-5level表示级别从左显示5个字符宽度,%logger显示日志记录器的名称, %msg表示日志消息,%n表示换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern>
</encoder>
</appender>

<!-- 系统文件输出 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 日志文件输出的文件名, %i表示序号 -->
<FileNamePattern>D:/tlias-%d{yyyy-MM-dd}-%i.log</FileNamePattern>
<!-- 最多保留的历史日志文件数量 -->
<MaxHistory>30</MaxHistory>
<!-- 最大文件大小,超过这个大小会触发滚动到新文件,默认为 10MB -->
<maxFileSize>10MB</maxFileSize>
</rollingPolicy>

<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d 表示日期,%thread 表示线程名,%-5level表示级别从左显示5个字符宽度,%msg表示日志消息,%n表示换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern>
</encoder>
</appender>

<!-- 日志输出级别 -->
<root level="ALL">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
</configuration>

定义日志记录对象

1
private static final Logger log = LoggerFactory.getLogger(类名.class);
image-20250421112659417

多表关系

多对一

在数据库中,例如部门和员工的关系,一个部门可以有多个员工,一个员工只在一个部门,我们可以通过在多的一方,也就是员工的数据库,加上一个dept_id,对应上部门的id,这样就可以用这个id来判断在哪个部门

image-20250421141118390

问题:

image-20250421141811024
  1. 创建表时添加外键约束

    1
    2
    3
    4
    5
    create table 表名(
    字段名 数据类型,
    ...
    [constraint] [外键名称] foreign key (外键字段名) references 主表 (字段名)
    )
  2. 创建表后添加外键约束

    1
    alter table 表名 add constraint 外键名称 foreign key (外键字段名) references 主表 (字段名);
  3. eg:

    先创建表

    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
    CREATE TABLE dept (
    id int unsigned PRIMARY KEY AUTO_INCREMENT COMMENT 'ID, 主键',
    name varchar(10) NOT NULL UNIQUE COMMENT '部门名称',
    create_time datetime DEFAULT NULL COMMENT '创建时间',
    update_time datetime DEFAULT NULL COMMENT '修改时间'
    ) COMMENT '部门表';

    INSERT INTO dept VALUES (1,'学工部','2023-09-25 09:47:40','2023-09-25 09:47:40'),
    (2,'教研部','2023-09-25 09:47:40','2023-10-09 15:17:04'),
    (3,'咨询部','2023-09-25 09:47:40','2023-11-30 21:26:24'),
    (4,'就业部','2023-09-25 09:47:40','2023-09-25 09:47:40'),
    (5,'人事部','2023-09-25 09:47:40','2023-09-25 09:47:40'),
    (15,'行政部','2023-11-30 20:56:37','2023-11-30 20:56:37');




    create table emp(
    id int unsigned primary key auto_increment comment 'ID,主键',
    username varchar(20) not null unique comment '用户名',
    password varchar(32) default '123456' comment '密码',
    name varchar(10) not null comment '姓名',
    gender tinyint unsigned not null comment '性别, 1:男, 2:女',
    phone char(11) not null unique comment '手机号',
    job tinyint unsigned comment '职位, 1 班主任, 2 讲师 , 3 学工主管, 4 教研主管, 5 咨询师',
    salary int unsigned comment '薪资',
    image varchar(255) comment '头像',
    entry_date date comment '入职日期',
    dept_id int unsigned comment '部门ID',
    create_time datetime comment '创建时间',
    update_time datetime comment '修改时间'
    ) comment '员工表';

    添加外键约束

    1
    2
    -- 添加外键约束(emp的 dept_id ---> dept的主键id)
    alter table emp add constraint fk_emp_dept_id foreign key (dept_id) REFERENCES dept(id);
  4. 更方便的图形化添加…

    image-20250421143803576

    image-20250421143707296

以上基本不用了

image-20250421144127923

一对一

特殊的多对一,需要在定义字段时加上unique

多对多

image-20250421145303154

多表查询

1
select * from emp,dept;

这样子会输出两个表的笛卡尔积,当我们要输入emp及其对应的dept时需要:

1
select * from emp,dept where emp.dept_id=dept.id;

内连接

image-20250421155124471

eg: 查询 性别为男 工资>8000 的员工 的ID 姓名 及所属的部门名称

隐式:

1
select emp.id,emp.name,dept.name from emp,dept where emp.dept_id=dept.id and emp.gender=1 and emp.salary>8000;

显式:

1
select emp.id,emp.name,dept.name from emp join dept on emp.dept_id=dept.id where emp.gender=1 and emp.salary>8000;

tip:给表起别名

image-20250421161405202

外连接

image-20250421162246681

eg1: 查询员工表 所有员工的姓名 和 对应的部门名称(左外连接)

1
select e.name,d.name from emp e left outer join dept d on e.dept_id=d.id;

eg2: 查询部门表 所有 部门的名称,和对应的员工名称(右外连接)

1
select e.name,d.name from emp e right outer join dept d on e.dept_id=d.id;

eg3:查询工资高于8000 的所有员工的姓名 和对应的部门名称(左外连接)

1
select e.name,d.name from emp e left outer join dept d on e.dept_id=d.id where e.salary>8000;

子查询

image-20250421164424218

eg:

  1. 查询最早入职时间:

    1
    select min(entry_date) from emp;
  2. 查询 最早入职 的员工信息:

    1
    select * from emp where entry_date=(select min(entry_date) from emp);
  3. 查询在”阮小五”入职之后入职的员工信息

    1
    select * from emp where entry_date > (select entry_date from emp where name ="阮小五");
  4. 查询**”教研部”和”咨询部”** 的所有员工信息

    1
    select * from emp where dept_id in (select id from dept where name="教研部" or name ="咨询部"); 
  5. 查询与 “李忠” 的薪资 及 职位 都相同的员工信息

    1
    select * from emp where salary=(select salary from emp where name="李忠") and job=(select job from emp where name="李忠");

    优化:

    1
    select * from emp where (salary,job)=(select salary,job from emp where name="李忠");
  6. 获取每个部门中薪资最高的员工信息

    1
    select * from emp where (dept_id,salary) in (select dept_id,max(salary) from emp group by dept_id);

员工管理

准备工作:

image-20250422102140894
  1. 创建表

    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
    -- 员工表
    create table emp(
    id int unsigned primary key auto_increment comment 'ID,主键',
    username varchar(20) not null unique comment '用户名',
    password varchar(32) default '123456' comment '密码',
    name varchar(10) not null comment '姓名',
    gender tinyint unsigned not null comment '性别, 1:男, 2:女',
    phone char(11) not null unique comment '手机号',
    job tinyint unsigned comment '职位, 1 班主任, 2 讲师 , 3 学工主管, 4 教研主管, 5 咨询师',
    salary int unsigned comment '薪资',
    image varchar(255) comment '头像',
    entry_date date comment '入职日期',
    dept_id int unsigned comment '部门ID',
    create_time datetime comment '创建时间',
    update_time datetime comment '修改时间'
    ) comment '员工表';


    INSERT INTO emp VALUES
    (1,'shinaian','123456','施耐庵',1,'13309090001',4,15000,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2000-01-01',2,'2023-10-20 16:35:33','2023-11-16 16:11:26'),
    (2,'songjiang','123456','宋江',1,'13309090002',2,8600,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2015-01-01',2,'2023-10-20 16:35:33','2023-10-20 16:35:37'),
    (3,'lujunyi','123456','卢俊义',1,'13309090003',2,8900,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2008-05-01',2,'2023-10-20 16:35:33','2023-10-20 16:35:39'),
    (4,'wuyong','123456','吴用',1,'13309090004',2,9200,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2007-01-01',2,'2023-10-20 16:35:33','2023-10-20 16:35:41'),
    (5,'gongsunsheng','123456','公孙胜',1,'13309090005',2,9500,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2012-12-05',2,'2023-10-20 16:35:33','2023-10-20 16:35:43'),
    (6,'huosanniang','123456','扈三娘',2,'13309090006',3,6500,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2013-09-05',1,'2023-10-20 16:35:33','2023-10-20 16:35:45'),
    (7,'chaijin','123456','柴进',1,'13309090007',1,4700,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2005-08-01',1,'2023-10-20 16:35:33','2023-10-20 16:35:47'),
    (8,'likui','123456','李逵',1,'13309090008',1,4800,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2014-11-09',1,'2023-10-20 16:35:33','2023-10-20 16:35:49'),
    (9,'wusong','123456','武松',1,'13309090009',1,4900,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2011-03-11',1,'2023-10-20 16:35:33','2023-10-20 16:35:51'),
    (10,'linchong','123456','林冲',1,'13309090010',1,5000,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2013-09-05',1,'2023-10-20 16:35:33','2023-10-20 16:35:53'),
    (11,'huyanzhuo','123456','呼延灼',1,'13309090011',2,9700,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2007-02-01',2,'2023-10-20 16:35:33','2023-10-20 16:35:55'),
    (12,'xiaoliguang','123456','小李广',1,'13309090012',2,10000,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2008-08-18',2,'2023-10-20 16:35:33','2023-10-20 16:35:57'),
    (13,'yangzhi','123456','杨志',1,'13309090013',1,5300,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2012-11-01',1,'2023-10-20 16:35:33','2023-10-20 16:35:59'),
    (14,'shijin','123456','史进',1,'13309090014',2,10600,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2002-08-01',2,'2023-10-20 16:35:33','2023-10-20 16:36:01'),
    (15,'sunerniang','123456','孙二娘',2,'13309090015',2,10900,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2011-05-01',2,'2023-10-20 16:35:33','2023-10-20 16:36:03'),
    (16,'luzhishen','123456','鲁智深',1,'13309090016',2,9600,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2010-01-01',2,'2023-10-20 16:35:33','2023-10-20 16:36:05'),
    (17,'liying','12345678','李应',1,'13309090017',1,5800,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2015-03-21',1,'2023-10-20 16:35:33','2023-10-20 16:36:07'),
    (18,'shiqian','123456','时迁',1,'13309090018',2,10200,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2015-01-01',2,'2023-10-20 16:35:33','2023-10-20 16:36:09'),
    (19,'gudasao','123456','顾大嫂',2,'13309090019',2,10500,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2008-01-01',2,'2023-10-20 16:35:33','2023-10-20 16:36:11'),
    (20,'ruanxiaoer','123456','阮小二',1,'13309090020',2,10800,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2018-01-01',2,'2023-10-20 16:35:33','2023-10-20 16:36:13'),
    (21,'ruanxiaowu','123456','阮小五',1,'13309090021',5,5200,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2015-01-01',3,'2023-10-20 16:35:33','2023-10-20 16:36:15'),
    (22,'ruanxiaoqi','123456','阮小七',1,'13309090022',5,5500,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2016-01-01',3,'2023-10-20 16:35:33','2023-10-20 16:36:17'),
    (23,'ruanji','123456','阮籍',1,'13309090023',5,5800,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2012-01-01',3,'2023-10-20 16:35:33','2023-10-20 16:36:19'),
    (24,'tongwei','123456','童威',1,'13309090024',5,5000,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2006-01-01',3,'2023-10-20 16:35:33','2023-10-20 16:36:21'),
    (25,'tongmeng','123456','童猛',1,'13309090025',5,4800,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2002-01-01',3,'2023-10-20 16:35:33','2023-10-20 16:36:23'),
    (26,'yanshun','123456','燕顺',1,'13309090026',5,5400,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2011-01-01',3,'2023-10-20 16:35:33','2023-11-08 22:12:46'),
    (27,'lijun','123456','李俊',1,'13309090027',2,6600,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2004-01-01',2,'2023-10-20 16:35:33','2023-11-16 17:56:59'),
    (28,'lizhong','123456','李忠',1,'13309090028',5,5000,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2007-01-01',3,'2023-10-20 16:35:33','2023-11-17 16:34:22'),
    (30,'liyun','123456','李云',1,'13309090030',NULL,NULL,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2020-03-01',NULL,'2023-10-20 16:35:33','2023-10-20 16:36:31'),
    (36,'linghuchong','123456','令狐冲',1,'18809091212',2,6800,'https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg','2023-10-19',2,'2023-10-20 20:44:54','2023-11-09 09:41:04');


    -- 员工工作经历信息
    create table emp_expr(
    id int unsigned primary key auto_increment comment 'ID, 主键',
    emp_id int unsigned comment '员工ID',
    begin date comment '开始时间',
    end date comment '结束时间',
    company varchar(50) comment '公司名称',
    job varchar(50) comment '职位'
    )comment '工作经历';
  2. 创建实体类:一张表对应一个实体类

  3. 三层架构

    image-20250422104537103
  4. 编写sql

    1
    2
    -- 查询所有的员工信息 以及所属部门名称
    select e.*,d.name from emp e left join dept d on e.dept_id=d.id;

1.1 分页查询

前端的展示是这样的,其中每页展示数靠的是前端,而后半部分靠的是后端

1
2
-- 查询总记录数
select count(*) from emp e left join dept d on e.dept_id=d.id;
image-20250422110217030

原始方法

1
2
3
4
5
6
7
8
9
10
11
-- 分页查询
-- limit 起始索引,每页展示记录数

-- 查询第一页每页展示五条
select e.*,d.name from emp e left join dept d on e.dept_id=d.id limit 0,5;

-- 查询第二页每页展示五条
select e.*,d.name from emp e left join dept d on e.dept_id=d.id limit 5,5;

-- 查询第三页每页展示五条
select e.*,d.name from emp e left join dept d on e.dept_id=d.id limit 10,5;

由上可得,我们从前端传递过来页码1,2,3……,而我们的数据库需要的是0,5,10……,我们需要通过计算把前端传递过来的数据换算成数据库所需要的数据:

1
起始索引=(页码-1)*每页展示记录数
image-20250422111233770

后端返回的数据一般封装成一个实体类

image-20250422111324841

代码实现

思路:

image-20250422143642943

控制层:

注: 在@RequestParam中使用defaultValue = “1”可以设置默认值

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 分页查询
* @param page
* @param pageSize
* @return
*/
@GetMapping
public Result pageQuery(@RequestParam(value = "page",defaultValue = "1") Integer page,@RequestParam(value = "pageSize",defaultValue = "10") Integer pageSize){

PageResult<Emp> PR=empService.pageQuery(page,pageSize);

return Result.success(PR);
}

服务层:

1
2
3
4
5
6
7
8
@Override
public PageResult<Emp> pageQuery(Integer page, Integer pageSize) {
long total=empMapper.getSum(page,pageSize);

List<Emp> pageData= empMapper.pageQuery((page-1)*pageSize,pageSize);

return new PageResult<Emp>(total,pageData);
}

持久层

当我们的实体类中没有某个属性的时候,需要去看接口文档来创建所需的属性名称,同时在sql语句中设置别名d.name deptName来匹配所创建的属性

1
2
3
4
5
6
@Select("select count(*) from emp e left join dept d on e.dept_id=d.id")
public Long getSum(Integer page, Integer pageSize);

@Select("select e.*,d.name deptName from emp e left join dept d on e.dept_id=d.id " +
"order by e.update_time desc limit #{page},#{pageSize};")
public List<Emp> pageQuery(Integer page, Integer pageSize);

PageHelper

image-20250423100155564

使用:

  1. 引入依赖:放在pom的dependences里

    1
    2
    3
    4
    5
    6
    <!--分页插件PageHelper-->
    <dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.7</version>
    </dependency>
  2. 定义一个普通的查询语句,不需要limit,同时也不需要传递参数进来

    1
    2
    3
    @Select("select e.*,d.name deptName from emp e left join dept d on e.dept_id=d.id " +
    "order by e.update_time desc ")
    public List<Emp> pageQuery();
  3. 回到服务层,把list封装到Page中,page中有自带获取各种属性的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 1. 设置分页参数
    PageHelper.startPage(page,pageSize);

    // 2. 执行查询
    List<Emp> empList=empMapper.pageQuery();

    // 3. 解析查询结果并封装
    Page<Emp> p = (Page<Emp>) empList;
    return new PageResult<Emp>(p.getTotal(),p.getResult());
image-20250423104956731

1.2 条件分页查询

需要根据姓名,性别,入职时间进行条件查询:

1
2
3
select e.*,d.name deptName from emp e left join dept d on e.dept_id=d.id 
where e.name like '%阮%' and e.gender=1 and e.entry_date BETWEEN '2010-01-01' and '2020-01-01'
ORDER BY e.update_time desc;

代码:

控制层:

对于接收,看需求文档,把姓名,性别,入职时间接受进来,其中时间每个项目的格式不同,需要自己去定义格式

1
@DateTimeFormat(pattern = "yyyy-MM-dd")
1
2
3
4
5
6
7
8
9
10
11
@GetMapping
public Result pageQuery(@RequestParam(value = "page",defaultValue = "1") Integer page,
@RequestParam(value = "pageSize",defaultValue = "10") Integer pageSize,
String name, Integer gender,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){
log.info("分页查询:{},{},{},{},{},{}",page,pageSize,name,gender,begin,end);
PageResult<Emp> PR=empService.pageQuery(page,pageSize,name,gender,begin,end);

return Result.success(PR);
}

服务层:

没有什么变化,需要多传入参数就是了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public PageResult<Emp> pageQuery(Integer page, Integer pageSize, String name, Integer gender, LocalDate begin, LocalDate end) {

//------------------原始分页查询-------------------------
// long total=empMapper.getSum(page,pageSize);
//
// List<Emp> pageData= empMapper.pageQuery((page-1)*pageSize,pageSize);

//------------------pagehelper------------------------:
// 1. 设置分页参数
PageHelper.startPage(page,pageSize);

// 2. 执行查询
List<Emp> empList=empMapper.pageQuery(name,gender,begin,end);

// 3. 解析查询结果并封装
Page<Emp> p = (Page<Emp>) empList;
return new PageResult<Emp>(p.getTotal(),p.getResult());
}

持久层:

由于sql语句很复杂,我们可以使用mapper的xml来编写,同时要注意!,**’%#{name}%’**引号内不能使用#{}

需要使用concat函数来拼接字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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.cl.mapper.EmpMapper">


<select id="pageQuery" resultType="com.cl.pojo.Emp">
select e.*,d.name deptName from emp e left join dept d on e.dept_id=d.id
where e.name like concat('%',#{name},'%')
and e.gender=#{gender}
and e.entry_date
BETWEEN #{begin} and #{end}
ORDER BY e.update_time desc;
</select>
</mapper>

优化:

  1. 请求参数接收优化:

    由于 接受过多 要改的会很麻烦,就可以把接收参数封装到一个实体类中,之后只要改 实体类 就好了

实体类:

1
2
3
4
5
6
7
8
9
@Data
public class EmpQueryParam {
private Integer page=1;
private Integer pageSize=10;
private String name;
private Integer gender;
@DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate begin;
@DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate end;
}

控制层:

1
2
3
4
5
6
7
@GetMapping
public Result pageQuery(EmpQueryParam empQueryParam){
log.info("分页查询:{}",empQueryParam);
PageResult<Emp> PR=empService.pageQuery(empQueryParam);

return Result.success(PR);
}

服务层

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public PageResult<Emp> pageQuery(EmpQueryParam empQueryParam) {

// 1. 设置分页参数
PageHelper.startPage(empQueryParam.getPage(),empQueryParam.getPageSize());

// 2. 执行查询
List<Emp> empList=empMapper.pageQuery(empQueryParam);

// 3. 解析查询结果并封装
Page<Emp> p = (Page<Emp>) empList;
return new PageResult<Emp>(p.getTotal(),p.getResult());
}

持久层,xml不用变

1
public List<Emp> pageQuery(EmpQueryParam empQueryParam);
  1. 由于传入参数可以不选择,但是我们写的sql语句是写死的,假如不写他不会传递参数,也就查询不出东西

    这就需要使用mybatis中的动态sql

    image-20250424160409283
    1
    2
    <if>: 判断条件是否成立,如果条件为true,则拼接SQL
    <where>: 根据查询条件,来生成where关键字,并且会自动去除and 或 or

    修改后xml:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <select id="pageQuery" resultType="com.cl.pojo.Emp">
    select e.*,d.name deptName from emp e left join dept d on e.dept_id=d.id
    <where>
    <if test="name != null and name !=''">
    e.name like concat('%',#{name},'%')
    </if>

    <if test="gender != null">
    and e.gender=#{gender}
    </if>

    <if test="begin != null and end != null">
    and e.entry_date BETWEEN #{begin} and #{end}
    </if>
    </where>
    ORDER BY e.update_time desc
    </select>

1.3 新增员工

  • 还是先写sql语句,我们要新增员工到emp表,新增员工的工作经历到emp_expr表

    – 保存员工基本信息

    1
    2
    3
    4
    5
    6
    7
    8
    -- 保存员工基本信息
    INSERT INTO emp (
    username, name, gender, phone, job, salary, image,
    entry_date, dept_id, create_time, update_time
    ) VALUES (
    'linpingzhi', '林平之', 1, '13333333333', 1, 6000, '1.jpg',
    '2020-01-01 00:00:00', 1, '2024-10-01 00:00:00', '2024-10-01 00:00:00'
    );

    – 批量保存员工工作经历信息

    1
    2
    3
    4
    5
    6
    7
    8
    -- 批量保存员工工作经历信息
    insert into emp_expr(
    emp_id,begin,end,company,job
    ) values (
    37,'2020-01-01','2021-01-01','百度','java开发'
    ),(
    37,'2020-01-01','2021-01-01','字节','java开发'
    )

保存员工基本信息

先理思路:

增加不需要返回什么数组,只要Result.success()就可以了

image-20250428101329063
1
@RequestMapping("/emps")

前端传来json格式,我们用@RequestBody接收

image-20250428101522239

在我们的对象中并没有exprList属性,需要我们新增一个属性

1
2
// 封装工作经历信息
private List<EmpExpr> exprList;

之后就是具体代码实现

1
2
3
4
5
6
7
8
9
10
/**
* 新增员工
* @return
*/
@PostMapping
public Result save(@RequestBody Emp emp){
log.info("新增员工:{}",emp);
empService.save(emp);
return Result.success();
}
1
2
3
4
5
6
7
8
@Override
public void save(Emp emp) {
// 1. 保存员工基本信息
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());

empMapper.insert(emp);
}
1
2
3
4
5
6
7
/**
* 新增员工基本信息
* @param emp
*/
@Insert("INSERT INTO emp (username, name, gender, phone, job, salary, image,entry_date, dept_id, create_time, update_time" +
") VALUES (#{username}, #{name},#{gender},#{phone},#{job},#{salary},#{image},#{entryDate},#{deptId},#{createTime},#{updateTime})")
void insert(Emp emp);

保存员工工作经历信息

先传来exprList

1
List<EmpExpr> exprList = emp.getExprList();

需要判断是否存在工作经历,

1
2
3
if(!CollectionUtils.isEmpty(exprList)){
empExprMapper.insertBatch(exprList);
}

要传来的工作经历已经封装到了exprList中,我们需要使用动态sql去遍历即可

foreach

image-20250428102900184
1
2
3
4
5
6
7
8
9
10
<!--批量保存员工工作经历信息-->
<insert id="insertBatch">
insert into emp_expr(
emp_id,begin,end,company,job
)values
<foreach collection="exprList" item="expr" separator=",">
(#{expr.empId},#{expr.begin},#{expr.end},#{expr.company},#{expr.job})
</foreach>

</insert>

同时他前端传来的工作经历并没有员工的id,需要我们自己获取主键

@Options:

1
2
3
4
@Options(useGeneratedKeys = true,keyProperty = "id")
@Insert("INSERT INTO emp (username, name, gender, phone, job, salary, image,entry_date, dept_id, create_time, update_time" +
") VALUES (#{username}, #{name},#{gender},#{phone},#{job},#{salary},#{image},#{entryDate},#{deptId},#{createTime},#{updateTime})")
void insert(Emp emp);

最后在服务层把id遍历进exprlist的empid即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public void save(Emp emp) {
// 1. 保存员工基本信息
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());

empMapper.insert(emp);

// 2. 保存员工工作经历信息
List<EmpExpr> exprList = emp.getExprList();
if(!CollectionUtils.isEmpty(exprList)){
// 遍历集合,为empId赋值
exprList.forEach(empExpr -> {
empExpr.setEmpId(emp.getId());
});
empExprMapper.insertBatch(exprList);
}
}

事务管理

image-20250428140753279 image-20250428141124195
1
2
3
4
5
6
7
8
-- 开启事务:
start transaction ;

-- 提交事务
commit ;

-- 回滚事务
rollback ;

spring事务管理

image-20250428142624102

image-20250428143455425

在方法前加上即可,这样就可以避免部分错误但是还是运行了

  • rollbackFor:

    image-20250428144317191

image-20250428145058749

因此要设置rollbackFor

  • propagation

    image-20250428145245632
image-20250428150726868

文件上传

image-20250428151548298
  • 关键:

    method必须为post,get方法大小不够,同时enctype必须为multipart/form-data否则只能上传文件名,type必须为file,才能选择要上传的文件

    服务端接收在c盘的临时文件,之后要永久保存

image-20250428161550804
  • 为了防止传上相同的文件名导致覆盖,可以用一个随机数来重写文件名

    image-20250428162649628

默认上传最大大小为1M

可以在.yml中配置

1
2
3
4
5
6
servlet:
multipart:
# 单个文件最大大小
max-file-size: 10MB
# 最大请求大小 包括所有文件
max-request-size: 100MB

1.4 删除员工

如何接收 ?ids=1,2,3

先写sql语句:

删除员工的同时要删除其工作经历

1
2
3
4
-- 删除员工
delete from emp where id in (1,2,3);

delete from emp_expr where emp_id in (1,2,3);
  • 现在就是第一大问题,如何在控制层接收:

    image-20250429141714973

    解决:

    image-20250429142110581

这下就好办了

代码:

控制层:

1
2
3
4
5
6
7
8
9
10
/**
* 批量删除员工
* @return
*/
@DeleteMapping
public Result deleteById(@RequestParam List<Integer> ids){
log.info("批量删除员工");
empService.deleteById(ids);
return Result.success();
}

服务层:

1
2
3
4
5
6
7
8
9
@Transactional
@Override
public void deleteById(List<Integer> ids) {
// 1. 批量删除员工信息
empMapper.deleteById(ids);

// 2. 批量删除员工工作经历
empExprMapper.deleteById(ids);
}

持久层:

1
2
3
4
5
6
<delete id="deleteById">
delete from emp_expr where emp_id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
1
2
3
4
5
6
<delete id="deleteById">
delete from emp where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>

成功自己实现

1.5 修改员工

根据id查询

点击修改按钮,首先我们需要他返回这个人数据库的数据,根据ID查询即可:

看接口文档:

Get方式

/emps/1–> @PathVariable

编写sql语句:

1
select e.*,ee.* from emp e left join emp_expr ee on e.id=ee.emp_id where e.id=#{id}

因为有重名的:改为:

1
2
3
4
5
6
7
8
9
10
select
e.*,
ee.id ee_id,
ee.emp_id ee_empid,
ee.begin ee_begin,
ee.end ee_end,
ee.company ee_company,
ee.job ee_job
from emp e left join emp_expr ee on e.id=ee.emp_id
where e.id=#{id}

但是这样子做当一个人拥有两条工作经历时,mabatis无法将其自动封装成一个Emp类,只能我们自己手动结果封装,使用resultMap

image-20250519145818739
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
<!--    定义ResultMap-->
<resultMap id="empResultMap" type="com.cl.pojo.Emp">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="name" property="name"/>
<result column="gender" property="gender"/>
<result column="phone" property="phone"/>
<result column="job" property="job"/>
<result column="salary" property="salary"/>
<result column="image" property="image"/>
<result column="entry_date" property="entryDate"/>
<result column="dept_id" property="deptId"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
<!-- 封装工作经历信息-->
<collection property="exprList" ofType="com.cl.pojo.EmpExpr">
<id column="ee_id" property="id"/>
<result column="ee_empid" property="empId"/>
<result column="ee_begin" property="begin"/>
<result column="ee_end" property="end"/>
<result column="ee_company" property="company"/>
<result column="ee_job" property="job"/>

</collection>
</resultMap>

<!-- 根据id查询员工信息及工作经历-->
<select id="searchById" resultMap="empResultMap">
select
e.*,
ee.id ee_id,
ee.emp_id ee_empid,
ee.begin ee_begin,
ee.end ee_end,
ee.company ee_company,
ee.job ee_job
from emp e left join emp_expr ee on e.id=ee.emp_id
where e.id=#{id}
</select>
image-20250519152159435

修改

1
2
Arrays.asList()
把值收集到List里

首先具体SQL语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--    根据id更新员工基本信息-->
<update id="updateById">
UPDATE emp
SET
username = #{username},
password = #{password},
name = #{name},
gender = #{gender},
phone = #{phone},
job = #{job},
salary = #{salary},
image = #{image},
entry_date = #{entryDate},
dept_id=#{deptId},
update_time=#{updateTime}
WHERE id=#{id}
</update>

根据接口文档控制层:

1
2
3
4
5
6
7
8
9
10
/**
* 修改员工
* @return
*/
@PutMapping
public Result update(@RequestBody Emp emp){
log.info("修改员工: {}",emp);
empService.update(emp);
return Result.success();
}

服务层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Transactional(rollbackFor = Exception.class)
@Override
public void update(Emp emp) {
// 1. 根据ID修改员工的基本信息
emp.setUpdateTime(LocalDateTime.now());
empMapper.updateById(emp);

// 2. 根据ID修改员工的工作经历信息
// 2.1 先删除
empExprMapper.deleteById(Arrays.asList(emp.getId()));

// 2.2 后添加
List<EmpExpr> exprList=emp.getExprList();
if(!CollectionUtils.isEmpty(exprList)){
exprList.forEach(empExpr->empExpr.setEmpId(emp.getId()));
empExprMapper.insertBatch(exprList);
}
}

持久层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--    根据id更新员工基本信息-->
<update id="updateById">
UPDATE emp
SET
username = #{username},
password = #{password},
name = #{name},
gender = #{gender},
phone = #{phone},
job = #{job},
salary = #{salary},
image = #{image},
entry_date = #{entryDate},
dept_id=#{deptId},
update_time=#{updateTime}
WHERE id=#{id}
</update>

优化

set标签:

1
2
1. 自动生成set关键字
2. 自动去除更新字段后多余的,

优化后持久层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--    根据id更新员工基本信息-->
<update id="updateById">
UPDATE emp
<set>
<if test="username != null and username != ''">username = #{username},</if>
<if test="password != null and password != ''">password = #{password},</if>
<if test="name != null and name != ''">name = #{name},</if>
<if test="gender != null">gender = #{gender},</if>
<if test="phone != null and phone != ''">phone = #{phone},</if>
<if test="job != null">job = #{job},</if>
<if test="salary != null">salary = #{salary},</if>
<if test="image != null and image != ''">image = #{image},</if>
<if test="entryDate != null">entry_date = #{entryDate},</if>
<if test="deptId != null">dept_id = #{deptId},</if>
<if test="updateTime != null">update_time = #{updateTime}</if>
</set>
WHERE id = #{id}
</update>

全局异常处理器

格式如下

1
2
3
4
5
6
7
8
9
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler
public Result handleException(Exception e){
log.error("程序出错了",e);
return Result.error("出错啦,请联系管理员");
}
}
1
2
3
4
5
6
7
8
public Result handleDuplicateKeyException(DuplicateKeyException e){
log.error("程序出错啦~",e);
String message=e.getMessage();
int i=message.indexOf("Duplicate entry");
String errMsg=message.substring(i);
String[] arr=errMsg.split(" ");
return Result.error(arr[2]+"已存在");
}
image-20250526170749509

参考视频:【全网首发AI+JavaWeb开发入门,Tlias教学管理系统项目实战全套视频教程,从需求分析、设计、前后端开发、测试、程序优化到项目部署一套搞定】 https://www.bilibili.com/video/BV1yGydYEE3H/?p=9&share_source=copy_web&vd_source=b2941ac430d9a6b02b476a1e91c98c42