Day01MySQL入门
1.1数据库介绍
1.什么是数据库?
数据存储的仓库,本质上是一个文件系统,作用:方便管理数据的。
2.数据库管理系统
数据库管理系统(DataBase Management System,DBMS):指一种操作和管理数据库的大型软件。
数据库管理系统--->MySQL软件-->多个仓库--->多张表--->多条记录(数据)
3.操作数据库流程
现在服务器上安装MySQL软件,再在MySQL软件中创建多个数据库,在每个数据库中创建多张表。数据是保存在数据库表中的。
4.实体(java类)和表关系
一个实体对应一张表
一个对象对应一条记录
对象和记录产生映射关系【ORM: Object Relational Mapping】
1.2 常见关系型数据库
1. MYSQL:开源免费的数据库,小型的数据库.已经被Oracle收购了,MySQL6.x版本(商业、社区)也开始收费。
2. Oracle:收费的大型数据库,Oracle公司的产品。Oracle收购SUN公司,收购MYSQL。
3. DB2:IBM公司的数据库产品,收费的。常应用在银行系统中。
4. OceanBase:阿里的大型数据库,性能已超越Oracle全球第一
------------------------------------------------------
5. SQLServer:MicroSoft 公司收费的中型的数据库。C#、.net等语言常使用。
6. SyBase:已经淡出历史舞台。提供了一个非常专业数据建模的工具PowerDesigner。
7. SQLite: 嵌入式的小型数据库,应用在手机端。
1.3 配置
* mysql开机自启动
1. 登录
a.dos命令窗口
1)本地登录
mysql -u用户名 -p密码 注意:-u与用户名之间没有空格 -p与密码之间也没有空格
2)指定ip
mysql -h主机地址 -u用户名 -p密码
示例:mysql -h127.0.0.1 -uroot -proot
b.图形化界面(sqlyog)
2. 退出
exit 或者 quit
1.4SQL概述
1.什么是SQL
结构化查询语言(Structured Query Language)
通过SQL语言完成数据库操作。实现对数据库中的对象(数据库、数据库表、数据库中数据等)进行增删改查
C:create 创建
R:retrieve(read)检索、查询
U:update 修改
D:delete 删除
2.SQL方言
SQL是一套标准,所有的数据库厂商都实现了此标准;但是各自厂商在此标准上增加了特有的语句,这部分内容我们称为方言。比如:MySQL方言注释 #
我们在学习sql语句时,同时要关注每一家数据库特有的方言
3.书写规范
1. sql语句可以单行或多行书写,最后以分号结尾
2. sql语句(在windows平台下)不区分大小写,建议关键字大写
3. 注释
单行
-- 所有数据库厂商支持
# 仅mysql厂商支持(方言)
多行
/* 注释内容 */
4.SQL分类
1. DDL(Data Definition Language)数据定义语言 (命令了解,掌握图形化工具操作)
用来定义数据库对象:数据库,表,列等。关键字:create(创建),drop(删除),alter(修改)等
2. DML(Data Manipulation Language)数据操作语言 (重点)
用来对数据库中表的数据进行增删改。关键字:insert(插入),delete(删除), update(修改)等
3. DQL(Data Query Language) 数据查询语言 (特别重要)
用来查询数据库中表的记录(数据)。关键字:select(查询),from(从哪张表查询), where(指定查询条件)等
4. DCL(Data Control Language)数据控制语言(了解中的了解) 由DBA(数据库管理员)完成DCL
用来定义数据库的访问权限和安全级别,及创建用户。关键字:grant(授权),revoke(撤销权限)等
5. TCL(Transaction Control Language) 事务控制语言
用于控制数据库的事务操作,关键字; commit,savepoint,rollback等
1.5SQL基础操作
进入公司后,数据库和表只创建一次,通常有DBA数据库管理员操作 DataBase Adminisitrator
1.操作数据库(创建库,显示库,修改库,删除库操作)
C:创建
直接创建数据库【掌握】
create database 数据库名称;
创建数据库并指定字符【了解】
create database 数据库名称 charset 字符集名称;
R:查询
查看所有数据库
show databases;
查看建库语句
show create database 数据库名称
U:修改
修改数据库字符集
alter database 数据库名称 charset 新字符集;
D:删除
直接删除数据库
drop database 数据库名称;
2.使用数据库
进入/切换某一个具体的数据库
use 数据库名称;
查看当前所在哪个数据库中
select database();
3.操作表(创建表,显示表,修改表,删除表操作)
C:创建
create table 表名(
列表1 数据类型,
列表2 数据类型,
列表3 数据类型,
...
);
常用数据类型
int:整型
float:浮点型
double:浮点型
decimal:浮点型(保留精准度)
decimal(m,n) 指定范围
m 总长度
n 小数长度
varchar:字符型
varchar(n) 指定容纳多少个字符 1~65535
text:文本型
date:日期类型
datetime:日期时间类型
克隆表
创建表时,可以快速指定另一张表的结构
create table 新表名 like 旧表名;
R:查询
查看所有表
show tables;
查看建表语句
show create table 表名;
查看表结构
desc 表名称;
U:修改
1. 添加一列
alter table 表名 add 列表 数据类型;
2. 修改列类型
alter table 表名 modify 列表 数据类型;
3. 修改列名和类型
alter table 表名 change 旧列表 新列表 新类型;
4. 删除指定列
alter table 表名 drop 列表;
5. 修改表字符集
alter table 表名 charset 字符集;
6. 修改表名
rename table 旧名称 to 新名称;
D:删除
直接删除表
drop table 表名;
1.6DML【重点】
1.添加记录
语法:
insert into 表名(列名1,列名2 ...) values(值1,值2 ...);
注意:
列名和值的数据类型要对应
字符串类型可以使用单双引号,推荐单引号
字符串可以插入一切(任意)类型,MySQL底层实现了隐式转换
个别列名字段名如果跟关键字冲突了,我们可以使用反引号包裹起来
2.蠕虫复制
将一张表的记录,快速复制到另外一张表,完成数据的迁移
要求二张表结构相同
步骤:
create table stu like student;
INSERT INTO stu SELECT * FROM student;
3.修改记录
语法:
update 表名 set 列1=值1,列2=值2 ... [where条件]
注意:
where条件可以不指定,如果没有指定,就是修改所有记录
4.删除记录
语法:
delete from 表名 [where条件] -- 一条条的删除,最终删除所有数据
TRUNCATE TABLE 表名 -- 删除整张表后,再重新创建表
解释:
[] 中的语法内容可以不写,如果没有写where条件,删除所有
1.7DQL简单查询(检索)
1. 格式:
-- 查询展示所有列
select * from 表名;
-- 查询展示指定列
select 列1,列2 ... from 表名;
2. 去重关键字 distinct:去除重复的关键字
select distinct 列表 from 表名;
3. 查询结果进行运算,不会影响原表中的记录
4. null参与数学运算结果还是null
ifnull()函数
ifnull(列名,默认值) 如果该列为有值,则直接返回值,如果该列值为null,则取默认值
例如:ifnull(english,0)
5. 设置查询别名
select 列表 [as] 列别名 from 表名 [as] 表别名
注意:as关键字可以省略
1.8DQL条件查询
1. 格式
select ... from 表名 where条件
2. 关系(比较)运算符
> < >= <= != =
3. 逻辑运算符
&& and(条件同时成功)
|| or(条件满足一个)
! not(条件取反)
4. in关键字(某一个列,查询多个值)
语法:select ... from 表名 where 列名 in(值1,值2,...);
5. between关键字(范围查询)
语法:select ... from 表名 where 列名 between 小值 and 大值;
取值范围包含:两端区间值
6. null值处理
is null 为空条件
is not null 不为空条件
7. like关键字(模糊匹配)
select ... from 表名 where 列表 like '通配符字符串';
_ 匹配单个任意字符
% 匹配多个任意字符
Day02查询、约束、多表
2.1DQL高级查询
1.排序
语法:
select ... from 表名 order by 排序列1 [asc | desc],排序列2 [asc | desc] ...
注意:
order by:排序语法关键字
asc:升序 默认排序方式
desc:降序
当多列进行排序时,先满足前面设置的排序条件,再匹配后面的排序条件
2.聚合函数
作用:
对一列数据进行计算,返回一个结果,忽略null值
语法:
count(列名):统计一列个数--一般为 *
count(IFNULL(列名,默认值))--如果为null则为默认值,否则为原值
max(列名):求出一列的最大值
min(列名):求出一列的最小值
sum(列名):对一列求和
avg(列名):求出一列的平均值
3.分组
作用:
对一列数据进行分组,相同的内容分为一组,通常与聚合函数一起使用,完成统计工作
语法:
select 分组列 from 表名 group by 分组列
where和having区别
where设置的是分组前的过滤条件,不支持聚合函数条件的
having设置的是分组后的过滤条件,支持聚合函数条件的
4.分页
语法:
select ... from 表名 limit 分页起始索引,每页显示的记录数据;
注意:
索引是从0开始,0也是默认值,可以省略
起始索引值=(当前页-1)*每页显示记录数
5.sql语句执行顺序问题
select ... from 表名 where 条件 group by 分组 having 分组后条件 order by 排序条件 limit 分页条件;
2.2数据库约束
1.作用
对表中的数据进行限定,保证数据的正确性、有效性和完整性。
添加约束,尽量让数据库中存储的数据更规范一些。
2.分类
1. primary key:主键约束,要求表中有一个字段唯一且非空,通常使用id作为主键
2. unique:唯一约束
3. not null:非空约束
4. default:默认值
5. foreign key:外键约束
3.主键约束
创建表(掌握)
create table 表名称(
id 数据类型 primary key,
列表1 数据类型,
...
);
已有表
alter table 表名 add primary key(主键列名);
联合主键
PRIMARY KEY(列表1,列表2)
自增器
只有主键约束才有意义设置自增器...(保证唯一性....)
create table 表名(
id 数据类型 primary key auto_increment,--起始值为1
...
);
--可以手动指定自增器起始值
alter table 表名 auto_increment=起始值;
删除主键约束
alter table 表名 drop primary key;
注意:
--先移出自增器
alter table 表名 modify id 数据类型;
--才能删除主键约束
alter table 表名 drop primary key;
4. 唯一约束
作用:
限定某一列的值不能重复,可以出现多个null
语法:
create table 表名(
id int primary key auto_increment,
列表 数据类型 unique,
...
);
5.非空约束
作用:
限定某一列的值不能为null
语法:
create table 表名(
id int primary key auto_increment,
列名1 数据类型 not null,
列名2 数据类型 unique not null,
...
);
5. 默认值
作用:
限定某一列的默认值,再没有指定的情况下所有列的默认值为null
语法:
create table 表名(
id int primary key auto_increment,
列名 数据类型 default 默认值,
...
);
注意:
当手动设置了默认值对应列的内容时(包括null),会覆盖默认值
6.外键约束
作用:
限定二张表有关系的数据,保证数据的正确性、有效性和完整性
传统的项目中我们需要外键约束,互联网项目绝对不用......(性能问题)
互联网项目是通过java代码组装业务逻辑维护表关联关系。。。
语法:创建表
create table 表名(
id int primary key auto_increment,
列表 数据类型,
[constraint] [约束名] foreign key(外键列) references 主表(主键)
);
注意:
constraint 声明外键名称关键字
foreign key:添加外键约束关键字
语法:已有表
alter table 表名称 add [constraint] [约束名] foreign key(外键列) references 主表(主键);
特点:
1、主表不能删除从表引用的数据
2、添加从表数据时,如果关联主表时,主表的数据不存在,也无法添加
3、添加数据时,先添加主表数据,再添加从表数据
4、删除数据时,先删除从表数据,再删除主表数据
5、外键约束时,不能维护不存在的数据
删除外键约束
alter table 表名 drop foreign key 约束名;
2.3表关系
1.介绍
关系型数据库(Relation DBMS)
2.一对多
一方对应的数据库表叫做:主表
多方对应的数据库表叫做:从表
建表时,在从表中,添加一个外键列,指向主表中的主键
3.多对多
可以看作是两个一对多
通过设计一种中间表,维护多对多关系
中间表添加两个外键约束,分别指向主表的主键
可以将中间表的两个外键列作为中间表的联合主键
4.一对一
将一个表中的主键作为外键维护到另一个表的主键
Day03多表、权限、备份
3.1多表查询
1.笛卡尔积
多张表的记录进行组合,这种现象称为笛卡尔积(交叉连接)
select ... from 左表,右表;
2.内连接
拿左表的记录去匹配右表的记录,若符合条件显示(二张表的交集)
隐式内连接
select ... from 左表,右表 where 连接条件;
显示内连接【推荐】
select ... from 左表 [inner] join 右表 on 连接条件;
3.外连接
左外连接
展示左表全部,再去匹配右表记录,若条件符合显示,若条件不符合显示NULL
select ... from 左表 left [outer] join 右表 on 连接条件
右外连接
展示右表全部,再去匹配左表记录,若条件符合显示,若条件不符合显示NULL
select ... from 左表 right [outer] join 右表 on 连接条件
4.子查询(嵌套)
一条select语句执行结果,作为另一条select语法的一部分
子查询结果为单列,肯定作为条件在where后面使用
select ... from 表名 where 字段 in (子查询);
select ... from 表名 where 字段=(子查询);
子查询结果为多列,一般作为虚拟表在from后面使用
select ... from (子查询) as 别名;
注意:
如果能够使用关联查询查到数据同时也能使用子查询查询,此时尽量使用关联查询完成,不要使用子查询。子查询效率低。
3.2用户权限 DCL
1. 创建用户
语法:
create user '用户名'@'主机名' identified by '密码';
注意:
主机名:限定客户端登录ip
指定ip:127.0.0.1 (localhost)
任意ip:%
2. 授权用户
语法:
grant 权限1,权限2... on 数据库名.表名 to '用户名'@'主机名';
注意:
权限:
select、insert、delete、update、create、drop...
all 所有权限
数据库名.* 指定库下面所有的表
3. 查看权限
语法:
show grants for '用户名'@'主机名';
4. 撤销授权
语法:
revoke 权限1,权限2... on 数据库名.表名 from '用户名'@'主机名';
注意:
权限:
select、insert、delete、update、create...
all 所有权限
数据库名.* 指定库下面所有的表
5. 删除用户
语法:
drop user '用户名'@'主机名';
6. 密码管理
1.超级管理员
set password for '用户名'@'主机名'=password('新密码');
2.普通用户
set password=password('新密码');
3.3数据库备份与还原
1.dos命令行
1. 备份
格式:
mysqldump -u用户名 -p 需要备份数据库名 > 导出路径(*.sql)
注意:备份的文件名以.sql结尾
实例:
mysqldump -uroot -p day20_pro > d:bak.sql
缺点:
通过命令备份的只有表结构和数据,没有建库语句...
2. 还原
格式:
mysql -u用户名 -p < 导入路径(*.sql)
实例:
mysql -uroot -p < d:bak.sql
2.图形化工具【sqlyog】
Day04MySQL函数、事务安全TCL
4.1MySQL函数
1.简介
为了简化操作,mysql提供了大量的函数给程序员使用
函数可以出现的位置:插入语句的values()中,更新语句中,删除语句中,查询语句及其子句中。
2.字符串函数
CONCAT(s1,s2...sn) -- 字符串 s1,s2 等多个字符串合并为一个字符串
CHAR_LENGTH(str) -- 返回字符串 str 的字符数
LENGTH(str) -- 返回字符串 s 的字节数
UCASE(s) | UPPER(s) -- 将字符串转换为大写
LCASE(s) | LOWER(s) -- 将字符串转换为小写
LOCATE(s1,s) -- 从字符串 s 中获取 s1 的开始位置(从1开始)
TRIM(str) | LTRIM(str) | RTRIM(str) -- 字符串去空格
REPLACE(s,s1,s2) -- 将字符串 s2 替代字符串 s 中的字符串 s1
SUBSTR(s, start, length) -- 从字符串 s 的 start 位置截取长度为 length 的子字符串(从1开始)
STRCMP(str1,str2) -- 比较字符串大小,返回 1 | 0 | -1
3.日期函数
NOW() | CURDATE() | CURTIME() -- 获取系统当前日期时间、日期、时间
YEAR(DATE) | MONTH(DATE) | DAY(DATE) -- 从日期中选择出年、月、日
LAST_DAY(DATE) -- 返回月份的最后一天
ADDDATE(DATE,n) | SUBDATE(DATE,n) -- 计算起始日期 DATE 加(减) n 天的日期
QUARTER(DATE) -- 返回日期 DATE 是第几季度,返回 1 到 4
DATEDIFF(d1,d2) -- 计算日期 d1->d2 之间相隔的天数
DATE_FORMAT(d,f) -- 按表达式 f的要求显示日期 d
4.数字函数
ABS(x) -- 返回 x 的绝对值
CEIL(x) | FLOOR(x) -- 向上(下)取整
MOD(x,y) -- 返回x/y的余数
RAND() -- 返回 0 到 1 的随机数
ROUND(x) -- 四舍五入
TRUNCATE(x,y) -- 返回数值 x 保留到小数点后 y 位的值
4.2高级函数
1.CASE表达式
SELECT
CASE [字段,值]
WHEN 判断条件1
THEN 希望的到的值1
WHEN 判断条件2
THEN 希望的到的值2
...
ELSE 前面条件都没有满足情况下得到的值
END
FROM
table_name;
2.IF表达式
SELECT IF(1 > 0,'真','假') from 表名;
4.3事务安全 TCL
1.概述
如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败。
事务是一组操作(增删改),要不然同时成功,要不同时失败。
2.手动提交事务
begin 或者 start transaction -- 开启事务
commit -- 提交事务
rollback -- 回滚事务 将数据恢复执行SQL前的状态
3.自动提交事务
show variables like 'autocommit'; -- 查看MySQL是否开启自动提交
set autocommit = off; -- 临时关闭自动提交
4.保存(回滚)点
savepoint 保存点名; -- 设置保存点
rollback to 保存点名; -- 回滚到保存点
4.4事务特性 ACID
1. 原子性:A atomicity
如果业务操作中,包含一组DML(多条DML操作语句)操作,被事务管理,这组操作要么全部成功,要么全部失败。
2. 一致性:C consistency
事务执行前后,保证数据一致。
3. 隔离性:I isolation【面试题重点】
多个事务之间相互独立,互不影响
1. 脏读【必须要避免的....】
一个事务中,读到另一个事务中没有提交的数据。
2. 不可重复读
一个事务中,两次读取内容不一致。在另外一个事务中执行了update(修改)操作。
3. 幻读
一个事务中,两次读取数量不一致。在另外一个事务中执行了添加(insert)或删除(delete)操作。
查看当前数据库隔离级别
show variables like '%isolation%';
临时修改隔离级别
set session transaction isolation level 级别字符串;
4. 持久性:D durability
事务一旦执行成功,数据会持久化到磁盘中
MySQL数据库隔离级别
级别 | 名字 | 隔离级别 | 脏读 | 不可重复读 | 幻读 | 数据库默认隔离级别 |
---|---|---|---|---|---|---|
1 | 读未提交 | read uncommitted | 是 | 是 | 是 | |
2 | 读已提交 | read committed | 否 | 是 | 是 | Oracle和SQL Server |
3 | 可重复读 | repeatable read | 否 | 否 | 是 | MySQL |
4 | 串行化 | serializable | 否 | 否 | 否 |
Day05MySQL索引
5.1MySQL性能
优化数据库,提示查询效率
硬件优化
软优化:通过技术实现提升查询效率。例如:SQL优化、创建索引等
查询密集型
我们使用查询频率较高,8:2 左右
就可以通过创建索引方式,查询数据时,可以减少磁盘IO操作
修改密集型
elasticsearch (全文检索技术)提升数据查询效率
--------------------------------------------------------------
查询数据库CRUD执行次数
show global status like 'Innodb_rows%';
查看慢查询日志开启情况
show variables like '%slow_query_log%';
查看慢查询时间配置
show variables like '%long_query_time%';
查看慢查询日志配置
show variables like '%query%';
开启慢查询日志
set global slow_query_log = on;
设置慢查询sql的时间阈值
set global long_query_time=3; 全局配置(下次生效...)
set session long_query_time=3; 临时(会话)配置(本次会话窗口生效)
注意:
这种方式会记录很多没有必要的日志,反复的IO影响效率,也会占用存储空间
所以这种方式只适合测试阶段,在生产阶段避免使用
5.2MySQL索引
1.索引分类
主键(约束)索引
主键约束+提高查询效率 特点:非空且唯一
唯一(约束)索引 unique
唯一约束+提高查询效率
普通索引
仅提高查询效率
组合(联合)索引
多个字段组成索引
全文索引
solr、es
hash索引
根据key-value 效率非常高
2. 直接创建索引【了解】
create index 索引名 on 表名(字段); -- 创建普通索引
create unique index 索引名 on 表名(字段) -- 创建唯一索引
create index 索引名 on 表名(字段1,字段2 ...); -- 创建普通组合索引
create unique index 索引名 on 表名(字段1,字段2 ...); -- 创建唯一组合索引
3.修改表时指定索引【了解】
alter table 表名 add primary key(字段); -- 添加一个主键
alter table 表名 add unique(字段); -- 添加唯一索引
alter table 表名 add index(字段);-- 添加普通索引
4. 创建表时指定索引【掌握】
列名1 INT PRIMARY KEY AUTO_INCREMENT, -- 添加主键索引
列名2 VARCHAR(11) UNIQUE, -- 添加唯一索引
INDEX(列名3) -- 添加普通索引
5. 删除索引
drop index 索引名 on 表名; -- 直接删除
alter table 表名 drop index 索引名; -- 修改表时删除 【掌握】
5.3索引特点
* 优缺点
优点
提高数据检索的效率,降低数据库的 IO 成本
通过索引列对数据进行排序,降低数据排序的成本,降低 CPU 的消耗。
缺点
索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的
当我们进行增删改操作后,需要重新维护索引,消耗服务资源,降低服务器性能
所以不是所有的字段都适合增加索引
* 创建原则
1. 字段内容可识别度不能低于70%
2. 在经常需要 搜索 的列上建索引,这样会大大加快查找速度
3. 在经常需要 连接 的列上建索引,可以加快连接的速度。
4. 在经常需要 排序 的列上建索引,加快排序查询速度。
* 常见索引失效情况
1.使用like模糊匹配,%通配符在最左侧使用时,会进行全表扫描
2.尽量避免使用or,如果条件有一个没有索引,那么会进行全表扫描
3.在索引列上进行计算
4.使用 !=、 not in、is not null时
5.4索引的数据结构
https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
索引= 排序后的数据结构
1.二叉树
左边的子节点比父节点小,右边的子节点比父节点大
特殊情况下会变成链表结构
2.红黑树:平衡二叉树
左旋和右旋实现自平衡
3.BTree:多路平衡搜索树
4.B+Tree:优化BTree
叶子节点 : 最后一层子节点 , 数据存储在叶子节点
非叶子节点: 中间 存储是索引+指针
一个节点16KB,指针6个字节,键值8个字节
MySQL数据库默认一次读取一页的大小(4个磁盘块) mysql 默认将一级节点加载到内存
show global status like 'innodb_page_size'; -- 查看mysql索引节点大小
一般来说 超过千万级数据 采取分库分表
5.Hash:通过散列算法,不支持范围查询
1. JDK1.7 (数组+链表)
2. JDK1.8 (数组+红黑树) 如果链表长度《=8
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UKJ6cMWV-1605668809265)(assets\7.png)]
5.5数据库的存储引擎
MyISAM(非聚集索引)
MySQL5.5版本之前默认存储引擎
不支持事务、不支持外键约束
查询效率更快
InnoDB(聚集索引)
MySQL5.5版本之后默认存储引擎
支持事务、支持外键约束
增删改效率更快
语法:
CREATE TABLE 表名 (
...
) ENGINE=MyISAM | INNODB;
Day06JDBC基础
6.1概述
1.简介:
Java 数据库连接(Java DataBase Connectivity)
2.作用:
通过Java语言操作数据库
3.本质:
官方(sun公司)定义的一套操作所有关系型数据库的规则(接口)
6.2API介绍
sun公司提供的:java.sql包下
DriverManager:驱动管理对象
1. 注册驱动
1)【了解】
static void registerDriver(Driver driver)
我们通过翻看MySQL Driver实现类的源码发现内部的静态代码已经提供了注册驱动功能
2)反射【掌握】
Class.forName("com.mysql.jdbc.Driver");
2. 建立连接
static Connection getConnection(String url, String user, String password)
url:连接到指定数据库【固定写法】
格式:
jdbc:mysql://ip地址+mysql端口号/数据库名称
jdbc:mysql:///数据库名称(本地连接,并且数据库端口号是3306)
user:用户名
password:密码
Connection:数据库连接对象
1. 获取sql执行对象
Statement createStatement()
PreparedStatement prepareStatement(String sql)
2. 事务管理
1)关闭自动提交(开启事务)
void setAutoCommit(boolean autoCommit)
参数:true:自动提交(默认值)
false:收到提交事务
2)提交事务
void commit()
3)回滚事务
void rollback()
Statement:执行sql的对象
1. 执行所有类型sql语句【了解】
boolean execute(String sql)
如果执行增删改操作,可以使用这个方法,但是如果执行查询操作,返回Boolean意义不大。我们需要返回查询结果。所以,这个方法基本不使用
2. 仅执行DML类型sql语句【掌握】
int executeUpdate(String sql)
返回结果:int 影响的行数
3. 仅执行DQL类型sql语句【掌握】
ResultSet executeQuery(String sql)
返回结果:ResultSet 查询结果集
ResultSet:结果集对象,封装查询结果
1. 指针下移
boolean next() ;
2. 获取数据
T getXxx(int 列编号);
T getXxx(String 列名);
返回所有类型:
Object getObject(String 列表);
6.3CRUD操作
//1、注册驱动
static void registerDriver(Driver driver);//实际注册了两次
Class.forName("com.mysql.jdbc.Driver");
//2、建立连接
static Connection getConnection(String url, String user, String password)
//3、编写SQL
String sql ="...";
//4、获取操作SQL的执行对象
Statement statement = connection.createStatement();
//5、执行SQL获取返回结果
boolean execute(String sql);
int executeUpdate(String sql);
ResultSet executeQuery(String sql);
//6、处理返回结果
while(resultSet.next()){
...
}
//7、释放资源
resultSet.close();
statement.close();
connection.close();
6.4工具类
1.properties文件配置
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/day06
jdbc.username=root
jdbc.password=root
2.工具类
//声明成员变量
...
//加载jdbc.properties配置文件,获取配置信息
static {
ResourceBundle jdbc = ResourceBundle.getBundle("properties文件");
...
}
//2、获取连接
//3、释放资源
//4 重载方法 释放资源
6.5事务操作
* 事务
如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败。
* MySQL操作
1.开启事务
begin | start transaction;
2.提交事务
commit;
3.回滚事务
rollback;
* java操作(使用Connection对象)
1.关闭自动提交(开启手动事务)
void setAutoCommit(boolean autoCommit)
参数:Boolean类型 默认是:true 自动提交事务
如果关闭自动提交事务,设置参数为false
2.提交事务
void commit()
3.回滚事务
void rollback()
Day07PreparedStatement&连接池
7.1PreparedStatement
1.SQL注入问题
用户输入的信息和SQL语句进行字符串拼接,改变了原有SQL真正的意义
2.优点
1、可以防止SQL注入问题,提高安全性
2、减少编译次数,提升执行性能
3、参数和SQL语句分离,代码可读性更好
3.基础语法
//1、注册驱动,获取连接
//2、编写SQL语句,使用占位符替代实际参数
SELECT * FROM USER WHERE username=? and password=?
//3、获取SQL预编译对象,将SQL发送到数据库预编译
PreparedStatement pstmt = connection.prepareStatement(String sql);
//4、设置占位符需要的参数
pstmt.setString(1,"admin");
pstmt.setString(2,"123");
//5、执行SQL语句,并返回结果 注意:此时没有再传到SQL
ResultSet executeQuery();
int executeUpdate();
//6、处理结果
//7、释放资源
7.2连接池
1.概述
连接池其实就是一个容器(集合),存放数据库连接的容器。
当系统初始化好后,容器被创建,容器中会申请一些连接对象
当用户来访问数据库时,从容器中获取连接对象
用户访问完之后,会将连接对象归还给容器。
2.优点
节约资源,减轻服务器压力
提高连接复用性,用户访问高效
3.常见连接池
DBCP:Apache提供的数据库连接池技术。
C3P0:数据库连接池技术,目前使用它的开源项目有Hibernate、Spring等。
HikariCP:小日本开发的连接池技术,性能之王,目前使用它的开源项目有SpringBoot等。
Druid(德鲁伊):阿里巴巴提供的数据库连接池技术,是目前最好的数据库连接池。支持sql日志监控
4.实现
Java为数据库连接池提供了公共的接口: DataSource
功能
获取连接:
Connection getConnection()
归还连接:
connection.close()
底层通过动态代理技术对close()方法进行了增强
7.3Druid连接池
步骤:
//1、创建druid连接池对象
DruidDataSource dataSource = new DruidDataSource();
//2、与mysql建立建立(初始化连接配置),设置四项基本配置
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/day23");
dataSource.setUsername("root");
dataSource.setPassword("root");
//3、从连接池中获取连接对象
Connection connection = dataSource.getConnection();
//4、执行CRUD具体业务...
System.out.println(connection);
//5、归还连接,注意此时不是关闭连接释放资源,而是归还连接到连接池
connection.close();
配置文件:
参数 说明
jdbcUrl 连接数据库的url:mysql : jdbc:mysql://localhost:3306/druid2
username 数据库的用户名
password 数据库的密码
driverClassName 驱动类名。根据url自动识别,这一项可配可不配,如果不配置druid会根据url
自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize 初始化时建立物理连接的个数。初始化发生在显示调用init方法,
或者第一次getConnection时
maxActive 最大连接池数量
minIdle 最小连接池数量
maxWait 获取连接时最大等待时间,单位毫秒。
7.4连接池工具类
//1.初始化连接池
static {
try {
//类加载器对类路径下(src)目录下的配置文件,获取io流
InputStream is = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
//创建properties对象
Properties properties = new Properties();
//读取类路径下的配置文件中的内容
properties.load(is);
//Druid连接池工厂对象创建连接池对象
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//2.获取连接池
//3.获取连接对象
//4、释放资源
//4 重载方法 释放资源
Day08Mybatis入门
8.1Mybatis简介
1.ORM概述:
ORM(object Relational Mapping)对象关系映射
常用ORM框架有:hibernate(全自动ORM映射)、mybatis(半自动ORM映射)、jpa
2.Mybatis介绍:
MyBatis本是apache的一个开源项目,名为iBatis。2010年这个项目由apache迁移到了google,并且改名为MyBatis。2013年迁移到Github。
mybatis是一款优秀的持久层框架,他不需要项JDBC繁琐编写代码,只需要开发人员关注(接口+sql)
采用了简单的xml配置+接口方式实现增删改查,开发时我们只需要关注SQL本身
8.2Mybatis增删改查
1.新增
1、插入语句标签:<insert>
2、parameterType属性指定映射数据类型
3、sql语句通过#{}作为占位符
4、插入操作API:sqlSession.insert("名称空间.id",实体对象);
5、执行DML操作,一定要手动提交事务
代码:
sqlSession.insert("UserMapper.save", user);
映射文件:
<insert id="save" parameterType="com.demo.domain.User">
insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>
-------------------------------------------------------------------
2.修改
1、修改语句标签:<update>
2、parameterType属性指定映射数据类型
3、sql语句通过#{}作为占位符
4、修改操作API:sqlSession.update("名称空间.id",实体对象);
5、执行DML操作,一定要手动提交事务
代码:
sqlSession.update("UserMapper.update", user);
映射文件:
<update id="update" parameterType="com.itheima.domain.User">
update user set username = #{username},birthday = #{birthday},
sex = #{sex},address = #{address} where id = #{id}
</update>
-------------------------------------------------------------------
3.删除
1、删除语句标签:<delete>
2、parameterType属性指定映射数据类型
是引用数据类型:例如:com.demo.domain.User 占位符中的内容是#{实体属性名}
是基本数据类型:例如:int 占位符中的内容是#{键名知意}
3、sql语句通过#{}作为占位符
4、删除操作API:sqlSession.delete("名称空间.id",参数值);
5、执行DML操作,一定要手动提交事务
代码:
sqlSession.delete("UserMapper.delete", 50);
映射文件:
<delete id="delete" parameterType="java.lang.Integer">
delete from user where id = #{id}
</delete>
-------------------------------------------------------------------
4.查询
代码:
List<User> list = sqlSession.selectList("UserMapper.findAll");
映射文件:
<select id="findAll" resultType="com.itheima.domain.User">
select * from user
</select>
8.3Mybatis核心文件概述
- 核心配置文件标签具有固定的顺序
environments标签
<environments default="默认数据源配置名">
<environment id="当前数据源配置名">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_db"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</dataSource>
</environment>
</environments>
1. 其中,事务管理器(transactionManager)类型有两种:
* JDBC:
这个配置就是直接使用了JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
* MANAGED:
这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。
例如:mybatis与spring整合后,事务交给spring容器管理。
2. 其中,数据源(dataSource)常用类型有二种:
* UNPOOLED:
这个数据源的实现只是每次被请求时打开和关闭连接。
* POOLED:
这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来。
properties标签
加载外部的properties文件
<properties resource = "文件名.properties"></properties>
${properties文件的key值}
......
typeAliases标签
为 Java 类型设置一个短的名字,也可自行定义
_int int
int Interger
... ...
<typeAliases type = "全名" alias="简化后的名字">
mappers标签
用于加载映射文件,加载方式有如下几种:
<mappers>
<mapper resource="com/demo/dao/UserMapper.xml"></mapper>
</mappers>
1. 加载指定的src目录下的映射文件。
<mapper resource="com/demo/mapper/UserMapper.xml"/>
2. 加载指定接口的全限定名。【注解开发时使用....】
<mapper class="com.demo.mapper.UserMapper"/>
3. 加载并扫描指定包下所有的接口。【基于接口扫描方式加载】(常用)
<package name="com.demo.mapper"/>
settings标签
全局参数配置
<settings>
<!--开启懒加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 指定触发延迟加载的方法,只有get方法执行时才会触发立即加载 -->
<setting name="lazyLoadTriggerMethods" value=""/>
<!--开启二级缓存 true开启(默认) false关闭-->
<setting name="cacheEnabled" value="true"/>
</settings>
8.4Mybatis的API概述
1.Resources
//加载mybatis的核心配置文件,获取io流
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
2.SqlSessionFactoryBuilder
//根据mybatis的核心配置文件构建出SqlSessionFactory工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
3.SqlSessionFactory
用于创建SqlSession会话对象,是一个工厂对象
这种对象创建和销毁都非常耗费资源,一个项目中只需要存在一个即可
// DML类型语句,需要手动提交事务
SqlSession openSession();
// 设置是否开启自动提交事务的会话对象,如果设置true,自动提交【了解】
SqlSession openSession(boolean autoCommit);
4.SqlSession
这是Mybatis的一个核心对象。我们基于这个对象可以实现对数据的CRUD操作
对于这个对象应做到每个线程独有,每次用时打开,用完关闭
<T> T selectOne(String statement, Object parameter);
<E> List<E> selectList(String statement, Object parameter);
int insert(String statement, Object parameter);
int update(String statement, Object parameter);
int delete(String statement, Object parameter);
5.操作事务的主要方法
void commit();
void roolback();
8.5工作原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5IYf8Lt1-1605668809267)(assets\8.png)]
8.6Mybatis实现Dao层
传统开发方式【了解】
1.编写UserMapper接口
2.编写UserMapperImpl实现类
3.编写UserMapper.xml映射文件
4.编写单元测试功能,模拟service调用dao过程
public void testFindAll() throws Exception{
UserMapper userMapper = new UserMapperImpl();
List<User> list = userMapper.findAll();
for (User user : list) {
System.out.println(user);
}
}
接口代理开发方式【重点】
1.简介
采用 Mybatis 的基于接口代理方式实现 持久层 的开发,这种方式是我们后面进入企业的主流。
基于接口代理方式的开发只需要:编写接口和映射文件,Mybatis 框架会为我们动态生成实现类的对象。
2.接口开发规范
1. Mapper映射文件的namespace与Mapper接口全限定名一致
2. Mapper接口的方法名与id的属性名一致
3. 方法的参数类型与parameterType属性类型一致
4. 方法的返回值类型与resultType属性类型一致
5. 映射文件需要与接口在同一个包下,文件名和接口名相同:扫描包,加载所有的映射文件
步骤
1.编写UserMapper接口
2.编写UserMapper.xml映射文件
3.编写单元测试功能,模拟service调用dao过程
public void testFindAll() throws Exception{
//基于mybatis工具类获取SQLSession对象
SqlSession sqlSession = MybatisUtils.openSession();
//创建UserMapper接口代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//调用查询所有的方法
List<User> list = userMapper.findAll();
for (User user : list) {
System.out.println(user);
}
}
Day09mybatis查询和多表
9.1resultMap标签
UserMapper.xml映射文件
<!--
resultMap标签:手动映射数据库表中的列表和java实体类属性名
id:当前resultMap标签唯一标记
type="com.demo.domain.User" 手动映射的java实体类型
id标签:映射主键列
column:数据库表列名
property:java实体类属性名
result:映射普通列
column:数据库表列名
property:java实体类属性名
-->
<resultMap id="userResultMap" type="com.demo.domain.User">
<id column="uid" property="id"></id>
<result column="name" property="username"></result>
<result column="bir" property="birthday"></result>
<result column="gender" property="sex"></result>
<result column="address" property="address"></result>
</resultMap>
<!--查询所有,数据库列表和java实体类属性名不一致-->
<select id="findAllResultMap" resultMap="userResultMap">
SELECT id AS uid,username AS `name`,birthday AS bir,sex AS gender,address FROM `user`
</select>
9.2多条件查询
UserMapper接口
基于用户id和用户名查询用户信息
方式一:
//当传递多个参数时,需要使用@Param注解指定参数名称
User findByIdAndUsername1(@Param("id") Integer id,@Param("username") String username);
方式二:
//基于用户id和用户名查询用户信息 将查询条件封装到对象中,通过对象作为参数传递到SQL语句中
User findByIdAndUsername2(User user);
UserMapper.xml映射文件
方式一:
<!-- 如果传递多个参数时,parameterType属性可以省略-->
<select id="findByIdAndUsername1" resultType="com.demo.domain.User">
SELECT * FROM `user` WHERE id=#{id} AND username=#{username}
</select>
方式二:
<!-- 将查询条件封装到对象中,通过对象作为参数传递到SQL语句中-->
<select id="findByIdAndUsername2" resultType="com.demo.domain.User" parameterType="com.demo.domain.User">
SELECT * FROM `user` WHERE id=#{id} AND username=#{username}
</select>
9.3模糊查询
方式一:
<!--
测试类中,参数加入%
List<User> list = mapper.findByUsername1("%...%");
缺点:java与SQL语句耦合
-->
<select id="findByUsername1" resultType="com.demo.domain.User" parameterType="String">
SELECT * FROM `user` WHERE username LIKE #{username}
</select>
方式二:【了解】
<!--
通过在SQL语句中拼接占位符 %
mysql5.5版本之前,此拼接的SQL中不支持使用单引号
oracle数据库中,除了别名位置,其他地方不能使用双引号
-->
<select id="findByUsername2" resultType="com.demo.domain.User" parameterType="String">
SELECT * FROM `user` WHERE username LIKE "%" #{username} "%"
</select>
方式三:【了解】
<!--
${} 字符串拼接 如果接受的是简单数据类型,使用固定参数名:value
存在SQL注入的问题
-->
<select id="findByUsername3" resultType="com.demo.domain.User" parameterType="String">
SELECT * FROM `user` WHERE username LIKE '%${value}%'
</select>
方式四:【掌握】
<!--
concat函数拼接字符串
Oracle:concat()函数只能传递两个参数,可以用嵌套解决
-->
<select id="findByUsername4" resultType="com.demo.domain.User" parameterType="String">
SELECT * FROM `user` WHERE username LIKE concat('%',#{username},'%')
</select>
9.4${} 与 #{} 区别
${}:底层 Statement
1. sql与参数拼接在一起,会出现sql注入问题
2. 每次执行sql语句都会编译一次
3. 接收简单数据类型,命名:${value}
4. 接收引用数据类型,命名: ${属性名}
5. 字符串类型需要加 '${value}'
org.apache.ibatis.scripting.xmltags.TextSqlNode
#{}:底层 PreparedStatement
1. sql与参数分离,不会出现sql注入问题
2. sql只需要编译一次
3. 接收简单数据类型,命名:#{随便写}
4. 接收引用数据类型,命名:#{属性名}
关键点:
${}字符串拼接,有SQL注入的问题,#{}占位符,预编译。没有SQL注入问题
9.5返回主键
应用场景
向数据库保存一个user对象后, 然后在控制台记录下此新增user的主键值(id)
返回主键,主要应用在表关联时。
UserMapper.xml映射文件
方式一
<!--
useGeneratedKeys:开启主键返回功能,默认不开启
keyColumn:user表中的主键列
keyProperty:user实体类主键属性名
注意:仅支持主键自增类型的数据库 mysql SQL server ,Oracle不支持
-->
<insert id="save1" parameterType="com.demo.domain.User" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>
<!--
返回主键方式二
<selectKey>执行返回主键SQL语句标签
keyColumn:user表中的主键列
keyProperty:user实体类主键属性名
resultType:user实体类中主键类型
order: 返回主键的查询语句是在insert插入数据语句前执行,还是在insert插入数据语句后执行
AFTER:主键自增往往使用AFTER
BEFORE :主键提前生成, 然后在将生成的主键插入的数据库表主键列
-->
<insert id="save2" parameterType="com.demo.domain.User">
<selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>
9.6 动态SQL
- 根据传入的参数不同, 需要执行的SQL的结构就会不同,这就是动态SQL
if 条件判断
<!--
if标签做条件判断
where 标签 相当于 where true 功能,但是如果没有设置条件时,where语句不会出现在SQL中
可以去除第一条件中的and或者or
-->
<select id="findByIdAndUsernameIf" resultType="com.demo.domain.User" parameterType="com.demo.domain.User">
select * from user
<where>
<if test="id != null">
and id=#{id}
</if>
<if test="username !=null ">
and username=#{username}
</if>
</where>
</select>
set 用于update语句
<!--
set 标签 :可以将更新列名中的最后一个逗号去除
-->
<update id="updateIf" parameterType="com.demo.domain.User">
update user
<set>
username=#{username},
</set>
where id=#{id}
</update>
foreach 用于循环遍历【重点】
<foreach>标签用于遍历集合,它的属性:
collection:代表要遍历的集合元素
遍历普通list集合,取值属性只能是list或者collection
遍历普通数组,取值属性只能是array
实体类中的集合属性QueryVo,实体类中list集合属性名
QueryVo queryVo = new QueryVo();
List<Integer> ids = new ArrayList<>();
ids.add(xxx);...
queryVo.setIds(ids);
List<User> userList = mapper.findByQueryVo(queryVo);
collection则取值为ids
open:代表语句的开始部分
close:代表结束部分
item:代表遍历集合的每个元素,生成的变量名
sperator:代表分隔符
<select id="findByXxx" resultType="com.demo.domain.User" parameterType="java.lang.Integer">
select * from user where id in
<foreach collection="Xxx" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</select>
9.7SQL片段&小结
应用场景
映射文件中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的
<sql id="selectUser">
select username,address from user
</sql>
<include refid="selectUser"></include>
-----------------------------------------------------------------
MyBatis映射文件配置:
<select>:查询
<insert>:插入
<update>:修改
<delete>:删除
<selectKey>:返回主键
<where>:where条件
<if>:if判断
<foreach>:for循环
<set>:set设置
<sql>:sql片段抽取
<include>:引入抽取的SQL片段
9.8MyBatis多表查询
一对一
实体类:
其中一个类A中,定义一个另一个类B的变量
类A的映射文件:
<resultMap id="orderMap" type="com.demo.domain.Order">
<id column="id" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="money" property="money"></result>
<!--
一对一表关系 association标签
property="user": order关联的user实体类属性名
javaType="com.demo.domain.User" 关联的实体类型
-->
<association property="user" javaType="com.demo.domain.User">
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="birthday" property="birthday"></result>
<result column="sex" property="sex"></result>
<result column="address" property="address"></result>
</association>
</resultMap>
<!--
询订单的同时,查询当前订单所属用户
resultType:单表映射封装
resultMap:多表查询,必须手动映射封装
-->
<select id="findByIdWithUser" resultMap="orderMap" parameterType="int">
SELECT * FROM orders o INNER JOIN `user` u ON o.`uid`=u.id WHERE o.id=#{id}
</select>
一对多
实体类
其中一个类A中,定义一个另一个类B的List集合变量
类A的映射文件:
<resultMap id="userMap" type="com.demo.domain.User">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="birthday" property="birthday"></result>
<result column="sex" property="sex"></result>
<result column="address" property="address"></result>
<!--
配置一对多关系
property="orderList":关联的实体类属性名称
ofType="com.demo.domain.Order":关联的实体类对象的java类型(集合中的泛型)
-->
<collection property="orderList" ofType="com.demo.domain.Order">
<id column="oid" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="money" property="money"></result>
</collection>
</resultMap>
<select id="findByIdWithOrder" parameterType="int" resultMap="userMap">
SELECT *,o.id AS oid FROM `user` u INNER JOIN orders o ON o.`uid`=u.id WHERE u.id=#{id}
</select>
多对多(由二个一对多组成)
<resultMap id="userWithRoleMap" type="com.demo.domain.User">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="birthday" property="birthday"></result>
<result column="sex" property="sex"></result>
<result column="address" property="address"></result>
<!--
配置多对多关系
property="orderList":关联的实体类属性名称
ofType="com.demo.domain.Order":关联的实体类对象的java类型(集合中的泛型)
-->
<collection property="roleList" ofType="com.demo.domain.Role">
<id column="rid" property="id"></id>
<result column="role_name" property="roleName"></result>
<result column="role_desc" property="roleDesc"></result>
</collection>
</resultMap>
<!--基于用户查询关联的角色-->
<select id="findByIdWithRole" parameterType="int" resultMap="userWithRoleMap">
SELECT * FROM `user` u INNER JOIN user_role ur ON u.id=ur.`uid`
INNER JOIN role r ON r.id=ur.`rid` WHERE u.id=#{id}
</select>
小结
一对一配置:使用<resultMap>+<association>做配置
association:
property:关联的实体属性名
javaType:关联的实体类型(别名)
一对多配置:使用<resultMap>+<collection>做配置
collection:
property:关联的集合属性名
ofType:关联的集合泛型类型(别名)
多对多配置:使用<resultMap>+<collection>做配置
collection:
property:关联的集合属性名
ofType:关联的集合泛型类型(别名)
多对多的配置跟一对多很相似,难度在于SQL语句的编写。
Day10MyBatis嵌套查询、加载策略、缓存
10.1嵌套查询
1.简介
嵌套查询就是将原来多表查询中的联合查询语句拆成多个单表的查询,再使用mybatis的语法嵌套在一起。
2.特点:
关联查询缺点:
1、SQL语句编写复杂
2、如果数据过大时,笛卡尔积产生的数据会造成内存溢出
嵌套查询优点:
1、SQL语句编写简单
2、没有多表关联查询, 不会产生笛卡尔积
3.步骤:一对多举例
1)先查询(一方)单表
2)再查询(多方)单表
3)最后由mybatis嵌套组合
4.配置:
一对一:
使用<resultMap>+<association>做配置,通过column条件,执行select查询
一对多:
使用<resultMap>+<collection>做配置,通过column条件,执行select查询
多对多:
使用<resultMap>+<collection>做配置,通过column条件,执行select查询
5.选择
传统开发,数据量小:使用关联查询
互联网开发,数据量大:使用嵌套查询
当前也有人这么玩
在java中先查用户,在查角色,不再使用嵌套....
10.2一对多嵌套查询
映射文件1
<resultMap id="orderMap" type="com.demo.domain.Order">
<id column="id" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="money" property="money"></result>
<!--基于mybatis配置,实现嵌套查询用户信息
column="uid" 订单表查询结果中的用户外键列,作为查询条件查询用户信息
elect="com.demo.dao.UserMapper.findById" 查询用户信息(用户接口+调用方法)
-->
<association property="user" javaType="com.demo.domain.User" column="uid" select="com.demo.dao.UserMapper.findById"></association>
</resultMap>
<!--
一对一嵌套查询
基于订单id查询订单数据
resultMap:多表查询,无论是关联查询还是嵌套查询,都是通过resultMap手动映射封装
-->
<select id="findByIdWithUser" parameterType="int" resultMap="orderMap">
SELECT * FROM orders o WHERE o.id=#{id}
</select>
映射文件2
<?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">
<!-- namespace 当前文件唯一标记 -->
<mapper namespace="com.demo.dao.UserMapper">
<!--基于用户id查询用户功能-->
<select id="findById" parameterType="int" resultType="com.demo.domain.User">
SELECT * FROM `user` WHERE id=#{id}
</select>
</mapper>
10.3多对多嵌套查询
映射文件1
<resultMap id="userWithRolesMap" type="com.demo.domain.User">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="birthday" property="birthday"></result>
<result column="sex" property="sex"></result>
<result column="address" property="address"></result>
<!--
多对多嵌套组合
column="id":用户id作为嵌套查询的条件
select="com.demo.dao.RoleMapper.findByUid":嵌套查询用户关联的角色列表(接口+方法)
-->
<collection property="roleList" ofType="com.demo.domain.Role" column="id" select="com.demo.dao.RoleMapper.findByUid"></collection>
</resultMap>
<!--多对多嵌套查询-->
<select id="findByIdWithRoles" parameterType="int" resultMap="userWithRolesMap">
SELECT * FROM `user` WHERE id =#{id}
</select>
映射文件2
<?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.demo.dao.RoleMapper">
<resultMap id="roleMap" type="com.demo.domain.Role">
<id column="id" property="id"></id>
<result column="role_name" property="roleName"></result>
<result column="role_desc" property="roleDesc"></result>
</resultMap>
<!--基于用户id查询角色列表-->
<select id="findByUid" parameterType="int" resultMap="roleMap">
SELECT * FROM role r INNER JOIN user_role ur ON r.id=ur.`rid` WHERE ur.`uid`=#{uid}
</select>
</mapper>
10.4MyBatis加载策略
1.介绍
当多个模型(表)之间存在关联关系时, 加载一个模型(表)的同时, 是否要立即加载其关联的模型, 我们把这种决策成为加载策略
立即加载:
加载一个模型(表)的时候, 需要立即加载出其关联的所有模型(表)
延迟加载(懒加载):
加载一个模型的时候, 不需要立即加载出其关联的所有模型, 等到真正需要的时候再加载
2.注意:
Mybatis中的加载策略有两种:立即加载和延迟加载, 默认是立即加载
延迟加载是在嵌套查询基础上实现的
一对一使用立即加载
一对多、多对多使用延迟加载
10.5配置加载策略
全局
SqlMapConfig.xml,设置开启全局延迟加载
<!--全局设置-->
<settings>
<!--全局延迟加载 true开启延迟加载 false:立即加载(默认值)-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
局部
mapper映射文件,指定某一个select标签配置
<association></association>
<collection></collection>
fetchType属性:
eager 立即加载
lazy 延迟加载
注意:局部优先级高于全局的…
触发(立即)加载
有这样一个全局配置lazyLoadTriggerMethods
,它定义的方法会触发立即加载
也就说当你调用它定义的方法时, 会执行数据加载, 它的默认值是equals,clone,hashCode,toString
<!--全局设置-->
<settings>
<!--全局延迟加载 true开启延迟加载 false:立即加载(默认值)-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--
触发立即加载的配置
默认值:equals,clone,hashCode,toString
覆盖默认值:value="" 在执行如上四个方法时不会触发立即加载了
可以调用get方法触发立即加载
-->
<setting name="lazyLoadTriggerMethods" value=""/>
</settings>
10.6MyBatis缓存
1.简介
缓存是服务器内存(硬盘)中的一块区域
经常访问但又不经常修改的数据适合做缓存
缓存是用来提高查询效率的,所有的持久层框架基本上都有缓存机制
Mybatis也提供了缓存策略,分为一级缓存,二级缓存
mybatis的缓存,都不需要我们手动存储和获取数据。mybatis自动维护的。
使用mybatis,如果是中小型项目,使用自带缓存的机制是可以满足需求的。如果是大型(分布式)项目,mybatis的缓存灵活性不足,需要使用第三方的缓存技术解决问题。
2.MyBatis一级缓存
本质是一个Map集合
SqlSession级别的缓存,默认开启,不需要手动配置
不同的SqlSession之间的缓存区域是互相不影响的
执行SqlSession的 CUD(增删改)操作,或者调用clearCache()、commit()、close()方法,都会清空缓存
3.二级缓存
MyBatis的二级缓存虽然是默认开启的,但需要在映射文件中配置`<cache/>`标签才能使用,而且要求实体类的必须实现序列化接口
二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1DP69yIp-1605668809268)(assets\9.png)]
Day11MyBatis注解
单表简单CRUD可以使用注解、多表及动态sql用xml
11.1MyBatis常用注解
注解在接口的方法上,括号内写sql语句的字符串
* @Insert:实现新增,代替了<insert></insert>
* @Update:实现更新,代替了<update></update>
* @Delete:实现删除,代替了<delete></delete>
* @Select:实现查询,代替了<select></select>
* @Result:实现结果集封装,代替了<result></result>
* @Results:可以与@Result 一起使用,封装多个结果集,代替了<resultMap></resultMap>
* @One:实现一对一结果集封装,代替了<association></association>
* @Many:实现一对多结果集封装,代替了<collection></collection>
局部延迟加载:
fetchType = FetchType.LAZY 表示懒加载
fetchType = FetchType.EAGER 表示立即加载
fetchType = FetchType.DEFAULT 表示使用全局配置
* @CacheNamespace 开启二级缓存的使用
一对一查询
接口1:
//一对一嵌套组合查询
@Select("SELECT * FROM orders WHERE id=#{id}")//基于订单id查询订单
@Results({
@Result(column = "id",property = "id",id=true),
@Result(column ="ordertime",property = "ordertime"),
@Result(column ="money",property = "money"),
/*<association property="user" javaType="com.demo.domain.User" column="uid" select="com.demo.dao.UserMapper.findById" fetchType="eager"></association>*/
@Result(property = "user",javaType = User.class,column="uid",one = @One(select = "com.demo.dao.UserMapper.findById",fetchType = FetchType.EAGER)) // 基于mybatis嵌套组合
})
Order findByIdWithUser(Integer id);
接口2:
@Select("select * from user where id =#{id}")
User findById(Integer id);
一对多查询
接口1:
@Select("SELECT * FROM `user` WHERE id=#{id}")
@Results({ //相当于 <resultMap>
@Result(column ="id" ,property ="id",id=true ), //相当于 <result> id=true表明当前列是主键列
@Result(column = "username",property = "username"),
@Result(column = "birthday",property = "birthday"),
@Result(column = "sex",property = "sex"),
@Result(column = "address",property = "address"),
/* <collection property="orderList" ofType="com.demo.domain.Order" column="id" select="com.demo.dao.OrderMapper.findByUid"></collection>*/
//一对多嵌套组合设置 javaType =List.class 通过list集合接收嵌套查询的结果列表
@Result(property="orderList",javaType =List.class,column = "id",many = @Many(select = "com.demo.dao.OrderMapper.findByUid") )
})
User findByIdWithOrders(Integer id);
接口2:
@Select("select * from orders where uid=#{uid}")
List<Order> findByUid(Integer uid);
多对多查询
接口1:
@Select("SELECT * FROM `user` WHERE id=#{id}")
@Results({ //相当于 <resultMap>
@Result(column ="id" ,property ="id",id=true ), //相当于 <result> id=true表明当前列是主键列
@Result(column = "username",property = "username"),
@Result(column = "birthday",property = "birthday"),
@Result(column = "sex",property = "sex"),
@Result(column = "address",property = "address"),
/* <collection property="roleList" ofType="com.demo.domain.Role" column="id" select="com.demo.dao.RoleMapper.findByUid"></collection>*/
//多对多嵌套组合设置 javaType =List.class 通过list集合接收嵌套查询的结果列表
@Result(property="roleList",javaType =List.class,column = "id",many = @Many(select = "com.demo.dao.RoleMapper.findByUid") )
})
User findByIdWithRoles(Integer id);
接口2:
@Select("SELECT * FROM role r INNER JOIN user_role ur ON r.id=ur.`rid` WHERE ur.`uid`=#{uid}")
@Results({
@Result(column = "id",property = "id",id=true),
@Result(column = "role_name",property="roleName"),
@Result(column = "role_desc",property="roleDesc")
})
List<Role> findByUid(Integer uid);
是主键列
@Result(column = “username”,property = “username”),
@Result(column = “birthday”,property = “birthday”),
@Result(column = “sex”,property = “sex”),
@Result(column = “address”,property = “address”),
/* */
//一对多嵌套组合设置 javaType =List.class 通过list集合接收嵌套查询的结果列表
@Result(property=“orderList”,javaType =List.class,column = “id”,many = @Many(select = “com.demo.dao.OrderMapper.findByUid”) )
})
User findByIdWithOrders(Integer id);
接口2:
@Select(“select * from orders where uid=#{uid}”)
List findByUid(Integer uid);
**多对多查询**
```java
接口1:
@Select("SELECT * FROM `user` WHERE id=#{id}")
@Results({ //相当于 <resultMap>
@Result(column ="id" ,property ="id",id=true ), //相当于 <result> id=true表明当前列是主键列
@Result(column = "username",property = "username"),
@Result(column = "birthday",property = "birthday"),
@Result(column = "sex",property = "sex"),
@Result(column = "address",property = "address"),
/* <collection property="roleList" ofType="com.demo.domain.Role" column="id" select="com.demo.dao.RoleMapper.findByUid"></collection>*/
//多对多嵌套组合设置 javaType =List.class 通过list集合接收嵌套查询的结果列表
@Result(property="roleList",javaType =List.class,column = "id",many = @Many(select = "com.demo.dao.RoleMapper.findByUid") )
})
User findByIdWithRoles(Integer id);
接口2:
@Select("SELECT * FROM role r INNER JOIN user_role ur ON r.id=ur.`rid` WHERE ur.`uid`=#{uid}")
@Results({
@Result(column = "id",property = "id",id=true),
@Result(column = "role_name",property="roleName"),
@Result(column = "role_desc",property="roleDesc")
})
List<Role> findByUid(Integer uid);