JavaWeb学习笔记
一. 前端开发
1. HTML
HTML学习笔记
2. CSS
CSS学习笔记
3. JavaScript
JavaScript学习笔记
4. Vue
A. Vue简介
Vue是一套前端框架,免除JS的DOM操作,简化书写
- 新建HTML页面,引入Vue.js文件
<script src="js/vue.js"></script>
- 在JS代码区域,创建Vue核心对象,定义数据模型
<script>new Vue({el: "#app",data: {message: "Hello Vue!"}})
</script>
- 编写视图
<div id="app"><input type="text" v-model="message">{{ message }}
</div>
B. Vue常用指令
指令 | 作用 |
---|---|
v-bind | 为HTML标签绑定属性值,如设置href,css样式等 |
v-model | 在表单元素上创建双向数据绑定 |
v-on | 为HTML标签绑定事件 |
v-if/v-else-if/v-else | 条件性的渲染某元素,判定为true时渲染,否则不渲染 |
v-show | 根据条件展示某元素,区别在于切换的是display属性的值 |
v-for | 列表渲染,遍历容器的元素或者对象的属性 |
-
v-bind:用来绑定 HTML 标签的属性,比如动态改变
href
,src
,class
,style
等。<a v-bind:href="url">链接</a>
-
v-model:用于在表单元素上实现双向数据绑定,通常用于
input
、textarea
或select
元素。<input v-model="message">
-
v-on:绑定事件处理程序,可以监听用户的交互操作,如点击、鼠标移入等。
<button v-on:click="submitForm">提交</button>
-
v-if/v-else-if/v-else:用于条件渲染,如果条件为真,渲染元素,否则不渲染。
<p v-if="isVisible">这是一个条件渲染的段落</p>
-
v-show:与
v-if
类似,不过v-show
不会移除 DOM 元素,而是通过控制display
样式来显示或隐藏元素。<p v-show="isVisible">这段内容根据条件展示</p>
-
v-for:用于列表渲染,遍历数组或对象。
<ul><li v-for="item in items" :key="item.id">{{ item.name }}</li> </ul>
C. Vue生命周期
Vue的生命周期指Vue对象创建和销毁的过程
mounted
: 挂载完成,Vue初始化成功,HTML页面渲染成功(发送请求到服务端,加载数据)
<script>new Vue({el: "#app",data: {},mounted() {console.log("Vue挂载完毕,发送请求获取数据");},methods: {},})
</script>
5. Ajax
A. 介绍
Asynchronous JavaScript And XML(异步的JavaScript和XML)
作用
- 数据交换: 通过Ajax可以向服务器发送请求和接收服务器响应的数据
- 异步交互:可以在不重新加载整个页面的情况下,与服务器交换数据并更新部分页面的技术。如搜索联想功能
B. Axios
对原生的Ajax进行封装,简化书写,快速开发
- 引入Axios的JS文件
<script src="js/axios-0.18.0.js"></script>
- 使用Axios发送请求,并获取响应结果
- axios.get(url [, config])
- axios.delete(url [, config])
- axios.post(url [, data[, config]])
- axios.put(url [, data[, config]])
发送GET请求
axios.get("http://yapi.smart-xwork.cn/mock/169327/emp/list").then((result) => {console.log(result.data);
});
发送POST请求
axios.post("http://yapi.smart-xwork.cn/mock/169327/emp/deleteByld","id=1").then((result) => {console.log(result.data);
});
6. 前端工程化
A. YAPI(接口文档管理平台)
YAPI官网
B. Vue项目创建
新建一个文件夹,在该文件夹中打开cmd,输入vue ui
创建vue项目
C. Element
Element官网
Vue组件库,用于创建更好看的组件(下图右边是基于Element)
1. 快速入门
-
安装ElementUI组件库(在当前工程的目录下),在命令行执行指令:
npm install element-ui@2.15.3
-
引入ElementUI组件库
//main.js
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';Vue.use(ElementUI);
- 访问官网,复制组件代码,调整
2. 常用组件
-
Table表格
表格 -
pagination分页
分页 -
Dialog对话框
对话框 -
Form表单
表单
D. Vue路由
前端路由:URL中的hash(#号)与组件之间的对应关系
VueRouter
:路由器类,根据路由请求在路由视图中动态渲染选中的组件<router-link>
:请求链接组件,浏览器会解析成<a>
<router-view>
:动态视图组件,用来渲染展示与路由路径对应的组件
E. 打包部署
nginx官网
二. Maven
是管理和构建java项目的工具
Maven官网
1. Maven介绍
2. 在idea中创建Maven项目和导入Maven项目
创建
导入
3. 依赖管理
依赖:就指项目运行所需要的jar包,一个项目可以引入多个依赖
A. 依赖配置(导入jar包)
- 在pom.xml中编写
<dependencies>
标签 - 在
<dependencies>
标签中 使用<dependency>
引入坐标 - 定义坐标的 groupld,artifactld, version
- 点击刷新按钮,引入最新加入的坐标
<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version></dependency>
</dependencies>
B. 依赖传递和排除依赖
依赖具有传递性
排除依赖
使用<exclusions></exclusions>
标签
<dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>5.3.8</version><exclusions><exclusion><groupId>org.springframework</groupId><artifactId>spring-core</artifactId></exclusion></exclusions>
</dependency>
C. 依赖范围
依赖的jar包,在默认情况下,可以在任何地方使用
可以通过<scope></scope>
设置其作用范围
scope值 | 主程序(main) | 测试程序(test) | 打包(运行) | 示例 |
---|---|---|---|---|
compile(默认) | Y | Y | Y | log4j |
test | N | Y | N | junit |
provided | Y | Y | N | servlet-api |
runtime | N | Y | Y | jdbc驱动 |
D. 生命周期
Maven中有3套相互独立的生命周期:
- clean:清理工作。
- default:核心工作,如:编译、测试、打包、安装、部署等。
- site:生成报告、发布站点等。
三. Web入门
1. SpringBootWeb入门
- 创建springboot工程,勾选spring web
- 创建请求处理类HelloController,添加请求处理方法hello,添加注释
@RestController
public class HelloControll {@RequestMapping("/hello-world")public String hello () {System.out.println("Hello World ~");return "Hello World ~";}
}
这里的启动类必须是请求处理类的父包
- 运行启动类,打开浏览器测试
打开localhost:8080/hello-world端口
2. HTTP协议
A. 介绍
Hyper Text Transfer Protocol(超文本传输协议)
规定浏览器和服务器之间数据传输的规则
- 基于TCP协议
- 基于请求-响应模型
- 是无状态的协议:每次请求-响应都是独立的
B. 请求协议
C. 响应协议
3. WEB服务器-Tomcat
一个轻量级的WEB服务器,是一个软件程序,对HTTP协议的操作进行封装,也称为web容器、servlet容器
4. 请求响应
A. 请求
1. 简单参数
SpringBoot方式:参数名与形参变量名相同,定义形参即可接收参数,会自动进行类型转换
@RequestMapping("/simpleParam")public String simpleParam(String name , Integer age) {System.out.println(name + " : " + age);return "OK";}
@RequestParam注解
- 方法形参名称与请求参数名称不匹配,通过该注解完成映射
- 该注解的require属性默认是true,代表请求参数必须传递
2. 实体参数
简单实体对象:请求参数名和形参对象属性名相同,定义POJO接收即可
@RequestMapping("/simplePojo")public String simplePojo(User user){System.out.println(user);return "OK";}
创建user类:
public class User {private String name;private Integer age;
}
postman中:GET: http://localhost:8080/simpleParam?name=abc&age=111
复杂实体对象:请求参数名和形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数
public class User {private String name;private Integer age;private Address address;
}public class Address{private String province;private String city;
}
postman中:GET:http://localhost:8080/simpleParam?name=abc&age=111&address.province=北京&address.city=北京
3. 数组集合参数
数组参数:请求参数名与形参数组名称相同且请求参数为多个,定义数组类型形参即可接收参数
@RequestMapping("/arrayParam")public String arrayParam(String[] hobby){System.out.println(Arrays.toString(hobby));return "OK";}
postman中:GET: http://localhost:8080/arrayParam?hobby=game&hobby=java
集合参数:请求参数名与形参中数组变量名相同,通过@RequestParam绑定参数关系
@RequestMapping("/listParam")public String listParam(@RequestParam List<String> hobby) {System.out.println(hobby);return "OK";}
postman中:GET: http://localhost:8080/listParam?hobby=game&hobby=java&hobby=sing
4. 日期参数
使用@DateTimeFormat注解完成日期参数格式转换
@RequestMapping("/dateParam")public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime) {System.out.println(updateTime);return "OK";}
postman中:GET:http://localhost:8080/dateParam?updateTime=2025-1-1 13:14:05
5. json参数
JSON参数:JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接收参数,需要使用@RequestBody标识
@RequestMapping("/jsonParam")public String jsonParam(@RequestBody User user){System.out.println(user);return "OK";}
postman中:GET:http://localhost:8080/jsonParam
6. 路径参数
路径参数:通过请求URL直接传递参数,使用{ … }来标识该路径参数,需要使用@PathVariable获取路径参数
@RequestMapping("/path/{id}")public String pathParam(@PathVariable Integer id) {System.out.println(id);return "OK";}
postman中:GET:http://localhost:8080/path/1
@RequestMapping("/path/{id}/{name}")public String pathParam2(@PathVariable Integer id, @PathVariable String name){System.out.println(id+" : " +name);return "OK";}
postman中:GET:http://localhost:8080/path/1/abc
B. 响应
1. @ResponseBody
- 类型:方法注解、类注解
- 位置:Controller方法上/类上
- 作用:将方法返回值直接响应,如果返回值类型是实体对象/集合,将会转换为JSON格式响应
- 说明:@RestController=@Controller+@ResponseBody;
2. 统一响应结果
Result(code,msg,data)
public class Result {//响应码,1代表成功;0代表失败private Integer code;//提示信息private String msg;//返回的数据private Object data;//...
}
5. 分层解耦
别人的笔记
A. 三层架构
B. 解耦
-
内聚:软件中各个功能模块内部的功能联系。
-
耦合:衡量软件中各个层/模块之间的依赖、关联的程度。
-
高内聚低耦合
-
控制反转:Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。
-
依赖注入:Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源称之为依赖注入。
-
Bean对象:IOC容器中创建、管理的对象,称之为bean。
C. IOC控制反转
Bean的声明
要把某个对象交给IOC容器管理,需要在对应的类上加上以下注解之一:
注解 | 说明 | 位置 |
---|---|---|
@Componert | 声明bean的基础注解 | 不属于以下三类时,用此注解 |
@Controller | @Component的衍生注解 | 标注在控制器类上 |
@Service | @Component的衍生注解 | 标注在业务类上 |
@Repository | @Component的衍生注解 | 标注在数据访问类上(由于与mybatis整合,用的少) |
注意事项:
-
声明bean的时候,可以通过value属性指定bean的名字,如果没有指定,默认为类名首字母小写。
-
使用以上四个注解都可以声明bean,但是在springboot集成web开发中,声明控制器bean只能用@Controller。
-
@SpringBootApplication具有包扫描作用,默认扫描当前包及其子包
D. DI依赖注入
- @Autowired:默认按照类型自动装配。
- 如果同类型的bean存在多个:
- @Primary
- @Autowired+@Qualifier(“bean的名称”)
- @Resource(name="bean的名称”)
- @Resource 与 @Autowired区别
- @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解。
- @Autowired 默认是按照类型注入,而@Resource默认是按照名称注入。
四. MySQL数据库
1. 数据库概述
A. 数据库介绍
- 数据库:DataBase(DB),是存储和管理数据的仓库
- 数据库管理系统:DataBase Management System(DBMS),操纵和管理数据库的大型软件。
- SQL: Structured Query Language,操作关系型数据库的编程语言,定义了一套操作关系型数据库统一标准。
mysql启动:net start mysql
mysql关闭: net stop mysql
mysql连接:mysql -u用户名 -p密码 /mysql -h数据库服务器IP地址 -P端口号
mysql -uroot -p123456 / mysql -h192.168.150.101 -P3306 -uroot -p123456
B. MySQL数据模型和SQL简介
1. MySQL数据模型
关系型数据库:建立在关系模型基础上,由多张相互连接的二维表组成的数据库
2. SQL简介
- SQL语句可以单行或多行书写,以分号结尾。
- SQL语句可以使用空格/缩进来增强语句的可读性。
- MySQL数据库的SQL语句不区分大小写。
- 注释:
单行注释:-- 注释内容
或#注释内容
(MySQL特有)
多行注释:/* 注释内容 */
分类 | 全称 | 说明 |
---|---|---|
DDL | Data Definition Language | 数据定义语言,用来定义数据库对象(数据库,表,字段) |
DML | Data Manipulation Language | 数据操作语言,用来对数据库表中的数据进行增删改 |
DQL | Data Query Language | 数据查询语言,用来查询数据库中表的记录 |
DCL | Data Control Language | 数据控制语言,用来创建数据库用户、控制数据库的访问权限 |
2. DDL语句
数据定义语言
A. 操作数据库
- 查询所有数据库:
show databases;
- 查询当前数据库:
select database();
- 使用数据库:
use 数据库名;
- 创建数据库:
create database [if not exists] 数据库名;
- 删除数据库:
drop database[if exists] 数据库名;
B. 表操作-创建
create table 表名(字段1 字段类型[约束][comment 字段1注释],...字段n 字段类型[约束][comment 字段n注释]
)[comment 表注释];
示例:
create table tb_user
(id int comment 'ID,唯一标识',username varchar(20) comment '用户名',name varchar(10) comment '姓名',age int comment '年龄',gender char(1) comment '性别'
)comment '用户表';
C. 约束
- 概念:约束是作用于表中字段上的规则,用于限制存储在表中的数据。
- 目的:保证数据库中数据的正确性、有效性和完整性。
约束 | 描述 | 关键字 |
---|---|---|
非空约束 | 限制该字段值不能为null | not null |
唯一约束 | 保证字段的所有数据都是唯一、不重复的 | unique |
主键约束 | 主键是一行数据的唯一标识,要求非空且唯一 | primary key |
默认约束 | 保存数据时,如果未指定该字段值,则采用默认值 | default |
外键约束 | 让两张表的数据建立连接,保证数据的一致性和完整性 | foreign key |
create table tb_user
(id int primary key comment 'ID,唯一标识',username varchar(20) not null unique comment '用户名',name varchar(10) not null comment '姓名',age int comment '年龄',gender char(1) default '男' comment '性别'
)comment '用户表';
D. 数据类型
三大类数据类型:数值类型
,字符串类型
,日期类型
数值类型
类型 | 描述 | 存储大小 | 取值范围 |
---|---|---|---|
TINYINT | 小整数 | 1字节 | -128 到 127(有符号) / 0 到 255(无符号) |
SMALLINT | 较小的整数 | 2字节 | -32,768 到 32,767(有符号) / 0 到 65,535(无符号) |
MEDIUMINT | 中等大小的整数 | 3字节 | -8,388,608 到 8,388,607(有符号) / 0 到 16,777,215(无符号) |
INT | 标准整数 | 4字节 | -2,147,483,648 到 2,147,483,647(有符号) / 0 到 4,294,967,295(无符号) |
BIGINT | 大整数 | 8字节 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807(有符号) / 0 到 18,446,744,073,709,551,615(无符号) |
FLOAT | 单精度浮动点数 | 4字节 | -3.402823466E+38 到 3.402823466E+38(可选精度) |
DOUBLE | 双精度浮动点数 | 8字节 | -1.7976931348623157E+308 到 1.7976931348623157E+308 |
DECIMAL | 精确的定点数(用于高精度运算) | 根据定义大小 | 精确存储数字,指定精度和小数位数,例如 DECIMAL(10,2) |
日期和时间类型
类型 | 描述 | 存储大小 | 取值范围 |
---|---|---|---|
DATE | 存储日期(年-月-日) | 3字节 | ‘1000-01-01’ 到 ‘9999-12-31’ |
TIME | 存储时间(时:分:秒) | 3字节 | ‘-838:59:59’ 到 ‘838:59:59’ |
DATETIME | 存储日期和时间(年-月-日 时:分:秒) | 8字节 | ‘1000-01-01 00:00:00’ 到 ‘9999-12-31 23:59:59’ |
TIMESTAMP | 存储日期和时间,自动记录插入/更新时的时间 | 4字节 | ‘1970-01-01 00:00:01’ 到 ‘2038-01-19 03:14:07’ |
YEAR | 存储年份(四位数) | 1字节 | 1901 到 2155 |
字符串类型
类型 | 描述 | 存储大小 | 取值范围 |
---|---|---|---|
CHAR(n) | 固定长度字符串 | n 字节(固定长度) | 存储 n 个字符(最大 255) |
VARCHAR(n) | 可变长度字符串 | n 字节(实际长度) | 存储 n 个字符(最大 65,535) |
TEXT | 长文本(适用于较大的文本数据) | 2 字节 + 数据长度 | 最大 65,535 个字符 |
TINYTEXT | 极小文本(适用于较小的文本数据) | 1 字节 + 数据长度 | 最大 255 个字符 |
MEDIUMTEXT | 中等大小文本 | 3 字节 + 数据长度 | 最大 16,777,215 个字符 |
LONGTEXT | 超大文本 | 4 字节 + 数据长度 | 最大 4,294,967,295 个字符 |
BLOB | 二进制大对象(用于存储二进制数据,如图片) | 2 字节 + 数据长度 | 最大 65,535 字节(与 TEXT 类似) |
TINYBLOB | 极小二进制对象 | 1 字节 + 数据长度 | 最大 255 字节 |
MEDIUMBLOB | 中等大小二进制对象 | 3 字节 + 数据长度 | 最大 16,777,215 字节 |
LONGBLOB | 超大二进制对象 | 4 字节 + 数据长度 | 最大 4,294,967,295 字节 |
E. 表结构-查询修改删除
一般都是用图形化页面工具
查询
- 查询当前数据库所有表:
show tables;
- 查询表结构:
desc 表名;
- 查询建表语句:
show create table 表名;
修改
- 添加字段:
alter table 表名 add 字段名 类型(长度)[comment 注释][约束];
- 修改字段类型:
alter table 表名 modify 字段名 新数据类型(长度);
- 修改字段名和字段类型:
alter table 表名 change 旧字段名 新字段名 类型(长度)[comment注释][约束];
- 删除字段:
alter table 表名 drop column 字段名;
- 修改表名:
rename table 表名 to 新表名;
删除
drop table [if exists] 表名;
3. DML语句
数据操作语言,用来对数据库中表的数据记录进行增删改操作
添加INSERT
,修改UPDATE
,删除DELETE
A. 添加-INSERT
- 指定字段添加数据:
insert into 表名(字段名1,字段名2)values(值1,值2);
- 全部字段添加数据:
insert into 表名 values(值1,值2,…);
- 批量添加数据(指定字段):
insert into 表名(字段名1,字段名2)values(值1,值2),(值1,值2);
- 批量添加数据(全部字段):
insert into 表名 values(值1,值2,…),(值1,值2, …. );
-- 向tb_emp表的username、name、gender字段插入数据
insert into tb_emp(username, name, gender, create_time, update_time) values ('wuji', '张无忌', 1, now(), now());-- 向tb_emp表的所有字段插入数据
insert into tb_emp(id, username, password, name, gender, image, job, entrydate, create_time, update_time)values (null, 'zhirou', '123', '周芷若', 2, '1.jpg', 1, '2010-01-01', now(), now());--批量向tb_emp表的username、name、gender字段插入数据
insert into tb_emp(username, name, gender, create_time, update_time)
values ('weifuwang', '韦一笑', 1, now(), now()),('fengzi', '张三疯', 1, now(), now());
B. 修改-UPDATE
语法:update 表名 set 字段名1 = 值1 , 字段名2 = 值2 , .... [where 条件];
-- 将tb_emp表中id为1的员工,姓名name字段更新为’张三’
update tb_emp set name='张三',update_time=now() where id=1;
-- 将tb_emp表的所有员工入职日期更新为’2010-01-01’
update tb_emp set entrydate='2010-01-01',update_time=now();
- 修改语句的条件可以有,也可以没有,如果没有条件,则会修改整张表的所有数据。
C. 删除-DELETE
语法:delete from 表名 [where 条件];
-- 删除tb_emp表中id为1的员工
delete from tb_emp where id = 1;
-- 删除tb_emp表中所有员工
delete from tb_emp;
4. DQL语句
数据查询语言,用于查询数据库表中的记录
A. 语法
SELECT字段列表
FROM表名列表
WHERE条件列表
GROUP BY分组字段列表
HAVING分组后条件列表
ORDER BY排序字段列表
LIMIT分页参数
B. 基本查询
- 查询多个字段
select 字段1, 字段2, 字段3 from 表名;
- 查询所有字段(通配符)
select * from 表名;
- 设置别名
select 字段1 [ as 别名1 ] , 字段2 [ as 别名2 ] from 表名;
- 去除重复记录
select distinct 字段列表 from 表名;
案例:
-- 查询指定字段 name,entrydate并返回
select name,entrydate from tb_emp;-- 查询返回所有字段
select * from tb_emp;-- 查询所有员工的 name,entrydate,并起别名(姓名、入职日期)
-- 方式1:
select name AS 姓名, entrydate AS 入职日期 from tb_emp;
-- 方式2: 别名中有特殊字符时,使用''或""包含
select name AS '姓 名', entrydate AS '入职日期' from tb_emp;
-- 方式3:
select name AS "姓名", entrydate AS "入职日期" from tb_emp;-- 查询已有的员工关联了哪几种职位(不要重复)
select distinct job from tb_emp;
C. 条件查询
语法:
select 字段列表 from 表名 where 条件列表 ; -- 条件列表:意味着可以有多个条件
比较运算符 | 功能 |
---|---|
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
= | 等于 |
<> 或 != | 不等于 |
between … and … | 在某个范围之内(含最小、最大值) |
in(…) | 在in之后的列表中的值,多选一 |
like 占位符 | 模糊匹配(_匹配单个字符, %匹配任意个字符) |
is null | 是null |
逻辑运算符 | 功能 |
---|---|
and 或 && | 并且 (多个条件同时成立) |
or 或 | 或者 (多个条件任意一个成立) |
not 或 ! | 非 , 不是 |
-- 查询 姓名 为 杨逍 的员工
select id, username, password, name, gender, image, job, entrydate, create_time, update_time
from tb_emp
where name = '杨逍';-- 查询 id小于等于5 的员工信息
select id, username, password, name, gender, image, job, entrydate, create_time, update_time
from tb_emp
where id <=5;-- 查询 没有分配职位 的员工信息
select id, username, password, name, gender, image, job, entrydate, create_time, update_time
from tb_emp
where job is null ;-- 查询 密码不等于 ‘123456’ 的员工信息
select id, username, password, name, gender, image, job, entrydate, create_time, update_time
from tb_emp
where password != '123456';-- 查询 入职日期 在 ‘2000-01-01’ (包含) 到 ‘2010-01-01’(包含) 之间的员工信息
-- 方式1:
select id, username, password, name, gender, image, job, entrydate, create_time, update_time
from tb_emp
where entrydate>='2000-01-01' and entrydate<='2010-01-01';
-- 方式2: between...and
select id, username, password, name, gender, image, job, entrydate, create_time, update_time
from tb_emp
where entrydate between '2000-01-01' and '2010-01-01';-- 查询 职位是2 (讲师), 3 (学工主管), 4 (教研主管)的员工信息
-- 方式1:使用or连接多个条件
select id, username, password, name, gender, image, job, entrydate, create_time, update_time
from tb_emp
where job=2 or job=3 or job=4;
-- 方式2:in关键字
select id, username, password, name, gender, image, job, entrydate, create_time, update_time
from tb_emp
where job in (2,3,4);-- 查询姓名为两个字的员工信息
select id, username, password, name, gender, image, job, entrydate, create_time, update_time
from tb_emp
where name like '__'; -- 通配符 "_" 代表任意1个字符-- 查询姓‘张’的员工信息
select id, username, password, name, gender, image, job, entrydate, create_time, update_time
from tb_emp
where name like '张%'; -- 通配符 "%" 代表任意个字符(0个 ~ 多个)
D. 聚合函数
之前我们做的查询都是横向查询,就是根据条件一行一行的进行判断,而使用聚合函数查询就是纵向查询,它是对一列的值进行计算,然后返回一个结果值。(将一列数据作为一个整体,进行纵向计算)
select 聚合函数(字段列表) from 表名 ;
聚合函数 | 功能 |
---|---|
count | 统计数量,按照列去统计有多少行的数据 |
max | 最大值,计算指定列的最大值 |
min | 最小值,计算指定列的最小值 |
avg | 平均值,计算指定列的平均值 |
sum | 求和,计算指定列的数值和,如果不是数值类型,那么计算结果为0 |
-- 统计该企业员工数量
-- count(字段)
select count(id) from tb_emp;-- 结果:29
select count(job) from tb_emp;-- 结果:28 (聚合函数对NULL值不做计算)
-- count(常量)
select count(0) from tb_emp;
select count('A') from tb_emp;
-- count(*) 推荐此写法(MySQL底层进行了优化)
select count(*) from tb_emp;-- 统计该企业最早入职的员工
select min(entrydate) from tb_emp;-- 统计该企业最迟入职的员工
select max(entrydate) from tb_emp;-- 统计该企业员工 ID 的平均值
select avg(id) from tb_emp;-- 统计该企业员工的 ID 之和
select sum(id) from tb_emp;
E. 分组查询
- 分组其实就是按列进行分类(指定列下相同的数据归为一类),然后可以对分类完的数据进行合并计算。
- 分组查询通常会使用聚合函数进行计算。
select 字段列表 from 表名 [where 条件] group by 分组字段名 [having 分组后过滤条件];
-- 根据性别分组 , 统计男性和女性员工的数量
select gender, count(*)
from tb_emp
group by gender; -- 按照gender字段进行分组(gender字段下相同的数据归为一组)-- 查询入职时间在 ‘2015-01-01’ (包含) 以前的员工 , 并对结果根据职位分组 , 获取员工数量大于等于2的职位
select job, count(*)
from tb_emp
where entrydate <= '2015-01-01' -- 分组前条件
group by job -- 按照job字段分组
having count(*) >= 2; -- 分组后条件
- where与having区别(面试题)
- 执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组之后对结果进行过滤。
- 判断条件不同:where不能对聚合函数进行判断,而having可以。
- 执行顺序:where > 聚合函数 > having
F. 排序查询
有升序排序,也有降序排序。
语法:
select 字段列表
from 表名
[where 条件列表]
[group by 分组字段]
order by 字段1 排序方式1 , 字段2 排序方式2 … ;
排序方式:
- ASC :升序(默认值)
- DESC:降序
-- 根据入职时间对公司的员工进行升序排序,入职时间相同,再按照更新时间进行降序排序
select id, username, password, name, gender, image, job, entrydate, create_time, update_time
from tb_emp
order by entrydate ASC , update_time DESC;
G. 分页查询
select 字段列表 from 表名 limit 起始索引, 查询记录数 ;
-- 从起始索引0开始查询员工数据, 每页展示5条记录
select id, username, password, name, gender, image, job, entrydate, create_time, update_time
from tb_emp
limit 0 , 5; -- 从索引0开始,向后取5条记录-- 查询 第3页 员工数据, 每页展示5条记录
select id, username, password, name, gender, image, job, entrydate, create_time, update_time
from tb_emp
limit 10 , 5; -- 从索引10开始,向后取5条记录
- 注意事项:
起始索引从0开始。 计算公式 : 起始索引 = (查询页码 - 1)* 每页显示记录数
5. 多表设计
一对多(多对一),多对多,一对一
A. 一对多
1. 设计多表
创建部门表和员工表
create database db03;
use db03;-- 部门表
create table tb_dept
(id int unsigned primary key auto_increment comment '主键ID',name varchar(10) not null unique comment '部门名称',create_time datetime not null comment '创建时间',update_time datetime not null comment '修改时间'
) comment '部门表';-- 员工表
create table tb_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 女',image varchar(300) comment '图像',job tinyint unsigned comment '职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管',entrydate date comment '入职时间',dept_id int unsigned comment '部门ID', -- 员工的归属部门create_time datetime not null comment '创建时间',update_time datetime not null comment '修改时间'
) comment '员工表';
- 一对多关系实现:在数据库表中多的一方,添加字段,来关联属于一这方的主键。
2. 外键约束
- 让两张表的数据建立连接,保证数据的一致性和完整性。
- 对应的关键字:
foreign key
语法
-- 创建表时指定
create table 表名(字段名 数据类型,...[constraint] [外键名称] foreign key (外键字段名) references 主表 (主表列名)
);-- 建完表后,添加外键
alter table 表名 add constraint 外键名称 foreign key(外键字段名) references 主表(主表列名);
通过SQL语句操作,为员工表的dept_id 建立外键约束,来关联部门表的主键。
-- 修改表: 添加外键约束
alter table tb_emp
add constraint fk_dept_id foreign key (dept_id) references tb_dept(id);
3. 物理外键和逻辑外键
物理外键概念:使用foreign key定义外键关联另外一张表。
缺点:
- 影响增、删、改的效率(需要检查外键关系)。
- 仅用于单节点数据库,不适用与分布式、集群场景。
- 容易引发数据库的死锁问题,消耗性能。
逻辑外键概念:在业务层逻辑中,解决外键关联。
- 通过逻辑外键,就可以很方便的解决上述问题。
在现在的企业开发中,很少会使用物理外键,都是使用逻辑外键。 甚至在一些数据库开发规范中,会明确指出禁止使用物理外键 foreign key
B. 一对一
在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)
- 一对一的应用场景: 用户表(基本信息+身份信息)
-- 用户基本信息表
create table tb_user(id int unsigned primary key auto_increment comment 'ID',name varchar(10) not null comment '姓名',gender tinyint unsigned not null comment '性别, 1 男 2 女',phone char(11) comment '手机号',degree varchar(10) comment '学历'
) comment '用户基本信息表';-- 用户身份信息表
create table tb_user_card(id int unsigned primary key auto_increment comment 'ID',nationality varchar(10) not null comment '民族',birthday date not null comment '生日',idcard char(18) not null comment '身份证号',issued varchar(20) not null comment '签发机关',expire_begin date not null comment '有效期限-开始',expire_end date comment '有效期限-结束',user_id int unsigned not null unique comment '用户ID',constraint fk_user_id foreign key (user_id) references tb_user(id)
) comment '用户身份信息表';
C. 多对多
- 学生和老师的关系,一个学生可以有多个授课老师,一个授课老师也可以有多个学生。
- 学生和课程的关系,一个学生可以选修多门课程,一个课程也可以供多个学生选修。
- 中间表包含两个外键,分别关联两方主键
-- 学生表
create table tb_student(id int auto_increment primary key comment '主键ID',name varchar(10) comment '姓名',no varchar(10) comment '学号'
) comment '学生表';-- 课程表
create table tb_course(id int auto_increment primary key comment '主键ID',name varchar(10) comment '课程名称'
) comment '课程表';-- 学生课程表(中间表)
create table tb_student_course(id int auto_increment comment '主键' primary key,student_id int not null comment '学生ID',course_id int not null comment '课程ID',constraint fk_courseid foreign key (course_id) references tb_course (id),constraint fk_studentid foreign key (student_id) references tb_student (id)
)comment '学生课程中间表';
6. 多表查询
指从多张表中查询数据
A. 笛卡尔积
- 笛卡尔乘积是指在数学中,两个集合(A集合和B集合)的所有组合情况。
- 在多表查询时,需要消除无效的笛卡尔积,只保留表关联部分的数据
B. 内连接查询
查询两表或多表中交集部分数据,分为隐式内连接
和显式内连接
- 隐式内连接语法:
select 字段列表 from 表1 , 表2 where 条件 ... ;
- 显式内连接语法:
select 字段列表 from 表1 [ inner ] join 表2 on 连接条件 ... ;
查询员工的姓名及所属的部门名称
-- 隐式内连接实现
select tb_emp.name , tb_dept.name -- 分别查询两张表中的数据
from tb_emp , tb_dept -- 关联两张表
where tb_emp.dept_id = tb_dept.id; -- 消除笛卡尔积-- 显式内连接实现
select tb_emp.name , tb_dept.name
from tb_emp inner join tb_dept
on tb_emp.dept_id = tb_dept.id;
C. 多表查询时给表起别名
tableA as 别名1 , tableB as 别名2 ;
tableA 别名1 , tableB 别名2 ;
select emp.name , dept.name
from tb_emp emp inner join tb_dept dept
on emp.dept_id = dept.id;
D. 外连接查询
外连接分为两种:左外连接
和 右外连接
- 左外连接相当于查询表1(左表)的所有数据,包含表1和表2交集部分
- 右外连接相当于查询表2(右表)的所有数据,包含表1和表2交集部分
左外连接语法
select 字段列表 from 表1 left [ outer ] join 表2 on 连接条件 ... ;
右外连接语法
select 字段列表 from 表1 right [ outer ] join 表2 on 连接条件 ... ;
案例:
-- 查询员工表中所有员工的姓名, 和对应的部门名称
-- 左外连接:以left join关键字左边的表为主表,查询主表中所有数据,以及和主表匹配的右边表中的数据
select emp.name , dept.name
from tb_emp AS emp left join tb_dept AS dept on emp.dept_id = dept.id;-- 查询部门表中所有部门的名称, 和对应的员工名称
-- 右外连接
select dept.name , emp.name
from tb_emp AS emp right join tb_dept AS depton emp.dept_id = dept.id;
E. 子查询(嵌套查询)
SELECT * FROM t1 WHERE column1 = (SELECT column1 FROM t2 ... );
分为
- 标量子查询(子查询结果为单个值[一行一列])
- 列子查询(子查询结果为一列,但可以是多行)
- 行子查询(子查询结果为一行,但可以是多列)
- 表子查询(子查询结果为多行多列[相当于子查询结果是一张表])
- 子查询可以书写的位置:
where之后
,from之后
,select之后
1. 标量子查询
子查询返回的结果是单个值(数字、字符串、日期等)
- 查询"教研部"的所有员工信息
-- 1.查询"教研部"部门ID
select id from tb_dept where name = '教研部'; --查询结果:2
-- 2.根据"教研部"部门ID, 查询员工信息
select * from tb_emp where dept_id = 2;-- 合并出上两条SQL语句
select * from tb_emp where dept_id = (select id from tb_dept where name = '教研部');
2. 列子查询
子查询返回的结果是一列(可以是多行)
操作符 | 描述 |
---|---|
IN | 在指定的集合范围之内,多选一 |
NOT IN | 不在指定的集合范围之内 |
- 查询"教研部"和"咨询部"的所有员工信息
-- 1.查询"销售部"和"市场部"的部门ID
select id from tb_dept where name = '教研部' or name = '咨询部'; -- 查询结果:3,2
-- 2.根据部门ID, 查询员工信息
select * from tb_emp where dept_id in (3,2);-- 合并以上两条SQL语句
select * from tb_emp where dept_id in (select id from tb_dept where name = '教研部' or name = '咨询部');
3. 行子查询
子查询返回的结果是一行(可以是多行)
常用的操作符:= 、<> 、IN 、NOT IN
- 查询与"韦一笑"的入职日期及职位都相同的员工信息
-- 查询"韦一笑"的入职日期 及 职位
select entrydate , job from tb_emp where name = '韦一笑'; -- 查询结果: 2007-01-01 , 2
-- 查询与"韦一笑"的入职日期及职位相同的员工信息
select * from tb_emp where (entrydate,job) = ('2007-01-01',2);-- 合并以上两条SQL语句
select * from tb_emp where (entrydate,job) = (select entrydate , job from tb_emp where name = '韦一笑');
4. 表子查询
子查询返回的结果是多行多列,常作为临时表
- 查询入职日期是 “2006-01-01” 之后的员工信息 , 及其部门信息
select * from emp where entrydate > '2006-01-01';select e.*, d.* from (select * from emp where entrydate > '2006-01-01') e left join dept d on e.dept_id = d.id ;
7. 事务
A. 事务的介绍
事务是一组操作的集合,它是一个不可分割的工作单位。事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
- 事务作用:保证
在一个事务中多次操作数据库表中数据时
,要么全都成功,要么全都失败
。
B. 事务的操作
SQL语句 | 描述 |
---|---|
start transaction; / begin ; | 开启手动控制事务 |
commit; | 提交事务 |
rollback; | 回滚事务 |
-- 开启事务
start transaction ;-- 删除学工部
delete from tb_dept where id = 1;
-- 删除学工部的员工
delete from tb_emp where dept_id = 1;-- 提交事务 (成功时执行)
commit ;-- 回滚事务 (出错时执行)
rollback ;
C. 事务的四大特性(ACID)
- 原子性(Atomicity):事务是不可分割的最小单元,要么全部成功,要么全部失败。
- 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
- 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
- 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
8. 索引
A. 索引的介绍
索引是帮助数据库高效获取数据
的数据结构
,使用索引可以提高查询的效率
优点:
提高数据查询的效率
,降低数据库的IO成本。- 通过索引列对数据进行排序,降低数据排序的成本,降低CPU消耗。
缺点:
- 索引会
占用存储空间
。 - 索引大大提高了查询效率,同时却也
降低了insert、update、delete的效率
。
B. 索引的数据结构
MySQL中用B+树索引
数据结构,B+Tree(多路平衡搜索树)
B+Tree结构:
- 每一个节点,可以存储多个key(有n个key,就有n个指针)
- 节点分为:叶子节点、非叶子节点
- 叶子节点,就是最后一层子节点,所有的数据都存储在叶子节点上
- 非叶子节点,不是树结构最下面的节点,用于索引数据,存储的的是:key+指针
- 为了提高范围查询效率,叶子节点形成了一个双向链表,便于数据的排序及区间范围查询
C. 语法
- 创建索引
create [ unique ] index 索引名 on 表名 (字段名,... ) ;
- 查看索引
show index from 表名;
- 删除索引
drop index 索引名 on 表名;
案例:
-- 为tb_emp表的name字段建立一个索引
create index idx_emp_name on tb_emp(name);-- 查询 tb_emp 表的索引信息
show index from tb_emp;-- 删除 tb_emp 表中name字段的索引
drop index idx_emp_name on tb_emp;
五. MyBatis
1. MyBatis简介
MyBatis就是用Java程序操作数据库
- application.properties
//驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
//数据库连接的url
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
//连接数据库的用户名
spring.datasource.username=root
//连接数据库的密码
spring.datasource.password=1234
- Mapper接口(编写SQL语句)
@Mapper
public interface UserMapper {@Select("select id, name, age, gender, phone from user")public List<User> list();
}
2. JDBC(了解)
就是使用Java语言操作关系型数据库的一套API
- 使用SpringBoot+Mybatis的方式操作数据库,能够提升开发效率、降低资源浪费
3. 数据库连接池
A. 数据库连接池介绍
- 数据库连接池是个容器,负责分配、管理数据库连接(Connection)
- 允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
- 释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏
- 数据库连接池的好处:
- 资源重用
- 提升系统响应速度
- 避免数据库连接遗漏
B. 实现数据库连接池
官方(sun)提供了数据库连接池标准(javax.sql.DataSource接口)
功能:获取连接
public Connection getConnection() throws SQLException;
想把默认的数据库连接池切换为Druid数据库连接池
- 在pom.xml文件中引入依赖
<dependency><!-- Druid连接池依赖 --><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version>
</dependency>
- 在application.properties中引入数据库连接配置
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql://localhost:3306/mybatis
spring.datasource.druid.username=root
spring.datasource.druid.password=1234
4. lombok工具包
Lombok是一个实用的Java类库,可以通过简单的注解来简化和消除一些必须有但显得很臃肿的Java代码
注解 | 作用 |
---|---|
@Getter/@Setter | 为所有的属性提供get/set方法 |
@ToString | 会给类自动生成易阅读的toString方法 |
@EqualsAndHashCode | 根据类所拥有的非静态字段自动重写 equals 方法和 hashCode 方法 |
@Data | 提供了更综合的生成代码功能(@Getter + @Setter + @ToString+@EqualsAndHashCode) |
@NoArgsConstructo | 为实体类生成无参的构造器方法 |
@AllArgsConstructor | 为实体类生成除了static修饰的字段之外带有各参数的构造器方法 |
在pom.xml文件中引入依赖(lombok)
<!-- 在springboot的父工程中,已经集成了lombok并指定了版本号,故当前引入依赖时不需要指定version -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>
5. Mybatis基础操作
A. 准备
准备工作
B. 删除
1. 功能实现
根据主键删除数据
- 接口方法
@Mapper
public interface EmpMapper {@Delete("delete from emp where id = #{id}") //使用#{key}方式获取方法中的参数值public void delete(Integer id);
}
- 在单元测试类中通过@Autowired注解注入EmpMapper类型对象
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {@Autowired //从Spring的IOC容器中,获取类型是EmpMapper的对象并注入private EmpMapper empMapper;@Testpublic void testDel(){//调用删除方法empMapper.delete(16);}
}
2. MyBatis的日志输出
- 打开application.properties文件
- 开启mybatis的日志,并指定输出到控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
3. 预编译SQL
- 性能更高
- 更安全(防止SQL注入)
在项目开发中,建议使用#{}
,生成预编译SQL,防止SQL注入安全。
C. 新增
1. 功能实现
- 接口方法
@Mapper
public interface EmpMapper {@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")public void insert(Emp emp);}
- 测试类
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {@Autowiredprivate EmpMapper empMapper;@Testpublic void testInsert(){//创建员工对象Emp emp = new Emp();emp.setUsername("tom");emp.setName("汤姆");emp.setImage("1.jpg");emp.setGender((short)1);emp.setJob((short)1);emp.setEntrydate(LocalDate.of(2000,1,1));emp.setCreateTime(LocalDateTime.now());emp.setUpdateTime(LocalDateTime.now());emp.setDeptId(1);//调用添加方法empMapper.insert(emp);}
}
2. 主键返回
在数据添加成功后,需要获取插入数据库数据的主键
如果我们想要拿到主键值,需要在Mapper接口中的方法上添加一个Options注解
@Options(useGeneratedKeys = true,keyProperty = "id")
代码实现:
@Mapper
public interface EmpMapper {//会自动将生成的主键值,赋值给emp对象的id属性@Options(useGeneratedKeys = true,keyProperty = "id")@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")public void insert(Emp emp);
}
D. 更新(修改)
- 接口方法:
@Mapper
public interface EmpMapper {/*** 根据id修改员工信息* @param emp*/@Update("update emp set username=#{username}, name=#{name}, gender=#{gender}, image=#{image}, job=#{job}, entrydate=#{entrydate}, dept_id=#{deptId}, update_time=#{updateTime} where id=#{id}")public void update(Emp emp);
}
- 测试类:
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {@Autowiredprivate EmpMapper empMapper;@Testpublic void testUpdate(){//要修改的员工信息Emp emp = new Emp();emp.setId(23);emp.setUsername("songdaxia");emp.setPassword(null);emp.setName("老宋");emp.setImage("2.jpg");emp.setGender((short)1);emp.setJob((short)2);emp.setEntrydate(LocalDate.of(2012,1,1));emp.setCreateTime(null);emp.setUpdateTime(LocalDateTime.now());emp.setDeptId(2);//调用方法,修改员工数据empMapper.update(emp);}
}
E. 查询
1. 根据ID查询
- 接口方法:
@Mapper
public interface EmpMapper {@Select("select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp where id=#{id}")public Emp getById(Integer id);
}
- 测试类:
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {@Autowiredprivate EmpMapper empMapper;@Testpublic void testGetById(){Emp emp = empMapper.getById(1);System.out.println(emp);}
}
- 而在测试的过程中,我们会发现有几个字段(deptId、createTime、updateTime)是没有数据值的
2. 数据封装
- 实体类属性名和数据库表查询返回的字段名一致,mybatis会自动封装。
- 如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装。
- 起别名:在SQL语句中,对不一样的列名起别名,别名和实体类属性名一样。
@Select("select id, username, password, name, gender, image, job, entrydate, dept_id deptld, create_time createTime, update_time updateTime from emp where id = #{id} ")
public Emp getByld(Integer id);
- 手动结果映射:通过@Results及@Result进行手动结果映射。
@Select("select * from emp where id = #{id}")
@Results({@Result(column ="dept_id", property = "deptld"),@Result(column ="create_time", property = "createTime"),@Result(column ="update_time", property = "updateTime")})
public Emp getByld(Integer id);
- 开启驼峰命名(推荐):如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射
//在application.properties中添加:
mybatis.configuration.map-underscore-to-camel-case=true
3. 条件查询
解决SQL注入风险,使用MySQL提供的字符串拼接函数:concat('%' , '关键字' , '%')
@Mapper
public interface EmpMapper {@Select("select * from emp " +"where name like concat('%',#{name},'%') " +"and gender = #{gender} " +"and entrydate between #{begin} and #{end} " +"order by update_time desc")public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);
}
6. XML映射文件
Mybatis的开发有两种方式:
- 注解
- XML
在Mybatis中使用XML映射文件方式开发,需要符合一定的规范:
- XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)
- XML映射文件的namespace属性为Mapper接口全限定名一致
- XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper"><!--查询操作--><select id="list" resultType="com.itheima.pojo.Emp">select * from empwhere name like concat('%',#{name},'%')and gender = #{gender}and entrydate between #{begin} and #{end}order by update_time desc</select>
</mapper>
MybatisX
是一款基于IDEA的快速开发Mybatis的插件
7. MyBatis动态SQL
SQL语句会随着用户的输入或外部条件的变化而变化,我们称为:动态SQL
A. <if>
<if>
用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL<where>
:只会在子元素有内容的情况下才插入where子句,而且会自动去除子句的开头的AND或OR<set>
:动态地在行首插入 SET 关键字,并会删掉额外的逗号。(用在update语句中)
<if test="条件表达式">//要拼接的sql语句
</if>
- 原来的SQL语句
<select id="list" resultType="com.itheima.pojo.Emp">select * from empwhere name like concat('%',#{name},'%')and gender = #{gender}and entrydate between #{begin} and #{end}order by update_time desc
</select>
- 动态SQL语句
<select id="list" resultType="com.itheima.pojo.Emp">select * from emp<where><!-- if做为where标签的子元素 --><if test="name != null">and name like concat('%',#{name},'%')</if><if test="gender != null">and gender = #{gender}</if><if test="begin != null and end != null">and entrydate between #{begin} and #{end}</if></where>order by update_time desc
</select>
测试:
@Test
public void testList(){//只有性别List<Emp> list = empMapper.list(null, (short)1, null, null);for(Emp emp : list){System.out.println(emp);}
}
B. foreach
员工删除功能(既支持删除单条记录,又支持批量删除)
使用<foreach>
遍历deleteByIds方法中传递的参数ids集合
<foreach collection="集合名称" item="集合遍历出来的元素/项" separator="每一次遍历使用的分隔符" open="遍历开始前拼接的片段" close="遍历结束后拼接的片段">
</foreach>
<delete id="deleteByIds">delete from emp where id in<foreach collection="ids" item="id" separator="," open="(" close=")">#{id}</foreach>
</delete>
C. sql&include
我们可以对重复的代码片段进行抽取
,将其通过<sql>
标签封装到一个SQL片段,然后再通过<include>
标签进行引用。
<sql>
:定义可重用的SQL片段<include>
:通过属性refid,指定包含的SQL片段
- SQL片段: 抽取重复的代码
<sql id="commonSelect">select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp
</sql>
- 通过
<include>
标签在原来抽取的地方进行引用
<select id="list" resultType="com.itheima.pojo.Emp"><include refid="commonSelect"/><where><if test="name != null">name like concat('%',#{name},'%')</if><if test="gender != null">and gender = #{gender}</if><if test="begin != null and end != null">and entrydate between #{begin} and #{end}</if></where>order by update_time desc
</select>
六. 事务管理
1. 事务回顾
事务是一组操作的集合,它是一个不可分割的工作单位。事务会把所有的操作作为一个整体,一起向数据库提交或者是撤销操作请求。所以这组操作要么同时成功,要么同时失败
。
事务的操作主要有三步:
- 开启事务(一组操作开始前,开启事务):start transaction / begin;
- 提交事务(这组操作全部成功后,提交事务):commit ;
- 回滚事务(中间任何一个操作出现异常,回滚事务):rollback ;
2. Spring事务管理
Transactional注解
@Transactional作用:就是在当前这个方法执行开始之前来开启事务,方法执行完毕之后提交事务
。如果在这个方法执行的过程当中出现了异常,就会进行事务的回滚操作
@Transactional注解:我们一般会在业务层当中来控制事务
,因为在业务层当中,一个业务功能可能会包含多个数据访问的操作。在业务层来控制事务,我们就可以将多个数据访问操作控制在一个事务范围内。
@Transactional注解书写位置:
- 方法:当前方法交给spring进行事务管理
- 类:当前类中所有的方法都交由spring进行事务管理
- 接口:接口下所有的实现类当中所有的方法都交给spring 进行事务管理
@Slf4j
@Service
public class DeptServiceImpl implements DeptService {@Autowiredprivate DeptMapper deptMapper;@Autowiredprivate EmpMapper empMapper;@Override@Transactional //当前方法添加了事务管理public void delete(Integer id){//根据部门id删除部门信息deptMapper.deleteById(id);//模拟:异常发生int i = 1/0;//删除部门下的所有员工信息empMapper.deleteByDeptId(id); }
}
可以在application.yml配置文件中开启事务管理日志,这样就可以在控制看到和事务相关的日志信息了
#spring事务管理日志
logging:level:org.springframework.jdbc.support.JdbcTransactionManager: debug
3. rollbackFor(异常回滚)
在Spring的事务管理中,默认只有运行时异常 RuntimeException
才会回滚。如果还需要回滚指定类型的异常,可以通过rollbackFor
属性来指定。
@Slf4j
@Service
public class DeptServiceImpl implements DeptService {@Autowiredprivate DeptMapper deptMapper;@Autowiredprivate EmpMapper empMapper;@Override@Transactional(rollbackFor=Exception.class)public void delete(Integer id){//根据部门id删除部门信息deptMapper.deleteById(id);//模拟:异常发生int num = id/0;//删除部门下的所有员工信息empMapper.deleteByDeptId(id); }
}
4. propagation
A. 事务传播行为
就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。
例如:两个事务方法,一个A方法,一个B方法。在这两个方法上都添加了@Transactional注解,就代表这两个方法都具有事务,而在A方法当中又去调用了B方法。
@Transactional (propagation = Propagation.REQUIRED
public void b () {.....
}
常用属性值 | 含义 |
---|---|
REQUIRED | 【默认值】需要事务,有则加入,无则创建新事务 |
REQUIRES_NEW | 需要新事务,无论有无,总是创建新事务 |
- REQUIRED :大部分情况下都是用该传播行为即
- REQUIRES_NEW :当我们不希望事务之间相互影响时,可以使用该传播行为。比如:下订单前需要记录日志,不论订单保存成功与否,都需要保证日志记录能够记录成功。
七. AOP
1. AOP基础
A. AOP介绍
面向特定方法编程
-
AOP常见的应用场景如下:
- 记录系统的操作日志
- 权限控制
- 事务管理
-
AOP面向切面编程的一些优势:
- 代码无侵入:没有修改原始的业务方法,就已经对原始的业务方法进行了功能的增强或者是功能的改变
- 减少了重复代码
- 提高开发效率
- 维护方便
B. AOP核心概念
- 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)
- 通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)
- 切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用
- 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)
- 目标对象:Target,通知所应用的对象
Spring的AOP底层是基于动态代理技术来实现的,也就是说在程序运行的时候,会自动的基于动态代理技术为目标对象生成一个对应的代理对象。在代理对象当中就会对目标对象当中的原始方法进行功能的增强。
2. AOP进阶
A. 通知类型
Spring中AOP的通知类型:
- @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行
- @Before:前置通知,此注解标注的通知方法在目标方法前被执行
- @After :后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
- @AfterReturning : 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
- @AfterThrowing : 异常后通知,此注解标注的通知方法发生异常后执行
@Slf4j
@Component
@Aspect
public class MyAspect1 {//切入点方法(公共的切入点表达式)@Pointcut("execution(* com.itheima.service.*.*(..))")private void pt(){}//前置通知(引用切入点)@Before("pt()")public void before(JoinPoint joinPoint){log.info("before ...");}//环绕通知@Around("pt()")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {log.info("around before ...");//调用目标对象的原始方法执行Object result = proceedingJoinPoint.proceed();//原始方法在执行时:发生异常//后续代码不在执行log.info("around after ...");return result;}//后置通知@After("pt()")public void after(JoinPoint joinPoint){log.info("after ...");}//返回后通知(程序在正常执行的情况下,会执行的后置通知)@AfterReturning("pt()")public void afterReturning(JoinPoint joinPoint){log.info("afterReturning ...");}//异常通知(程序在出现异常的情况下,执行的后置通知)@AfterThrowing("pt()")public void afterThrowing(JoinPoint joinPoint){log.info("afterThrowing ...");}
}
B. 通知顺序
- 不同的切面类当中,默认情况下通知的执行顺序是与切面类的
类名字母排序
是有关系的 - 可以在切面类上面加上
@Order注解
,来控制不同的切面类通知的执行顺序
C. 切入点表达式
主要用来决定项目中的哪些方法需要加入通知
1. execution
execution主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配,语法为:
execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
(带?的可以省略)
示例:
@Before("execution(void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))")
public void before(JoinPoint joinPoint){}
可以使用通配符描述切入点
*
:单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分..
:多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数
2. @annotation
用于匹配标识有特定注解的方法
@annotation(com.itheima.anno.Log)
@Before("@annotation(com.itheima.anno.Log)")
public void before () {log.info("before..") ;
}
D. 连接点
在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等。
- 对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint类型
- 对于其他四种通知,获取连接点信息只能使用JoinPoint,它是ProceedingJoinPoint的父类型
八. Springboot原理篇
1. 配置优先级
SpringBoot项目当中支持的三类配置文件和两种配置方法:
- application.properties
- application.yml
- application.yaml
- Java系统属性配置 (格式: -Dkey=value)(-Dserver.port=9000)
- 命令行参数 (格式:–key=value)(–server.port=10010)
配置优先级排名(从高到低):
- 命令行参数
- Java系统属性配置
- properties配置文件
- yml配置文件(常用)
- yaml配置文件
2. Bean管理
A. 获取Bean
默认情况下,SpringBoot项目在启动的时候会自动的创建IOC容器(也称为Spring容器),并且在启动的过程当中会自动的将bean对象都创建好,存放在IOC容器当中。应用程序在运行时需要依赖什么bean对象,就直接进行依赖注入就可以了。
而在Spring容器中提供了一些方法,可以主动从IOC容器中获取到bean对象,下面介绍3种常用方式:
- 根据name获取bean
Object getBean(String name)
- 根据类型获取bean
<T> T getBean(Class<T> requiredType)
- 根据name获取bean(带类型转换)
<T> T getBean(String name, Class<T> requiredType)
B. Bean作用域
- 在Spring中支持五种作用域,后三种在web环境才生效:
作用域 | 说明 |
---|---|
singleton | 容器内同名称的bean只有一个实例(单例)(默认) |
prototype | 每次使用该bean时会创建新的实例(非单例) |
- 可以借助Spring中的
@Scope注解
来进行配置作用域
@Scope("prototype")
@RestController
@RequestMapping("/depts")
public class DeptController {}
C. 第三方bean
如果要管理的bean对象来自于第三方(不是自定义的),是无法用@Component 及衍生注解声明bean的,就需要用到@Bean注解。
- 在启动类上添加@Bean标识的方法
@SpringBootApplication
public class SpringbootWebConfig2Application {public static void main(String[] args) {SpringApplication.run(SpringbootWebConfig2Application.class, args);}//声明第三方bean@Bean //将当前方法的返回值对象交给IOC容器管理, 成为IOC容器beanpublic SAXReader saxReader(){return new SAXReader();}
}
- 在配置类中定义@Bean标识的方法
@Configuration //配置类 (在配置类当中对第三方bean进行集中的配置管理)
public class CommonConfig {//声明第三方bean@Bean //将当前方法的返回值对象交给IOC容器管理, 成为IOC容器bean//通过@Bean注解的name/value属性指定bean名称, 如果未指定, 默认是方法名public SAXReader reader(DeptService deptService){System.out.println(deptService);return new SAXReader();}
}
3. SpringBoot原理
A. 起步依赖
原理就是maven的依赖传递
B. 自动配置
1. 自动配置的概述
SpringBoot的自动配置就是当Spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。
2. 自动配置的原理
方案一: @ComponentScan组件扫描
@SpringBootApplication
@ComponentScan({"com.itheima","com.example"}) //指定要扫描的包
public class SpringbootWebConfig2Application {
}
方案二:@Import导入
.使用@Import导入的类会被Spring加载到IOC容器中
- 导入普通类,交给IOC管理
- 导入配置类
- 导入ImportSelector接口实现类
- @EnableXxxx注解,封装@Import注解
@Conditional注解:
- 作用:按照一定的条件进行判断,在满足给定条件后才会注册对应的bean对象到Spring的IOC容器中。
- 位置:方法、类
- @Conditional本身是一个父注解,派生出大量的子注解:
- @ConditionalOnClass:判断环境中有对应字节码文件,才注册bean到IOC容器。
- @ConditionalOnMissingBean:判断环境中没有对应的bean(类型或名称),才注册bean到IOC容器。
- @ConditionalOnProperty:判断配置文件中有对应属性和值,才注册bean到IOC容器。
@Bean
@ConditionalOnClass(name="io.jsonwebtoken.Jwts")//当前环境存在指定的这个类时,才声明该bean
public HeaderParser headerParser(){ ... }@Bean
@ConditionalOnMissingBean//当不存在当前类型的bean时,才声明该bean
public HeaderParser headerParser(){ ... }@Bean
@ConditionalOnProperty(name="name",havingValue="itheima")//配置文件中存在对应的属性和值,才注册bean到IOC容器。
public HeaderParser headerParser(){ ... }
4. WEB后端开发总结
九. Maven高级
1. 分模块设计与开发
将项目按照功能拆分为若干个子模块,方便项目的管理维护、扩展,方便模块间的相互调用、资源共享
2. 继承与聚合
A. 继承
1. 继承的介绍
- 概念:继承描述的是两个工程间的关系,与java中的继承相似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承。
- 作用:简化依赖配置、统一管理依赖
- 实现 :
<parent> ..</parent>
2. 继承关系实现
- 创建maven模块tlias-parent,该工程为父工程,设置打包方式pom(默认jar)。
- 在子工程的pom.xml文件中,配置继承关系。
- 在父工程中配置各个工程共有的依赖(子工程会自动继承父工程的依赖)。
3. 版本锁定
在maven中,可以在父工程的pom文件中通过<dependencyManagement>
来统一管理依赖版本。
在父工程中:
<dependencyManagement><dependencies><!-- JWT令牌 --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency> </dependencies>
</dependencyManagement>
<dependencyManagement>
与<dependencies>
的区别是什么?
<dependencies>
是直接依赖,在父工程配置了依赖,子工程会直接继承下来。<dependencyManagement>
是统一管理依赖版本,不会直接依赖,还需要在子工程中引入所需依赖(无需指定版本)
B. 聚合
-
将多个模块组成一个整体,同时进行项目的构建
-
聚合工程:一个不具有业务功能的空工程(有且只有一个pom文件)
-
作用:快速构建项目
-
maven中可以通过
<modules>
设置当前聚合工程所包含的子模块名称
父工程(聚合工程)
<!-- 聚合 -->
<modules><module> .. /tlias-pojo</module><module> .. /tlias-utils</module><module> .. /tlias-web-management</module>
</modules>
C. 继承与聚合的关联
-
作用
- 聚合用于快速构建项目
- 继承用于简化依赖配置、统一管理依赖
-
相同点:
- 聚合与继承的pom.xml文件打包方式均为pom,可以将两种关系制作到同一个pom文件中
- 聚合与继承均属于设计型模块,并无实际的模块内容
-
不同点:
- 聚合是在聚合工程中配置关系,聚合可以感知到参与聚合的模块有哪些
- 继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己
3. 私服
私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务
,用来代理位于外部的中央仓库,用于解决团队内部的资源共享与资源同步问题。
依赖查找顺序:
本地仓库->私服->中央仓库