09-MySQL


1.MySQL

前端(页面:展示,数据!)

后台(连接点:连接数据库JDBC,链接前端(控制视图跳转,和给前端传递数据))

数据库(存数据,Txt,Excel,word)

1.初识mysql

1.1什么是数据库

数据库(DB,DataBase)

概念:数据仓库,软件,安装在操作系统之上 SQL,可以存储大量的数据

作用:存储数据,管理数据

1.2数据库分类

关系型数据库:

  • MySQL,Oracle,Sql Server,DB2、SQLlite

  • 通过表和表之间,行和列之间的关系进行数据的存储,学员信息表等…..

非关系型数据库:(NoSQL)Not only SQL

  • Redis,MongDB
  • 非关系型数据库,对象存储,通过对象的自身属性来决定。

DBMS(数据库管理系统)

  • 数据库的管理软件,科学有效的管理数据
  • MySQL,数据库管理系统

1.3MySQL简介

MySQL是一个关系型数据库管理系统

前:瑞典MySQL AB公司

后:属于Oracle旗下产品

MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。

开源的数据库软件

体积小、速度快、总体拥有成本低

安装MySQL尽量使用压缩包安装

1.4连接数据库

net start mysql 开启数据库

net stop mysql 关闭数据库

连接数据库

mysql -uroot -p123456 -- 连接数据库

update mysql.user set authentication_string=password('123456') where user = 'root' and Host = 'localhost'; -- 修改用户密码
flush privileges; -- 刷新权限

-- 所有的语句都是用;进行结尾
show databases; -- 产看所有数据库;
user 数据库名 -- 切换数据库
show table; -- 产看所有的表

desc 表名; -- 查看表结构信息

create database 数据库名; -- 创建一个数据库
exit; --退出连接

-- 单行注释

/*
多行注释

*/

数据库 xxx语音 CRUD 增删改查

DDL 数据库定义语音

DML 数据库操作语音

DQL 数据库查询语音

DCL 数据库控制语音

2.操作数据库

操作数据库->操作数据库中的表->操作数据库中表的数据

mysql关键字不区分大小写

2.1操作数据库

1.创建数据库

CREATE DATABASE [IF NOT EXISTS] school;

2.删除数据库

DROP DATABASE [IF EXISTS]school;

3.使用数据库

-- tab键上面,如果你的表名或者字段名是一个特殊字符,就需要带` `;
USER `school`

4.产看数据库

SHOW DATABASE; -- 查看所有的数据库

2.2数据库的数据类型

数值类型

  • tinyint 十分小的数据 1个字节

  • smallint 较小的数据 2个字节

  • mediumint 中等大小的数据 3个字节

  • int 标准字节 4个字节 常用 int

  • bigint 较大的数据 8个字节 long

  • float 浮点数 4个字节

  • double 浮点数 8个字节 (精度问题)

  • decimal 字符串形式的浮点数 金融计算的时候,一般使用decimal

字符串

  • char 固定大小的字符串 0~255
  • varchar 可变字符串 0~65535 常用的 String
  • tinytext 微型文本 2^8 -1
  • text 文本串 2^16 - 1 保存大文本 String

时间日期

java.util.Date

  • date YYYY-MM-DD,日期
  • time HH:mm:ss 时间格式
  • datetime YYYY-MM-DD HH:mm:ss 最常用的时间格式
  • timestamp 时间戳 1970.1.1到现在的毫秒数! 常用
  • year 年份表示

null

  • 没有值,未知
  • 不要使用NULL进行运算,结果为NULL

2.3数据库的字段属性

Unsigned:

  • 无符号的整数
  • 声明了该列不能声明为负数

zerofill

  • 0填充
  • 不足的位数,使用0来填充,比如:int(3),填5 为 005;

自增:

  • 通常理解为自增,自动在上一条的记录的基础上+1(默认)
  • 通常用来设计唯一的主键~index,必须是整数类型
  • 可以自定义设计主键自增的起始值和步长

非空 NULL NOT NULL

  • 设置为NOT NULL不给赋值,就会报错
  • NULL,如果不填写值,默认为NULL

默认

  • 设置默认值
  • sex,设置默认值为男,如果不指定该列的值,则会有默认的值

每一个表,都必须存在以下五个字段,表示表存在的意义

id 主键

version 版本 乐观锁

is_delete 伪删锁

gmt_create 创建时间

gmt_update 修改时间

2.4创建数据库表

-- 使用英文括号 , 表的名称和字段尽量使用`括起来
-- AUTO_INCREMENT 自增
-- 字符串使用单引号括起来
-- 所有的语句后面加,最后一个字段不要加
-- primary key 主键,一般一个表只有一个唯一主键!
CREATE TABLE IF NOT EXISTS `student`(
`id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
`name` VARCHAR(30) NOT NULL DEFAULT '不知道' COMMENT '姓名',
`pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码',
`sex` VARCHAR(2) NOT NULL DEFAULT '女' COMMENT '性别',
`birthday` DATETIME DEFAULT NULL  COMMENT '出生日期',
`address` VARCHAR(100) DEFAULT NULL COMMENT '家庭住址',
`email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
)ENGINE = INNODB DEFAULT CHARSET = utf8;

格式

CREATE TABLE[ IF NOT EXISTS] `表明`(
`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
    .........
`字段名` 列类型 [属性] [索引] [注释]
)[表类型] [字符集设置] [注释];

常用命令

SHOW CREATE DATABASE school; -- 查看创建数据库语句
SHOW CREATE TABLE student; -- 查看创建数据表语句的定义语句
DESC student; -- 显示表的结构

2.5数据表的类型

-- 关于数据库引擎/*INNODB 默认使用MYISAM 之前使用*/
MYISAM INNODB
事务支持 不支持 支持
数据行锁定 不支持 支持
外键约束 不支持 支持
全文索引 支持 不支持
表空间的大小 较小 较大,约为两倍MYISAM

常规使用操作:

  • MYISAM 节约空间,速度较快
  • INNODB 安全性高,事物的处理,多表多用户操作

在物理空间存在的位置

所有的数据库文件都存在data目录下,一个文件夹对应一个数据库

呢妹纸还是文件的存储!

MySQL引擎在物理文件上的区别

  • innoDB 在数据库表中只有一个 *.frm文件,以及上级目录下的 ibdata1文件
  • MYISAM 对应文件
    • *.frm - 表结构额定义文件
    • *.MYD 数据文件(data)
    • *.MYI 索引文件(index)

设置数据库标的字符集编码

CHARSET = utf8

不设置,会是MySQL默认的字符集编码~(不支持中文)

MySQL的默认编码是Latin1,不支持中文

在my.ini中配置默认的编码

character-set-server = utf8

2.6修改删除表

修改

 -- 修改表  ALTER TABLE 旧表名 RENAME AS 新表名;
 ALTER TABLE student RENAME AS r_student;

-- 增加表的字段 ALTER TABLE 表名 ADD 字段名 列属性
ALTER TABLE r_student ADD age INT(11);

-- 修改表的字段 (重命名,修改约束!)
-- ALTER TABLE 表名 MODIFY 字段名 列属性[]; 
-- ALTER TABLE 表名 CHANGE 旧字段名 新字段名 列属性[];
ALTER TABLE r_student MODIFY age VARCHAR(11); -- 修改约束
ALTER TABLE r_student CHANGE age age1 INT(11); -- 字段重命名

-- 删除表的字段 ALTER TABLE 表名 DROP 字段名 ;
ALTER TABLE r_student DROP age1 ;

删除

-- 删除表(如果表存在再删除) DROP TABLE [IF EXISTS] 表名;
DROP TABLE IF EXISTS d_name;

所有的创建和删除操作尽量加上判断,以免报错

注意点

  • `` 字段名,使用包裹
  • 注释 – /**/
  • SQL关键字大小写不敏感,建议大家写小写
  • 所有的符号使用英文

3、MySQL数据管理

3.1、外键

1、在创建表的时候,增加约束

create table if not exists `student`(`id` int(4) not null auto_increment comment '学号',`gradeid` int(10) not nunll comment '学生年级',primary key (`id`),key `FK_gradeid`(`gradeid`),constraint `FK_gradedid` foreign key (`gradeid`) references `grade`(`gradeid`)    );

删除有外键关系的表时,必须要先删除别人的表,再删除引用的表

2、创建表成功后,添加外键约束

alter table `student` add CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) references `grade`(`gradeid`);
-- alter table `表名` add CONSTRAINT `约束名` FOREIGN KEY (`作为外键的列`) references `哪个表`(`哪个列`);

以上的操作都是物理外键,数据库级别的外键,不建议使用!

最佳实现

  • 数据库就是单纯的表,只用来存数据,只有行和列
  • 我们想使用多张表的数据,想使用外键(程序去实现)

3.2、DML语言

数据库意义:数据存储,数据管理

DML语言:数据操作语言

  • insert
  • update
  • delete

3.3、添加

1、

insert into 表名([字段名1,字段名2]) values(值1,值2),
(值1,值2)

注意:

  1. 字段和字段之间使用英文逗号隔开
  2. 字段是可以省略的,但是后面的值必须要一一对应,不能少
  3. 可以同时插入多条数据,values后面的值,需要使用,隔开即可values(),()

3.4、修改

update 表名 set 字段名 = 新值 where 条件

-- 在不指定条件的情况下,会改动所有的表!update 表名 set 字段名 = 新值 where 条件-- 修改多个属性,逗号隔开update `student` set `name` = 'lwj',class = '12' where id = 1;

条件:where子句 运算符 id 等于某个值,大于某个值,在某个区间内修改….

语法:update 表名 set 列 = value, [列 = value……] where [条件]

注意

  • 数据库的列尽量带上``
  • 条件,筛选的条件,如果没有指定,则会修改所有的列,
  • value,是一个具体的值,也可以是一个变量
  • 多个设置的属性之间,使用英文逗号隔开

3.5、删除

delete 命令

语法 delete from 表名[where 条件]

-- 删除数据(避免这样写删除全部数据)delete from `student`-- 删除指定数据delete from `student` where id = 1;

truncate 命令

作用:完全清空一个数据库,表的结构和索引约束不会变!

-- 清空表truncate `student`

delete和truncate区别

  • 相同点:都能删除数据,都不会删除表结构
  • 不同点:
    • truncate 重新设置自增列,计数器会归零
    • truncate 不会影响事务

delete删除问题,重启数据库,现象

  • innodb 自增列会从1开始(存在内存中,断电即失,mysql8.0已经修复问题)
  • myisam 继续从上一个自增量开始(存在文件中,不会丢失)

4、DQL查询数据

4.1、DQL

(Date Query LANGUAGE 数据库查询语言)

  • 所有的查询操作都用它 select
  • 简单查询,复杂的查询
  • 数据库中最核心的语言
  • 使用频率最高的语言

)

4.2、指定查询字段

-- 查询全部的学生 select 字段 from 表
SELECT * from student

-- 查询指定字段
select `student`.address from `student`

-- 别名,给结果起一个名字 AS
select `student`.address AS 地址 from `student`

-- 函数 CONCAT(str1,str2,...)
select CONCAT('姓名:',studentname) AS 新名字 FROM student

语法:select 字段…… from 表

起别名 AS 字段名 as 别名 表名 as 别名

去重 distinct

作用:去除select查询出来的结果中的重复的数据只显示一条

-- 查询一下有哪些同学参加考试SELECT * from result -- 查询全部的考试成绩-- 查询有哪些同学参加了考试SELECT studentno FROM result-- 发现重复数据,去重SELECT DISTINCT studentno FROM result

数据库的列(表达式)

-- 查询系统版本SELECT VERSION()-- 用来计算SELECT 100*3-1 AS 计算结果-- 查询自增的步长SELECT @@auto_increment_increment-- 学员成绩加分SELECT studentno,studentresult+1 AS '提分后' FROM result

数据库中的表达式:文本值,列,null,函数,计算表达式,系统变量……

select 表达式 from 表

4.3、 where 条件子句

作用:检索数据中符合条件的值

搜索的条件由一个或者多个表达式组成!结果布尔值

逻辑运算符

运算符 语法 描述
and && a and b a&&b 逻辑与,两个都为正,结果为真
or || a or b a||b 逻辑或,其中一个为真,则结果为真
Not ! not a !a 逻辑非,真为假,假为真!

尽量使用英文字母

select studentno, studentresult from result

-- 查询考试成绩在95~100分之间
SELECT studentno,studentresult FROM result
WHERE studentresult>=95 and studentresult <= 100;


-- 模糊查询
SELECT studentno,studentresult FROM result WHERE studentresult BETWEEN 95 and 100;

-- 除了1000号学生之外的同学成绩
SELECT studentno,studentresult FROM result
where studentresult != 100

-- != not 
SELECT studentno,studentresult FROM result
where NOT studentresult =100

模糊查询:比较运算符

运算符 语法 描述
is null a is null 如果操作符为null,结果为真
is not null a is not null 如果操作符为not null,结果为真
between a between a and b 若a在b和c之间,则结果为真
like a like b sql匹配,如果a匹配b,则结果为真
in a in (a1,a2,a3…) 假设a在a1,或者a2……其中的某一个值,结果为真
-- ==================模糊查询=============

-- 查询姓刘的同学
-- like结合%(代表0到任意个字符) _(代表一个字符)
SELECT `studentno`,`studentname` FROM `student`
WHERE `studentname` LIKE '刘%'

-- 查询姓刘的同学,名字后面只有一个名字
SELECT `studentno`,`studentname` FROM `student`
WHERE `studentname` LIKE '刘_'
-- 查询姓刘的同学,名字后面只有二个名字
SELECT `studentno`,`studentname` FROM `student`
WHERE `studentname` LIKE '刘_'


-- 查询有嘉字的同学。
SELECT `studentno`,`studentname` FROM `student`
WHERE `studentname` LIKE '%嘉%'

-- ========in(具体得到一个或者多个值)=======
-- 查询 1001,1002,1003同学
SELECT `studentno`,`studentname` FROM `student`
where studentno in (1001,1002,1003);

-- 查询在北京的学生
SELECT `studentno`,`studentname` FROM `student`
where address in ('北京','南京');

-- ======null======
-- 查询地址为空得到学生
SELECT `studentno`,`studentname` FROM `student`
WHERE address is null OR address = ''

-- 查询有出生日期的同学,is not null
SELECT `studentno`,`studentname` FROM `student`
WHERE borndate is not null 

-- 查询没有出生日期的同学,is null
SELECT `studentno`,`studentname` FROM `student`
WHERE borndate is  null 

4.4、联表查询

join对比

)

-- ======================联表查询=================

-- join(连接的表) on(判断的条件) 连接查询
-- where 等值查询
-- 查询参加了考试的同学(学号,姓名,科目编号,分数)
-- INNER JOIN
SELECT s.studentno,studentname,subjectno,studentresult 
FROM student as s INNER join result as r ON s.studentno = r.studentno

-- RIGHT JOIN
SELECT s.studentno,studentname,subjectno,studentresult 
FROM student as s RIGHT join result as r ON s.studentno = r.studentno

-- LEFT JOIN
SELECT s.studentno,studentname,subjectno,studentresult 
FROM student as s LEFT join result as r ON s.studentno = r.studentno

-- 查询缺考的同学
SELECT s.studentno,studentname,subjectno,studentresult 
FROM student as s LEFT join result as r ON s.studentno = r.studentno
WHERE studentresult is NULL

-- 联结三个表进行查询
SELECT s.studentno,studentname,subjectname,studentresult 
FROM student s right join result r on s.studentno = r.studentno
inner join `subject` sb on r.subjectno = sb.subjectno
-- 假设存在一种多张表查询,先查询两张表然后再慢慢增加
操作 描述
inner join 如果表中至少有一个匹配的值,就返回行
leftf join 即使右表中没有匹配,也会从左表中返回所有的值
right join 即使左表中没有匹配,也会从右表中返回所有的值

自连接

自己的表和自己的表连接,核心:一张表拆成两张一样的表即可

父类

categoryid categoryName
2 信息技术
3 软件开发
5 美术设计

子类

pid categoryid categoryName
3 4 数据库
2 8 办公信息
3 6 web开发
5 7 美术设计

操作:查询父类对应的子类关系

父类 子类
信息技术 办公信息
软件开发 数据库
软件开发 web开发
美术设计 ps技术
 -- 自连接
-- 查询父子信息:把一张表拆成两张表一摸一样的表
SELECT a.categoryname as '父栏目',b.categoryname AS '子栏目'
FROM category as a,category as b
WHERE a.categoryid = b.pid


-- 查询学员所属的年级(学号,学生的姓名,年级名称)
SELECT studentno,studentname,gradename
FROM student s 
INNER JOIN grade g
ON s.gradeid = g.gradeid

-- 查询科目所属的年级(科目名称,年级名称)
SELECT `subjectname`,gradename
FROM `subject` s
INNER JOIN grade g
ON s.gradeid = g.gradeid

-- 三表查询
SELECT s.studentno,studentname,subjectname,studentresult 
FROM student s INNER join result r on s.studentno = r.studentno
inner join `subject` sb on r.subjectno = sb.subjectno
WHERE subjectname = '数据库结构-1'

4.5、分页和排序

排序

-- 排序:升序ASC,降序 DESC

-- ORDER BY 通过哪个字段排序 (升降)序
-- 根据结果成绩降序排序
SELECT s.studentno,studentname,subjectname,studentresult 
FROM student s INNER join result r on s.studentno = r.studentno
inner join `subject` sb on r.subjectno = sb.subjectno
WHERE subjectname = '数据库结构-1'
ORDER BY studentresult ASC

分页

-- 分页 每页只显示5个
-- 语法:limit 起始位置,页面显示的大小
-- 网页应用:当前,总的页数,页面的大小
-- LIMIT(0,5) 1~5行数据
-- LIMIT(1,5) 2~6行数据
SELECT s.studentno,studentname,subjectname,studentresult 
FROM student s INNER join result r on s.studentno = r.studentno
inner join `subject` sb on r.subjectno = sb.subjectno
WHERE subjectname = '数据库结构-1'
ORDER BY studentresult DESC
LIMIT 0,5

-- 第一页 limit 0,5
-- 第二页 limit 5,5
-- 第三页 limit 10,5
-- 第N页 limit pagesize(N-1),pagesize
-- pagesize:代表页面大小 (n-1)*pagesize:起始值 n:当前页
-- 数据总数/页面大小 = 总页数

语法:limit(查询起始下标,pagesize)

4.6、子查询

where(这个值是计算出来的)

本质:在where语句中嵌套一个子查询语句

where(select * from ……)

-- 2.使用子查询(由里到外)-- 查询所有数据库结构-1的学生学号SELECT r.studentno,r.subjectno,studentresultFROM result rWHERE studentno = (SELECT subjectno FROM `subject` WHERE subjectname = '数据库结构-1')-- 分数不小于80分的学号和姓名 高等数学SELECT studentno,studentname FROM student WHERE studentno IN (SELECT DISTINCT studentno FROM resultWHERE  studentresult >= 80 AND subjectno = (SELECT subjectno FROM `subject` WHERE subjectname = '高等数学-2'))s

4.7、分组和过滤

-- 查询不同课程的平均分,最高分,最低分-- 核心:(分组)SELECT subjectname,AVG(studentresult) AS 平均分,MAX(studentresult) AS 最大值,MIN(studentresult) AS 最小值FROM result rINNER JOIN `subject` sON r.subjectno = s.subjectnoGROUP BY r.subjectno -- 通过什么字段进行分组HAVING 平均分>80 

5.Mysql函数

5.1、常用函数

-- ======================常用函数======================

-- 数学运算
SELECT ABS(-8) -- 绝对值
SELECT CEILING(9.4) -- 向上取整
SELECT FLOOR(9.4) -- 向下取整
SELECT RAND() -- 返回一个0~1之间的随机数
SELECT SIGN(0) -- 判断一个数的符号 0返回0  负数返回-1 ,正数返回1

-- 字符串函数
SELECT CHAR_LENGTH('字符串函数') -- 字符串长度
SELECT CONCAT('一','二','三') -- 拼接字符串
SELECT INSERT('Helloworld',1,2,'一') -- 查询,从某个位置开始替换某个个长度
SELECT LOWER('DSAHB') -- 转换小写
SELECT UPPER('dsadsa') -- 转换成大写
SELECT INSTR('dsadsadsa','a') -- 返回第一次出现的位置的索引 
SELECT REPLACE('abcdefg','d','g') -- 替换出现指定字符串
SELECT SUBSTR('abcdefgh',2,3) -- 返回指定字符串(原字符串,截取的位置,截取的长度)
SELECT REVERSE('abcdefg') -- 反转字符串

-- 查询姓周的同学,改姓为邹
SELECT REPLACE(studentname,'周','邹')
FROM student
WHERE studentname LIKE '周%'

-- 时间和日期
SELECT CURRENT_DATE() -- 获取当前日期
SELECT CURDATE() -- 获取当前日期
SELECT NOW() -- 获取当前时间
SELECT LOCALTIME() -- 本地时间
SELECT SYSDATE() -- 系统时间

SELECT YEAR(NOW())
SELECT MONTH(NOW())
SELECT DAY(NOW())
SELECT HOUR(NOW())
SELECT MINUTE(NOW())
SELECT SECOND(NOW())


-- 系统
SELECT SYSTEM_USER()
SELECT USER()
SELECT VERSION()

5.2、聚合函数(常用)

函数名称 描述
COUNT() 计数
SUM() 求和
AVG() 平均值
MAX() 最大值
MIN() 最小值
-- ================聚合函数===================
-- 都能够统计表中的数据
SELECT COUNT(studentname) from student; -- count(字段),会忽略所有的null值
SELECT COUNT(*) from student; -- count(*) ,不会会忽略所有的null值
SELECT COUNT(1) from student; -- count(指定列),不会会忽略所有的null值

SELECT SUM(studentresult) AS 总和 FROM result
SELECT AVG(studentresult) AS 平均分 FROM result
SELECT MAX(studentresult) AS 最大值 FROM result
SELECT MIN(studentresult) AS 最小值 FROM result

5.3、数据库级别MD5加密

什么是MD5?

主要增强算法复杂度和不可逆性。

MD5不可逆,具体的值md5是一样的

MD5破解的原理是有一个字典,进行查找,暴力破解

-- ==================测试MD5加密===================
CREATE TABLE testmd5(
`id` INT(4) NOT NULL,
`name` VARCHAR(20) not NULL,
`pwd` VARCHAR(50) not NULL,
PRIMARY KEY(`id`)
)ENGINE = INNODB DEFAULT CHARSET = utf8;

-- 明文密码
INSERT INTO testmd5 VALUES(1,'zhangsan','123456'),(2,'lisi','123456'),(3,'wangwu','123456')


-- 加密
UPDATE testmd5 SET pwd = MD5(pwd); -- 加密全部密码

-- 插入的时候加密
INSERT INTO testmd5 VALUES(4,'xiaoming',MD5('123456'));

-- 如何检验:将用户传递进来的密码,进行MD5加密,然后进行对加密后的值进行比较
SELECT * FROM testmd5 WHERE `name` = 'xiaoming' AND pwd = MD5('123456')

6、事务

6.1、什么是事务

要么都成功,要么都失败

将一组sql放在一个批次中去执行

事务原则:ACID原则,原子性,一致性,隔离性,持久性(脏读,不可重复读,幻读)

原子性(Atomicity)

要么都成功,要么都失败

一致性(Consistency)

事务前后的数据完整性要保持一致

隔离性(Isolation)

事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

持久性(Durability)

事务一旦提交就是不可逆的,被持久化的存储到数据库中

隔离所导致的一些问题

脏读

指一个事务读取了另外一个事务未提交的数据。

不可重复读

在一个事务内读取表中的某一行数据,多次读取结果不同。(这个不一定是错误,只是某些场合不对)

**虚读(幻读)**:

是指在一个事务内读取到了别的事务插入的数据,导致前后读取数量总量不一致。

(一般是行影响,多了一行)

执行事务

-- =====================事务=======================
-- mysql是默认开启事务自动提交的
SET autocommit = 0; /* 关闭*/
SET autocommit = 1; /* 开启(默认)*/

-- 手动处理事务
set autocommit = 0 -- 关闭自动提交


-- 事务开启
START TRANSACTION -- 标记一个事务的开始,从这个之后的sql都在同一个事务内

INSERT xx
INSERT xx


-- 提交:持久化(成功)
COMMIT

-- 回滚:回到原来的样子(失败)
ROLLBACK

-- 事务结束
SET autocommit = 1 -- 开启自动提交

-- 了解
SAVEPOINT 保存点名 -- 设置一个事务的保存点
ROLLBACK TO SAVEPOINT 保存点名 -- 回滚到保存点名
RELEASE SAVEPOINT 保存点名 -- 删除(撤销)保存点

模拟场景

-- 转账
CREATE DATABASE shop;

USE shop;

CREATE TABLE `account`(
`id`INT(3)  not NULL auto_increment,
`name` VARCHAR(30) NOT NULL,
`money` DECIMAL(9,2) NOT NULL,
PRIMARY KEY(`id`)
)ENGINE = INNODB DEFAULT CHARSET = utf8;

INSERT INTO account(name,money) 
VALUES('A',2000.00),('B',10000.00)


-- 模拟转账
SET autocommit = 0; -- 关闭自动提交

start TRANSACTION -- 开启事务(一组事务)

UPDATE account SET money = money - 500 WHERE `name` = 'A' -- A减去500
UPDATE account SET money = money + 500 WHERE `name` = 'B' -- B加上500

COMMIT; -- 提交事务,就被持久化!

ROLLBACK; -- 回滚

SET autocommit = 1; -- 恢复默认值

7、索引

索引是数据结构

7.1、索引的分类

在表中,主键索引只能只能有一个,唯一索引可以有多个

  • 主键索引 primary key

    • 唯一标识,主键不可重复,只能有一个列作为主键
  • 唯一索引 unique key

    • 避免重复列出现,唯一索引可以重复,多个列都可以标识为唯一索引
  • 常规索引 key/index

    • 默认的,index或key关键字
  • 全文索引 fulltext

    • 在特定的数据库引擎下才有
    • 快速定位数据
-- 索引的使用
-- 在创建表的时候给字段增加索引
-- 创建完毕后,增加索引


-- 显示所有得到索引信息
SHOW INDEX FROM student1

-- 增加一个索引
ALTER TABLE `student1` ADD FULLTEXT INDEX `StudentName`(`StudentName`);

-- EXPLAIN分析sql执行的状况
EXPLAIN SELECT * FROM student1; -- 非全文索引

EXPLAIN SELECT * FROM student1 WHERE MATCH(StudentName) AGAINST('刘');  -- 全文索引

7.2、测试索引

-- 插入100万数据
Delimiter $$ -- 写函数之前必须写,标志


CREATE FUNCTION mock_data()
RETURNS INT
DETERMINISTIC -- 8.0版本需要多这么一行
BEGIN
	DECLARE num INT DEFAULT 10000;
	DECLARE i INT DEFAULT 0;
	WHILE i<num DO
	-- 插入语句
		INSERT INTO app_user(`name`,`email`,`phone`) VALUES(CONCAT('用户',i),'123321',CONCAT('18',FLOOR(RAND()*((	999999999-100000000)+100000000))))
		SET i = i+1;
	end WHILE
	RETURN i;

END;

SELECT * FROM app_user WHERE `name` = '用户9999'

-- 创建索引
-- id_表名_字段名
-- CREATE [FULLTEXT可以为其他] INDEX 索引名 on 表字段(字段) 
CREATE INDEX id_app_user_name ON app_user(`name`)

在数据量多时出现区别

7.3、索引原则

  • 索引不是越多越好
  • 不要对经常变动的数据加索引
  • 小数据量的表不要加索引
  • 索引一般加在常用来查询得到字段上

索引的数据结构

Hash 类型的索引

Btree:InnoDB的默认数据结构~

8、权限管理和备份

8.1、用户管理

可视化管理

各种软件进行可视化管理

sql命令操作

用户表:MySQL.user

本质上对这张表进行增删改查

-- 创建用户 CREATE USER 用户名@主机名 IDENTIFIED by '密码'
-- 其中 localhost 指本地才可连接
-- 可以将其换成 % 指任意 ip 都能连接
CREATE USER lwj@localhost IDENTIFIED by '123456'

-- 修改密码
ALTER USER lwj@localhost IDENTIFIED by '111111'

-- 重命名 原名TO新名
RENAME USER lwj@localhost TO lwj2@localhost


-- 用户授权  ALL PRIVILEGES 全部权限(为增删改查等),(库.表)
-- ALL PRIVILEGES除了给别人授权
GRANT ALL PRIVILEGES ON *.* TO lwj2@localhost

-- 查询权限
SHOW GRANTs FOR lwj2@localhost -- 查看指定用户得到权限
SHOW GRANTS FOR root@localhost

-- 撤销权限
REVOKE ALL PRIVILEGES ON *.* FROM lwj2@localhost

-- 删除用户
DROP USER lwj2@localhost
  • 保证重要的数据不丢失
  • 数据转移

MYSQL数据库的备份方式11

  • 拷贝物理文件

  • 使用可视化工具

  • 使用命令行导出 mysqldump 命令行使用

    #导出一张表
    # mysqldump -h 主机 -u 用户名 -p 密码 数据库 表名>物理磁盘位置/文件名
    #导出多张表
    # mysqldump -h 主机 -u 用户名 -p 密码 数据库 表名 表2 表3>物理磁盘位置/文件名
    #导出数据库
    # mysqldump -h 主机 -u 用户名 -p 密码 数据库 >物理磁盘位置/文件名
    mysqldump -hlocalhost -uroot -p123456 school student>D:/a.sql
    mysqldump: [Warning] Using a password on the command line interface can be insecure.
    
    #导入
    #登录情况下,切换到指定的数据库
    #source 备份文件
    source d:/a.sql
    
    mysql -u用户名 -p密码 库名<备份文件

    备份数据库,防止数据丢失。

9、数据库的规约,三大范式

9.1数据库的设计

当数据库比较复杂的时候,就需要进行设计

糟糕的数据库设计:

  • 数据冗余,浪费空间
  • 数据库插入和删除都会麻烦、异常【屏蔽使用物理外键】

良好的数据库设计:

  • 节省内存空间
  • 保证数据库的完整性
  • 方便开发

关于数据库设计

  • 分析需求:分析业务和需要处理的数据库需求
  • 概要设计:设计关系图e-r图

设计数据库的步骤:(个人博客)

  • 收集信息,分析需求

    • 用户表(用户登录注销,用户的个人信息,写博客,创建分类)
    • 分类表(文章分类,创建者)
    • 文章表(文章信息)
    • 评论表
    • 友链表(友链信息)
    • 自定义表(系统信息,某个关键的字,或者一些主字段)key:value
    • 说说表
    • 关注表
  • 标识实体(把需求落实到每个字段)

  • 标识实体之间的关系

    • 写博客: user –>blog
    • 创建分类: user –>category
    • 关注:user–> user
    • 友链: links
    • 评论:user-user-blog

9.2三大范式

为什么需要数据规范化

  • 信息重复
  • 更新异常
  • 插入异常
    • 无法正常显示信息
  • 删除异常
    • 丢失有效信息

三大范式

第一范式(1NF)

原子性:要求数据库表的每一列都是不可分割的原子数据项

第二范式(2NF)

前提:满足第一范式

每张表只描述一件事情,确保数据库表中的每一列都和主键有关,而不能只与主键的某一部分相关(主要针对联合主键而言)

第三范式(3NF)

前提:满足第一范式,满足第二范式

确保数据库表中的每一列都和主键直接相关,而不能间接相关。

规范性和性能的问题

关联查询的表不得超过三张表

  • 考虑商业化的需求和目标,(成本,用户体验)数据库的性能更加重要。
  • 在规范性能的问题的时候,需要适当考虑一下规范性!
  • 故意给某些表增加一些冗余的字段。(从多表查询中变为单表查询)
  • 故意增加一些计算列(从大数据量降为小数据量的查询:索引)

10、JDBC

java操作数据库

10.1、数据库驱动

不同的数据库有不同的驱动

我们程序通过数据库驱动,和数据库打交道!

10.2、JDBC

简化开发人员(对数据库的统一)的操作,提供了一个(Java操作数据库)规范 简称 JDBC 这些规范的具体实现由具体的厂商去做

对于开发人员而言,只需要掌握JDBC接口的操作即可

java.sql

javax.sql

还需要导入一个数据库驱动包

10.3、第一个JDBC程序

创建测试数据库

1.创建一个普通项目

2.导入数据库驱动 add as library

3.编写测试代码

public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //1.加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.用户信息和url
        String url = "jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&characterEncoding=utf8&useSSL=true";
        String username = "root";
        String password = "123456";

        //3.连接成功,数据库对象  Connection 代表数据库
        Connection connection = DriverManager.getConnection(url, username, password);
        //4.执行SQL对象
        Statement statement = connection.createStatement();

        //5.执行SQL对象 去执行SQL,可能存在结果,查看返回结果
        String sql = "select * from users";
        //返回的结果集,结果集中封装了我们全部的查询出来的结果
        ResultSet resultSet = statement.executeQuery(sql);

        while (resultSet.next()){
            System.out.println(resultSet.getObject(1)+" "+resultSet.getObject(2)+" "+resultSet.getObject(3)+" "+resultSet.getObject(4)+" "+resultSet.getObject(5));
        }

        //6.释放连接
        resultSet.close();
        statement.close();
        connection.close();
    }

步骤总结:

  1. 加载驱动
  2. 连接数据库Drivermanager
  3. 获得执行sql对象Statement
  4. 获得返回结果集
  5. 释放连接

10.4、statement对象

jdbc中的statement对象用于向数据库发送SQL语句,完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可

statement对象的executeUpdate方法,用于向数据库发送增删改的sql语句,执行完后,返回一个整数,即影响的行数

executeQuery用于向数据库发送查询语句,返回代表查询结果的ResultSet对象。

代码实现

1、提取工具类

package com.liang.sql1;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * @Author lwj
 * @Data 2022/01/2022/1/24 15:37
 */
public class JdbcUtils {
    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;
    static{
        try {
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(in);
            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");
            //1. 驱动只用加载一次
            Class.forName(driver);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取链接
     * @return 返回连接
     * @throws SQLException 数据库异常
     */
    public static Connection getConnection() throws SQLException {
        Connection connection = DriverManager.getConnection(url, username, password);
        return connection;
    }

    /**
     * 释放资源
     * @param connection 连接
     * @param statement 语句
     * @param rs 结果集
     */
    public static void relese(Connection connection, Statement statement, ResultSet rs){
        if (rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (statement != null){
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (connection != null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }


    }
}

2、编写增删改方法,executeUpdate

package com.liang.sql1;

import com.sun.tools.javac.Main;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * @Author lwj
 * @Data 2022/01/2022/1/24 15:54
 */
public class TestUtil {
    public static void main(String[] args) {
        Connection connection = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            //获取数据库连接
            connection = JdbcUtils.getConnection();
            //获得SQL的执行对象
            st = connection.createStatement();
            //增
            String sql = "insert into users(id,name,password,email,birthday) values (4,'lwj','123456','123456@qq.com','2022-01-01')";
            //删
            String sql = "delete from users where id = 4";
            //改
            String sql = "update users set username = 'lwj',email = '123@123.com' where = id = 3";
            int i = st.executeUpdate(sql);
            if (i>0){
                System.out.println("插入成功");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.relese(connection,st,rs);
        }
    }
}

3.查询

package com.liang.sql1;

import java.sql.*;

/**
 * @Author lwj
 * @Data 2022/01/2022/1/24 16:14
 */
public class TestSelect {
    public static void main(String[] args) {
        Connection connection = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            connection = JdbcUtils.getConnection();
            st = connection.createStatement();
            //SQL
            String sql = "select * from users";
            //查询,返回结果集
            rs = st.executeQuery(sql);
            while (rs.next()){
                System.out.println(rs.getObject(1)+" "+rs.getObject(2)+" " + rs.getObject(3)
                +" " + rs.getObject(4)+ " " + rs.getObject(5));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.relese(connection,st,rs);
        }
    }
}

SQL注入的问题

sql存在漏洞,会被攻击导致数据泄露 sql会被拼接 or

package com.liang.sql1;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Stack;

/**
 * @Author lwj
 * @Data 2022/01/2022/1/24 16:39
 */
public class TestLogin {
    public static void main(String[] args) {
        //loging("lwj","123456");
        loging("'or '1=1","'or '1=1");
    }
    public static void loging(String username ,String password){
        Connection connection = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            connection = JdbcUtils.getConnection();
            st = connection.createStatement();
            String sql = "select * from users where Name = '"+ username+"' and password = '"+ password+"'";
            rs = st.executeQuery(sql);
            while (rs.next()){
                System.out.println(rs.getObject("name")+" " + rs.getObject("password"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


}

10.5、PreparedStatement对象

PreparedStatement可以防止sql注入,效率更高

1、更改

package com.liang.sql2;

import com.liang.sql1.JdbcUtils;

import java.sql.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;

/**
 * @Author lwj
 * @Data 2022/01/2022/1/24 16:47
 */
public class TestInsert {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement pst = null;
        try {
            connection = JdbcUtils.getConnection();
            //区别于statement
            //使用?占位符代替参数
            //增加
            String sql = "insert into users(id,name,password,email,birthday) values (?,?,?,?,?)";
            //删除
            //String sql = "delete from users where id = ?";
            //pst = connection.prepareStatement(sql);
            //手动给参数赋值
            //pst.setInt(1,4);
            //修改
            //String sql = "update users set username = ?,email = ? where = id = ?";
            //pst = connection.prepareStatement(sql);
            //手动给参数赋值
            //pst.setString(1,"lll");
            //pst.setString(2,"123456@qq.com");
            //pst.setString(3,4);
            //预编译sql,先写sql,然后不执行.
            pst = connection.prepareStatement(sql);
            //手动给参数赋值
            pst.setInt(1,5);
            pst.setString(2,"lll");
            pst.setString(3,"123456");
            pst.setString(4,"123456@qq.com");
            //注意点:sql.Date
            //       util.Date
            pst.setDate(5,new java.sql.Date(new java.util.Date().getTime()));

            //执行
            int i = pst.executeUpdate();
            if (i>0){
                System.out.println("插入成功");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.relese(connection,pst,null);
        }
    }
}

2、查询

package com.liang.sql2;

import com.liang.sql1.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Author lwj
 * @Data 2022/01/2022/1/24 17:11
 */
public class TestSelect {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement pst = null;
        ResultSet rs  =null ;
        try {
            connection = JdbcUtils.getConnection();
            String sql ="select * from users where id = ?";
            pst = connection.prepareStatement(sql);
            //传递参数
            pst.setInt(1,1);
            //执行
            rs = pst.executeQuery();
            while (rs.next()){
                System.out.println(rs.getObject(1)+ " "+rs.getObject(2));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.relese(connection,pst,rs);
        }


    }
}

3、防止sql注入

package com.liang.sql2;

import com.liang.sql1.JdbcUtils;

import java.sql.*;

/**
 * @Author lwj
 * @Data 2022/01/2022/1/24 17:52
 */
public class TestSQL注入 {
    public static void main(String[] args) {
        //loging("lwj","123456");
        loging("''or '1=1'","''or '1=1'");
    }
    public static void loging(String username ,String password){
        Connection connection = null;
        PreparedStatement pst = null;
        ResultSet rs = null;
        try {
            connection = JdbcUtils.getConnection();
            //PreparedStatement防止sql注入的本质,把传递进来的参数当作字符
            //假设其中存在转义字符,就直接忽略,引号 ' 会被直接转译
            String sql = "select * from users where Name = ? and password = ?";
            pst = connection.prepareStatement(sql);
            pst.setString(1,username);
            pst.setString(2,password);
            rs = pst.executeQuery();
            while (rs.next()){
                System.out.println(rs.getObject("name")+" " + rs.getObject("password"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.relese(connection,pst,rs);
        }
    }


}

10.7、使用idea连接数据库

在idea的右测边栏中的database可以进行数据库连接

数据查看双击表

10.8、事务

要么都成功,要么都失败

ACID原则

原子性:要么一起成功,要么都不完成

一致性:总数不变

隔离性:多个进程互不干扰

持久性:一旦提交不可逆,持久化到数据库中

隔离性的问题:

脏读:一个事务读取另一个没有提交的事务

不可重复读:在同一个事务内,重复读取表中数据

虚读(幻读):在同一个事物内,读取到了别人插入的数据,导致前后读出来的结果不一致。

代码实现

package com.liang.sql2;

import com.liang.sql1.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Author lwj
 * @Data 2022/01/2022/1/24 18:29
 */
public class TestTransaction {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement pst = null;
        ResultSet rs = null;
        try {
            connection = JdbcUtils.getConnection();
            //关闭数据库的自动提交,自动开启事务
            connection.setAutoCommit(false);
            //
            String sql1 = "update account set money = money-100 where name = 'A'";
            String sql2 = "update account set money = money-100 where name = 'B'";
            pst =  connection.prepareStatement(sql1);
            pst.executeUpdate();
            pst = connection.prepareStatement(sql2);
            pst.executeUpdate();
            //业务完毕,提交事务
            connection.commit();
            System.out.println("操作成功");


        } catch (SQLException e) {
            try {
                //失败则回滚
                connection.rollback();
            } catch (SQLException ex) {
                //若失败默认回滚
                ex.printStackTrace();
            }
            e.printStackTrace();
        }finally {
            JdbcUtils.relese(connection,pst,rs);
        }
    }
}

1.开启事务

2.一组事务执行完毕,提交事务

3.可以在catch语句中显示定义回滚语句,但默认失败就会回滚。

10.9、数据库连接池

数据库连接–执行完毕–释放

连接到释放 十分的浪费系统资源

池化技术:准备一些预先的资源,过来就连接预先准备好的

常用连接数:10个

最小连接数:10个,根据常用连接数来设定

最大连接数:100个 业务最高承载上限

超过100就排队等待

等待超时:100ms

编写连接池,实现一个接口DataSource

开放数据源实现

DBCP

C3P0

Druid:阿里巴巴

使用了这些数据库连接之后,我们在项目开发中就不需要编写连接数据库的代码了!

DBCP

需要用jar包

commons - dbcp/logging/pool

C3P0

需要用jar包

c3p0-0.9.5.5.jar mchange-commons-java-0.2.19.jar

结论

无论使用上面数据源,本质还是一样的,DataSource接口不变,方法就不会变。


文章作者: 半页
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 半页 !
评论
  目录