1、初始MySQL
2、操作数据库
2.1、操作数据库
-- 创建数据库
CREATE DATABASE [IF EXISTS] westos
-- 删除数据库
DROP DATABASE [IF EXISTS] westos
-- 使用数据库
-- 若表明或字段名是特殊字符就需要加 `
USE `school`
-- 查看所有的数据库
SHOW DATABASES
2.2、数据库列类型
数值
- tinyint 十分小的数据 一个字节
- smallint 较小的数据 两个字节
- mediumint 中等大小的数据 三个字节
-
int 标准的整数 四个字节
- bigint 较大的数据 八个字节
- float 浮点数 四个字节
- double 浮点数 八个字节 (精度问题)
- decimal 字符串形式的浮点数 金融计算的时候使用
字符串
- char 字符串固定大小的 0~255
-
varchar 可变字符串 0~65535
- tinytext 微型文本 2^8 – 1
-
text 文本串 2^16 – 1
保存大文本
时间日期
java.util.data
- date YYYY-MM-DD,日期
- time HH:mm:ss,时间格式
-
datetime YYYY-MM-DD HH:mm:ss 最长用的时间格式
-
timestamp 时间戳 1970.1.1到现在的毫秒数
- year 年份表示
null
- 没有值
- 注意不要使用null进行运算
2.3、数据库字段属性
Unsigned:
- 无符号整数
- 声明了该列不能为负数
zerofill:
- 0填充的
- 不足的位数,使用0来填充 int 长度3 5 005
自增:
- 自动在上一条记录的基础上加一
- 通常用来设置唯一的主键 index ,必须是整数类型
- 可以自定义设置起始值和步长
非空 NULL not null
- 设置为非空如果不给赋值便会报错
- null,如果不填写值就默认为null
默认:
- 设置默认的值
-- 拓展(未来做项目用,表示一个记录存在的意义)
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文件,以及上级目录
-
MYISAM对应文件
- *.frm 表结构的定义文件
- *.MYD 数据文件(data)
- *.MYI 索引文件
设置数据库表的字符集编码
CHARSET=utf8
不设置的话,会是mysql默认的Latin1(不支持中文)
2.6、修改删除表
修改
-- 修改表名:ALTER TABLE 旧表名 RENAME AS 新表名
ALTER TABLE student RENAME AS student1
-- 增加表的字段:ALTER TABLE 表名 add 字段名 列属性
ALTER TABLE student1 ADD age INT(11)
-- 修改表的字段(重命名,修改约束)
-- ALTER TABLE 表名 MODIFY 字段名 列属性[]
ALTER TABLE student1 MODIFY age VARCHAR(11) -- 修改约束
-- ALTER TABLE 表名 CHANGE 旧名字 新名字 列属性[]
ALTER TABLE student1 CHANGE age age1 INT(1) -- 字段重命名
-- 删除表的字段
-- ALTER TABLE 表名 DROP 字段名
ALTER TABLE student1 DROP age1
-- 删除表 (如果不存在)
DROP TABLE IF EXISTS student1
所有创建和删除操作尽量加上判断
3、MySQL数据管理
3.1、外键
constraint 约束
方式一、在创建表的时候,增加约束(比较复杂)
CREATE TABLE `grade`(
`gradeid` INT(10) NOT NULL AUTO_INCREMENT COMMENT '年级id',
`gradename` VARCHAR(50) NOT NULL COMMENT '年级名称',
PRIMARY KEY (`gradeid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
-- 学生表的 gradeid 字段 要去引用年级表的 gradeid
-- 定义外键 key
-- 给外键添加约束 (执行引用) reference 引用
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 '出生日期',
`gradeid` INT(10) NOT NULL COMMENT '学生的年级',
`address` VARCHAR(100) DEFAULT NULL COMMENT '家庭住址',
`email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY(`id`),
KEY `FK_gradeid` (`gradeid`),
CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
删除外键关系的时候,必须先删除引用别人的表(从表),再删除被引用的表(主表)
方式二:创建表成功后添加外键约束
CREATE TABLE `grade`(
`gradeid` INT(10) NOT NULL AUTO_INCREMENT COMMENT '年级id',
`gradename` VARCHAR(50) NOT NULL COMMENT '年级名称',
PRIMARY KEY (`gradeid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
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 '出生日期',
`gradeid` INT(10) NOT NULL COMMENT '学生的年级',
`address` VARCHAR(100) DEFAULT NULL COMMENT '家庭住址',
`email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
-- 创建表的时候没有外键关系
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、添加
insert
-- 插入语句(添加)-- insert into 表名 ([字段名1,字段名2...])values('值1'),('值2'),(...)INSERT INTO `grade`(`gradename`) VALUES('大四')-- 一般写插入语句,数据和字段要保证一一对应-- 插入多个字段INSERT INTO `grade`(`gradename`) VALUES('大二'),('大一') INSERT INTO `student`(`name`) VALUES('张三')INSERT INTO `student`(`name`,`pwd`,`sex`) VALUES('张三','aaaaa','男'),('李四','bbbb','男'),('王五','aaaaa','女')
语法 :
insert into 表名 ([字段名1,字段名2...])values('值1'),('值2'),...
注意事项:
- 字段和字段之间用英文逗号隔开
- 字段名可以省略,但是后面的值必须要一一对应
3.4、修改
update 修改
-- 修改学员的名字
UPDATE `student` SET `name`='阳光就在天上' WHERE id=1;
-- 不指定条件的情况下,会改变所有的表
UPDATE `student` SET `name`='阳光就在天上'
-- 修改多个属性,逗号隔开
UPDATE `student` SET `name`='阳光就在天上',`email`='123456.com' WHERE id=1;
语法:
UPDATE 表名 SET colnum_name = VALUE WHERE 条件
colnum_name 是数据库的列
条件:where子句
3.5 删除
delete 删除
语法:
delete from 表名 [where 条件]
-- 删除指定数据
DELETE FROM `student` WHERE id = 1
TRUNCATE 命令
作用:完全清空数据库表,表的结构和索引约束不会变
delete 和 TRUNCATE 的区别
- 相同点:都不会删除表结构
-
不同点:
- TRUNCATE 会重新设置 自增列 计数器会清零
- TRUNCATE 不会影响事务
-- delete 和 TRUNCATE 的区别CREATE TABLE `test`( `id` INT(4) NOT NULL AUTO_INCREMENT, `coll` VARCHAR(20) NOT NULL, PRIMARY KEY(`id`))ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO `test`(`coll`) VALUES('1'),('2'),('3')-- 不会影响自增DELETE FROM `test`-- 自增会清零TRUNCATE TABLE `test`
delete删除的问题
,重启数据库,现象
- innoDB 自增列会从一开始(存在内存中,断电即失)
- MyISAM 继续从上一个子增量开始(存在文件中的,不会丢失)
4、DQL查询数据
4.1、DQL
Data Query Language:数据查询语言(select)
select 完整语法:
SELECT [ALL | DISTINCT]{ * | table.* | [table.field1[AS alias1],[table.field2[AS alias2]][....]]}FROM table_name [AS table_alias] [LEFT | RIGHT | INNER JOIN table_name2] -- 联合查询 [WHERE ...] -- 指定结果满足条件 [GROUP BY ...] -- 指定结果按照那几个字段分组 [HAVING] -- 过滤分组记录必须要满足的次要条件 [ORDER BY] -- 指定查询记录按照一个或多个条件排序 [LIMIT {[OFFSET,]ROW_COUNT | row_countOFFSET OFFSET}]; -- 指定查询的记录从哪条到哪条
4.2、查询指定字段
语法:
SELECT 字段 FROM 表
SELECT * FROM student-- 别名,给结果起一个名字 ASSELECT `student` AS 学号,`name` AS 学生姓名 FROM student-- 函数 concat(a,b)SELECT CONCAT('姓名:',`name`) AS 新名字 FROM student
去重 distinct
作用:去除select查询出来的结果的重复数据,只显示一条
SELECT `gradeid` FROM student
-- 发现重复数据 去重
SELECT DISTINCT `gradeid` FROM student
数据库的列(表达式)
-- 查询系统的版本(函数)
SELECT VERSION()
-- 用来计算(表达式)
SELECT 100*3-1 AS 计算结果
-- 查询自增的步长(变量)
SELECT @@auto_increment_increment
-- 学员成绩加分
SELECT `gradeid`+1 AS '加分后' FROM student
数据库中的 表达式:文本值,列,NULL,函数,计算表达式,系统变量…
select
表达式
from 表
4.3、where条件子句
作用:检索数据中符合条件的值
逻辑运算符
运算符 | 语法 | 描述 |
---|---|---|
and && | a and b a&&b | 逻辑与 |
or || | a or b a||b | 逻辑或 |
not ! | not a !a | 逻辑非 |
SELECT `name` ,`pwd` FROM student WHERE `name`='张三' AND `pwd`='aaaaa'SELECT `name` ,`pwd` FROM student WHERE `name`='张三' && `pwd`='aaaaa'-- 模糊查询(区间范围)SELECT `name`,`gradeid` FROM student WHERE `gradeid` BETWEEN 2 AND 3SELECT `name`,`gradeid` FROM student WHERE `gradeid`!=3
模糊查询:比较运算符
运算符 | 语法 | 描述 |
---|---|---|
IS NULL | a is null | 若操作符结果为NULL,结果为真 |
IS NOT NULL | a is not null | 若操作符结果不为NULL,结果为真 |
BETWEEN | a between b and c | 若a在b和c之间,则结果为真 |
LIke | a like b | SQL 匹配,如果a匹配b,则结果为真 |
IN | a in (a1,a2,a3…) | 若a在其中的某一个,则结果为真 |
-- 查询姓阳的同学-- like 结合 %(代表0到任意个字符) _(一个字符)SELECT `name`,`gradeid` FROM student WHERE `name` LIKE '阳%'-- 名字后面只有一个字的SELECT `name`,`gradeid` FROM student WHERE `name` LIKE '阳_'-- 查询名字中间有光字的同学SELECT `name`,`gradeid` FROM student WHERE `name` LIKE '%光%'-- in(具体的一个或多个值)-- 查询范围内学生SELECT `gradeid`,`name` FROM student WHERE `gradeid` IN (2,5,3)-- 查询地址为空的学生 null ''SELECT `name`,`birthday` FROM student WHERE `birthday`='' OR `birthday`IS NULL
4.4、联表查询
-- 联表查询
/*
分析需求,分析查询的字段来自哪些表(连接查询)
确定交叉点(两个表的数据哪里是相同的)
判断条件
*/
SELECT s.gradeid FROM student AS s
INNER JOIN grade AS g
WHERE s.gradeid = g.gradeid
SELECT s.gradeid FROM student AS s
RIGHT JOIN grade AS g
ON s.gradeid = g.gradeid
SELECT s.gradeid FROM student AS s
LEFT JOIN grade AS g
ON s.gradeid = g.gradeid
操作 | 描述 |
---|---|
Inner join | 如果表中至少有一个匹配,便返回 |
left join | 会从左表中返回所有的值,即使右表中没有匹配 |
reght join | 会从右表中返回所有的值,即使左表中没有匹配 |
4.5、自连接
自己的表和自己的表连接,一张表拆为两张表
SELECT a.`gradeid` AS '父栏目',b.`gradeid` AS '子栏目'
FROM `student` AS a,`student` AS b
WHERE a.`gradeid` = b.`gradeid`
4.6、分页和排序
排序
-- 分页 limit 和排序 order by -- 排序 升序 asc 降序 desc SELECT `gradeid` FROM student ORDER BY `gradeid` DESC SELECT `gradeid` FROM student ORDER BY `gradeid` ASC
分页
-- 分页:缓解数据库压力,更好的体验-- limit 起始值 页面的大小(pagesize)-- 【第N页 limit (n-1)*pagesize】-- 【pagesize 页面大小】-- 【(n-1)*pagesize 起始值 】-- 【n当前页】-- 【数据总数/页面大小 = 总页数】 SELECT `gradename` FROM `grade` LIMIT 3,2
4.7、子查询
where (值是固定的)
本质:在where语句中嵌套一个子查询语句
5、常用函数
5.1、常用函数
SELECT ABS(-2) -- 绝对值
SELECT CEILING(9.4) -- 向上取整
SELECT FLOOR(9.4) -- 向下取整
SELECT RAND() -- 0-1之间随机数
SELECT SIGN(-1) -- 判断一个数的符号
-- 字符串函数
SELECT CHAR_LENGTH('阳光就在天上') -- 字符串长度
SELECT CONCAT('我','高') -- 拼接字符串
SELECT INSERT('阳光就在天上',1,2,'天上') -- 查询,替换
SELECT LOWER('YangGuang') -- 小写字母
SELECT UPPER('YangGuang') -- 大写字母
SELECT INSTR('yangguang','h') -- 返回第一次出现的字串的索引
SELECT REPLACE('阳光就在天上','坚持','努力') -- 替换出现的指定字符串
SELECT SUBSTR('阳光就在天上',4,6) -- 返回指定字符串(源字符串)
SELECT REVERSE('阳光就在天上') -- 反转
SELECT REPLACE(`name`,'李','大') FROM student
WHERE `name` LIKE '李%'
-- 时间和日期函数
SELECT CURRENT_DATE() -- 获取当前日期
SELECT CURDATE()
SELECT NOW()
SELECT LOCALTIME() -- 本地时间
SELECT SYSDATE() -- 系统时间
-- 系统
SELECT SYSTEM_USER()
SELECT USER()
SELECT VERSION()
5.2、聚合函数
函数名称 | 描述 |
---|---|
count() | 计数 |
sum() | 求和 |
AVG() | 平均值 |
MAX() | 最大值 |
MIN() | 最小值 |
… | … |
-- 聚合函数
SELECT COUNT(`name`) FROM student -- count(字段),会忽略所有的null值
SELECT COUNT(*) FROM student -- count(*),不会忽略null值,本质计算行数
SELECT COUNT(1) FROM student -- count(1),不会忽略null值,本质计算行数
SELECT SUM(`gradeid`) AS 总和 FROM `student`
SELECT AVG(`gradeid`) AS 平均分 FROM `student`
SELECT MAX(`gradeid`) AS 最高分 FROM `student`
SELECT MIN(`gradeid`) AS 最低分 FROM `student`
5.3、数据库级别的MD5加密
MD5: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) WHERE id=1
UPDATE `testmd5` SET pwd=MD5(pwd)
-- 插入的时候加密
INSERT INTO `testmd5` VALUES(4,'xioaming',MD5('123456'))
-- 如何校验:将用户传递的密码进行MD5加密,然后比对加密后的值
SELECT * FROM `testmd5` WHERE `name`='xiaoming' AND pwd=MD5('123456')
6、事务
将一组SQL放在一个批次中执行
事务原则:ACID原则 原子性(Atomicity),一致性(Consistency),隔离性(Isolation),持久性(Durability) (脏读,幻读)
原子性(Atomicity)
要么都成功,要么都失败
一致性(Consistency)
事务前后的数据完整性要保持一致
持久性(Durability)
–事务提交
事务一旦提交则不可逆,被持久化到数据库中
隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干预,事务之间要相互隔离
隔 离所导致的问题:
脏读:
指一个事务读取了另一个事务未提交的数据。
不可重复读:
在一个事务内读取表中的某一行数据,多次读取结果不同。(这个不一定是错误,只是某些场合不对)
虚读(幻读):
指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。
6.1、执行事务
-- 事务
-- 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 CHARACTER SET utf8 COLLATE utf8_general_ci
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、索引
索引(index)是帮助MySQL高效获得数据的数据结构
7.1、索引的分类
在一个表中主键索引只能有一个,唯一索引可以有多个
-
主键索引 (PRIMARY KEY)
- 唯一表示,主键不可重复,只能有一个列作为主键
-
唯一索引 (UNIQUE KEY)
- 避免重复列出现,唯一索引可以重复,多个列都可以表示唯一索引
-
常规索引 (KEY/INDEX)
- 默认的,index,key关键字设置
-
全文索引 (FullText)
- 在特定的数据库引擎下才有,MyISAM
- 快速定位数据
-- 索引的使用
-- 1、创建表的时候增加索引
-- 2、创建完毕后,增加索引
-- 显示所有的索引信息
SHOW INDEX FROM student
-- 增加一个索引
ALTER TABLE school.student ADD FULLTEXT INDEX `name`(`name`)
-- EXPLAIN 分析sql执行的状况
EXPLAIN SELECT * FROM student; -- 非全文索引
EXPLAIN SELECT * FROM student WHERE MATCH(`name`) AGAINST('阳')
7.2、测试索引
CREATE TABLE `app_user`(
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) DEFAULT '' COMMENT '用户昵称',
`email` VARCHAR(50) NOT NULL COMMENT '用户邮箱',
`phone` VARCHAR(20) DEFAULT '' COMMENT '手机号',
`gender` TINYINT(4) UNSIGNED DEFAULT '0' COMMENT '性别(0:男;1:女)',
`password` VARCHAR(100) NOT NULL COMMENT '密码',
`age` TINYINT(4) DEFAULT '0' COMMENT '年龄',
`update_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='app用户表'
-- 插入100万条数据
DELIMITER $$ -- 写函数前必写,标志
CREATE FUNCTION mock_data()
RETURNS INT
BEGIN
DECLARE num INT DEFAULT 1000000;
DECLARE i INT DEFAULT 0;
WHILE i<num DO
INSERT INTO `app_user`(`name`,`email`,`phone`,`gender`,`password`,`age`)
VALUES(CONCAT('用户',i),'123456',
CONCAT('18',FLOOR(RAND()*((999999999-100000000)+100000000))),
FLOOR(RAND()*2),UUID(),FLOOR(RAND()*100));
SET i = i+1;
END WHILE;
RETURN i;
END;
SELECT mock_data();
SELECT * FROM app_user WHERE `name`='用户9999'; -- 执行耗时: 0.552 sec
EXPLAIN SELECT * FROM app_user WHERE `name`='用户9999';
-- creat index 索引名 on 表(字段)
CREATE INDEX id_app_user_name ON app_user(`name`);
SELECT * FROM app_user WHERE `name`='用户9999'; -- 执行耗时: 0.004 sec
7.3、索引原则
- 索引不是越多越好
- 不要对经常变动的数据加索引
- 小数据量的表不需要加索引
- 索引一般加在常用来查询的字段上
索引的数据结构 (https://blog.codinglabs.org/articles/theory-of-mysql-index.html)
Hash类型的索引
Btree:InnoDB默认数据结构
8、数据库备份
8.1、用户管理
SQL yog 可视化管理
- 通过用户管理器添加用户
SQL 命令操作
-
用户表:mysql.user
- 本质:对这张表增删改查
-- 创建用户 CREATE USER 用户名 IDENTIFIED BY '密码'
CREATE USER xiong IDENTIFIED BY '123456'
-- 修改密码(修改当前用户密码)
SET PASSWORD = PASSWORD('123456')
-- 修改密码(修改指定用户密码)
SET PASSWORD FOR xiong = PASSWORD('123456')
-- 重命名 RENAME USER 原来名字 TO 新名字
RENAME USER xiong TO xiaoxiong
-- 用户授权 GRANT ALL PRIVILEGES 全部的权限(给他人授权除外),库.表
GRANT ALL PRIVILEGES ON *.* TO xiaoxiong
-- 查看权限
SHOW GRANTS FOR xiaoxiong
SHOW GRANTS FOR root@localhost -- root用户的权限
-- 撤销权限 revoke
REVOKE ALL PRIVILEGES ON *.* FROM xiaoxiong
-- 删除用户
DROP USER xiaoxiong
8.2、MySQL备份
MySQL数据库备份的方式:
-
拷贝物理文件
-
在可视化工具中手动导出
-
使用命令行导出 mysqldump 命令行使用
-
# mysqldump -h主机 -u用户 -p密码 数据库 表名 >物理磁盘位置/文件名 mysqldump -hlocalhost -uroot -p123456 school student >D:/a.sql # 导入 # source 备份文件 source D:/a.sql
-
9、规范数据库设计
9.1、设计的优略
糟糕的数据库设计:
-
数据冗余,浪费空间
-
数据插入删除麻烦,(屏蔽使用物理外键)
-
程序的性能差
良好的数据库设计:
- 节省内存空间
- 保证数据库完整性
- 方便开发系统
软件开发中,关于数据库的设计:
- 分析需求:分析业务和需要处理的数据库的需求
- 概要设计:设计关系图E-R图
9.2、三大范式
数据不规范的后果:
- 信息重复
- 更新异常
-
插入异常
- 无法正常显示信息
-
删除异常
- 丢失有效信息
三大范式
第一范式(1NF)
- 原子性:要求数据库表每一列都是不可再分
第二范式(1NF)
前提:满足第一范式
- 每张表只描述一件事情
第三范式(1NF)
前提:满足第一第二范式
- 第三范式需要确保数据表中每一列数据数据都和主键直接相关,而且不能间接相关
规范性和性能的问题
关联查询的表不得超过三张表
- 考虑商业化需求和目标(成本,用户体验)数据库的性能更加重要
- 在处理性能的问题 的时候,需要适当考虑规范性
- 故意给某些表增加一些冗余的字段(从多表查询变为单表查询)
- 故意增加一些计算列(从大数据量降低为小数据量的查询:索引)
10、JDBC
10.1、数据库驱动
我们的程序会通过
数据库驱动
和数据库进行交互!!
10.2、JDBC
SUN公司为了简化开发人员的(对数据库的统一)操作,提供了一个(Java操作数据库的)规范,叫JDBC
10.3、第一个JDBC的程序
创建测试数据库
CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;
USE jdbcStudy;
CREATE TABLE users(
id INT PRIMARY KEY,
`name` VARCHAR(40),
PASSWORD VARCHAR(40),
email VARCHAR(60),
birthday DATE
);
INSERT INTO users(id,`name`,PASSWORD,email,birthday)
VALUES(1,'zhansan','123456','zs@sina.com','1980-12-04'),
(2,'lisii','123456','lisi@sina.com','1981-12-04'),
(3,'wangwu','123456','wangwu@sina.com','1979-12-04');
步骤:
- 加载驱动
- 连接数据库 DriverManager
- 获得执行sql的对象 Statement
- 获得返回的结果集
- 释放连接
import java.sql.*;
public class JdbcFirstDemo {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//用户信息和url
//useUnicode=true&characterEncoding=utf8&useSSL=true
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
String username = "root";
String password = "123456";
//连接成功,返回数据库对象 Connection代表数据库
Connection connection = DriverManager.getConnection(url, username, password);
//执行SQL的对象 statement 执行SQL的对象
Statement statement = connection.createStatement();
//执行SQL,查看返回结果
String sql = "SELECT * FROM users";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()){
System.out.println("id = " + resultSet.getObject("id"));
System.out.println("name = " + resultSet.getObject("name"));
System.out.println("pwd = " + resultSet.getObject("password"));
System.out.println("email = " + resultSet.getObject("email"));
System.out.println("birth = " + resultSet.getObject("birthday"));
}
//断开数据库
resultSet.close();
statement.close();
connection.close();
}
}
DriverManager
//DriverManager.registerDriver(new Driver());
Class.forname("com.mysql.jabc.Driver");//加载驱动
Connection connection = DriverManger.getConnection(url,username,password);
//connection 代表数据库
connection.rollback(); //数据库设置回滚
connection.commit(); //事务提交
connection.setAutoCommit(); //数据库设置自动提交
URL
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
//mysql -- 3306
//协议://主机地址:端口号/数据库名?参数1&参数2
//oralce -- 1521
//jdbc:oracle:thin:@localhost:1521:sid
Statement 执行SQL的对象
statement.executeQuery(); //查询操作
statement.execute(); //执行任何SQL
statement.executeUpdat(); //更新,插入,删除 返回受影响的行数
ResultSet 查询的结果集,封装了所有的查询结果
resultSet.getObject(); //在不知道类型的情况下使用
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getDate();
遍历:指针
resultSet.beforeFirst(); //移动到最前面
resultSet.afterLast(); //移动到最后面
resultSet.next(); //移动到下一个数据
resultSet.previous(); //移动到前一行
释放资源
resultSet.close();
statement.close();
connection.close();
10.4、statement对象
jdbc中的statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向书库发送增删改查的语句即可。
Statement对象的executeUpdate方法,用于向数据库发送增,删,改的sql语句,executeUpdated执行完成后,将返回一个整数(该操作执行完成后几行数据发生了变化)。
Statement.executeQuery方法用于向数据库发送查询语句,executeQuery方法返回代表查询结果的ResultSet对象。
提取工具类
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");
//驱动只需要加载一次
Class.forName(driver);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,username,password);
}
//释放连接资源
public static void release(Connection conn, Statement st, ResultSet rs) {
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(st!=null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
//其中的db.properties文件内容
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456
CRUD操作-create
使用executeUpdate(String sql)方法完成数据的添加操作:
public class TestInsert {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();//获取数据库连接
st = conn.createStatement();//获得SQL的执行对象
String sql = "INSERT INTO users(id,`name`,`password`,`email`,`birthday`)" +
"VALUES(4,'xiong','123456','12348886@.com','2020.04.12')";
int i = st.executeUpdate(sql);
if(i>0){
System.out.println("插入成功");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
CRUD操作-delete
使用executeUpdate(String sql)方法完成数据删除操作:
public class TestDelete {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();//获取数据库连接
st = conn.createStatement();//获得SQL的执行对象
String sql = "DELETE FROM users WHERE id=4";
int i = st.executeUpdate(sql);
if(i>0){
System.out.println("删除成功");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
CRUD操作-update
使用executeUpdate(String sql)方法完成数据修改操作:
public class TestUpdate {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();//获取数据库连接
st = conn.createStatement();//获得SQL的执行对象
String sql = "UPDATE users SET `name` = 'xiong' WHERE id=2";
int i = st.executeUpdate(sql);
if(i>0){
System.out.println("更新成功");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
CRUD操作-read
使用executeQuery(String sql)方法完成数据查询操作:
public class TestSelect {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
st = conn.createStatement();
String sql = "select * from users where id = 1";
rs = st.executeQuery(sql);
while(rs.next()){
System.out.println(rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
SQL注入问题
sql存在漏洞,会导致数据泄漏。
public class SqlInto {
public static void main(String[] args) {
//正确的登陆login("xiong","123456");
login(" ' or '1=1"," 'or'1=1");
}
public static void login(String username,String password){
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
st = conn.createStatement();
rs = st.executeQuery("select * from users where name = '" + username + "' and password = '"+ password+"'");
while(rs.next()){
System.out.println(rs.getString("name"));
System.out.println(rs.getString("password"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
10.5、PreparedStatement对象
PreparedStatement可以防止SQL注入,并且效率更高
防止SQL注入的本质:
- 将传递进来的参数当作字符,假设其中有转义字符,就直接忽略。
- 增加
public class TestInsert {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
try {
conn = JdbcUtils.getConnection();
//使用?占位符代替参数
String sql = "insert into users(id,`name`,`password`,`email`,`birthday`)values(?,?,?,?,?)";
st = conn.prepareStatement(sql);//预编译SQL,先写但是不执行
st.setInt(1,4);
st.setString(2,"yuan");
st.setString(3,"16515661");
st.setString(4,"+64654894@.com");
//sql.Date 数据库
//utils.Date JAVA
st.setDate(5,new java.sql.Date(new Date().getTime()));
//执行
int i = st.executeUpdate();
if(i>0){
System.out.println("插入成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,null);
}
}
}
- 删除
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestDelete {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
try {
conn = JdbcUtils.getConnection();
//使用?占位符代替参数
String sql = "delete from users where id = ?";
st = conn.prepareStatement(sql);//预编译SQL,先写但是不执行
st.setInt(1,4);
//执行
int i = st.executeUpdate();
if(i>0){
System.out.println("删除成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,null);
}
}
}
- 更新
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestUpdate {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
try {
conn = JdbcUtils.getConnection();
//使用?占位符代替参数
String sql = "update users set `name` = ? where id = ?;";
st = conn.prepareStatement(sql);//预编译SQL,先写但是不执行
st.setString(1,"dapangyuan");
st.setInt(2,1);
//执行
int i = st.executeUpdate();
if(i>0){
System.out.println("更新成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,null);
}
}
}
- 查询
public class TestSelect {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
String sql = "select * from users where id = ?";
st = conn.prepareStatement(sql);
st.setInt(1,1);//传递参数
//执行
rs = st.executeQuery();
if(rs.next()){
System.out.println(rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
10.6、数据库连接池
数据库的连接–释放耗费系统资源
池化技术:准备一些预先的资源
编写连接池:实现一个接口 DataSource
开源数据源实现
DBCP
C3P0
Druid:阿里巴巴
使用数据库连接池之后,在项目开发中就不需要编写连接数据库的代码
DBCP
需要用到的jar包:
commons-dbcp-1.4、commons-pool-1.6
dbcpconfig.properties //配置文件
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false
username=root
password=123456
#!-- 初始化连接 --
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 --
maxWait=60000
# JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:【属性名=property;】
#注意:user 与 password 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=UTF8
# 指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
# driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=
# driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
加载配置文件
import com.xiong.lesson02.utils.JdbcUtils;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils_DBCP {
private static DataSource dataSource =null;
static {
try{
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties properties = new Properties();
properties.load(in);
//创建数据源 工厂模式-->创建对象
dataSource = BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();//从数据源中获取连接
}
//释放连接资源
public static void release(Connection conn, Statement st, ResultSet rs) {
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(st!=null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
测试
import java.sql.*;
public class TestDBCP {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
try {
conn = JdbcUtils_DBCP.getConnection();
//使用?占位符代替参数
String sql = "insert into users(id,`name`,`password`,`email`,`birthday`)values(?,?,?,?,?)";
st = conn.prepareStatement(sql);//预编译SQL,先写但是不执行
st.setInt(1,4);
st.setString(2,"yuan");
st.setString(3,"16515661");
st.setString(4,"+64654894@.com");
//sql.Date 数据库
//utils.Date JAVA
st.setDate(5,new java.sql.Date(new java.util.Date().getTime()));
//执行
int i = st.executeUpdate();
if(i>0){
System.out.println("插入成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils_DBCP.release(conn,st,null);
}
}
}
C3P0
c3p0-config.xml //配置文件
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!--
C3P0的缺省(默认)配置,
如果在代码中“ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();”
这样写表示用的是C3P0的默认配置
-->
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">
jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
</default-config>
<!--
C3P0的命名配置:
如果在代码中“ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource(“mySource”);”
这样写表示使用的是name为mySource的配置
-->
<named-config name="mySource">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/school1</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
</named-config>
</c3p0-config>
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcUtils_C3P0 {
private static DataSource dataSource =null;
static {
try{
//配置文件配置
dataSource = new ComboPooledDataSource("MySQL");
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();//从数据源中获取连接
}
//释放连接资源
public static void release(Connection conn, Statement st, ResultSet rs) {
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(st!=null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
代码测试
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestC3P0 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
try {
conn = JdbcUtils_C3P0.getConnection();
//使用?占位符代替参数
String sql = "insert into users(id,`name`,`password`,`email`,`birthday`)values(?,?,?,?,?)";
st = conn.prepareStatement(sql);//预编译SQL,先写但是不执行
st.setInt(1,4);
st.setString(2,"yuan");
st.setString(3,"16515661");
st.setString(4,"+64654894@.com");
//sql.Date 数据库
//utils.Date JAVA
st.setDate(5,new java.sql.Date(new java.util.Date().getTime()));
//执行
int i = st.executeUpdate();
if(i>0){
System.out.println("插入成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils_C3P0.release(conn,st,null);
}
}
}
本质:DataSource接口