1、数据库分类
关系型数据库:(SQL)
- MySQL,Oracle,Sql Server,DB2,SQLite
- 通过表和表之间,行和列之间的关系进行数据的存储, 学员信息表,考勤表,……
非关系型数据库:(NoSQL) Not Only
- Redis,MongDB
- 非关系型数据库,对象存储,通过对象的自身的属性来决定。
连接数据库
命令行连接!
mysql -uroot -p123456 --连接数据库
update mysql.user set authentication_string=password('123456') where user='root' and Host = 'localhost'; -- 修改用户密码
flush privileges; -- 刷新权限
----------------------------------------
-- 所有的语句都使用;结尾
show databases; -- 查看所有的数据库
mysql> use school -- 切换数据库 use 数据库名
Database changed
show tables; -- 查看数据库中所有的表
describe student; -- 显示数据库中所有的表的信息
create database westos; -- 创建一个数据库
exit; --退出连接
-- 单行注释(SQL 的本来的注释)
/**/ 多行注释
数据库的列类型
数值
- tinyint 十分小的数据 1个字节
- smallint 较小的数据 2个字节
- mediumint 中等大小的数据 3个字节
- int 标准的整数 4个字节 常用的 int
- bigint 较大的数据 8个字节
- float 浮点数 4个字节
- double 浮点数 8个字节 (精度问题!)
- decimal 字符串形式的浮点数 金融计算的时候,一般是使用decimal
字符串
- char 字符串固定大小的 0~255
- varchar 可变字符串 0~65535 常用的变量 String
- tinytext 微型文本 2^8 - 1
- text 文本串 2^16 -1 保存大文本
时间日期
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
数据库的字段属性(重点)
Unsigned :
- 无符号的整数
- 声明了该列不能声明为负数
zerofill:
- 0填充的
- 不足的位数,使用0来填充, int(3), 5 --- 005
自增:
- 通常理解为自增,自动在上一条记录的基础上 + 1(默认)
- 通常用来设计唯一的主键~ index,必须是整数类型
- 可以自定义设计主键自增的起始值和步长
非空 NULL not null
- 假设设置为 not null ,如果不给它赋值,就会报错·
- NULL 如果不填值,默认就是null
2.4、创建数据库表(重点)
-- 目标 :创建一个school数据库
-- 创建学生表(列,字段) 使用SQL 创建
-- 学号int 登录密码varchar(20) 姓名,性别varchar(2),出生日期(datatime),家庭住址,email
-- 注意点,使用英文(),表的名称 和 字段 尽量使用 `` 括起来
-- 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
常用命令
SHOW CREATE DATABASE school -- 查看创建数据库的语句
SHOW CREATE TABLE student -- 查看student数据表的定义语句
DESC student -- 显示表的结构
数据表的类型
-- 关于数据库引擎
-
INNODB 默认使用~
-
MYISAM 早些年使用的 在数据库表引擎的选择上,MYISAM和INNODB存在诸多差异:
-
事务支持方面,MYISAM不支持而INNODB支持;
-
数据行锁定机制上,MYISAM不支持而INNODB支持;
-
外键约束上,MYISAM不支持而INNODB支持;
-
全文索引功能上,MYISAM支持而INNODB不支持;
-
表空间大小方面,MYISAM的表空间较小,INNODB的表空间较大(约为MYISAM的2倍)。
常规使用操作:
- MYISAM 节约空间,速度较快
- INNODB 安全性高,事务的处理,多表多用户操作
设置数据库表的字符集编码
CHARSET=utf8
不设置的话,会是mysql默认的字符集编码~ (不支持中文!)
MySQL的默认编码是Latin1,不支持中文
在my.ini中配置默认的编码
character-set-server=utf8
2.6、修改删除表
修改
-- 修改表名: ALTER TABLE 旧表名 RENAME AS 新表名
ALTER TABLE teacher RENAME AS teacher1
-- 增加表的字段: ALTER TABLE 表名 ADD 字段名 列属性
ALTER TABLE teacher1 ADD age INT(11)
-- 修改表的字段 (重命名,修改约束!)
-- ALTER TABLE 表名 MODIFY 字段名 列属性[]
ALTER TABLE teacher1 MODIFY age VARCHAR(11) -- 修改约束
-- ALTER TABLE 表名 CHANGE 旧名字 新名字 列属性[]
ALTER TABLE teacher1 CHANGE age age1 INT(1) -- 字段重命名
-- 删除表的字段: ALTER TABLE 表名 DROP 字段名
ALTER TABLE teacher1 DROP age1
删除
-- 删除表(如果表存在再删除)
DROP TABLE IF EXISTS teacher1
所有的创建和删除操作尽量加上判断,以免报错~
注意点:
`字段名,使用这个包裹!- 注释
--//**/ - sql 关键字大小写不敏感,建议大家写小写
- 所有的符号全部用英文
3、MySQL数据管理
3.1、外键(了解即可)
方式一、在创建表的时候,增加约束(麻烦,比较复杂)
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`),
KEY `FK_gradeid` (`gradeid`),
CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`)
)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 那个表(哪个字段)
以上的操作都是物理外键,数据库级别的外键,我们不建议使用!(避免数据库过多造成困扰)
最佳实践
- 数据库就是单纯的表,只用来存数据,只有行(数据)和列(字段)
- 我们想使用多张表的数据,想使用外键(程序去实现)
DML语言(全部记住)
数据库意义:数据存储,数据管理
DML 语言:数据操作语言
- Insert
- update
- delete
Insert添加数据
插入语句(添加)
-- insert into 表名(字段名1,字段2,字段3)values('值1'),('值2'),('值3',.....)
INSERT INTO `grade`(`gradename`) VALUES('大四')
-- 由于主键自增我们可以省略(如果不写表的字段,他就会一一匹配)
INSERT INTO `grade` VALUES('大三')
-- 一般写插入语句,我们一定要数据和字段一一对应!
-- 插入多个字段
INSERT INTO `grade`(`gradename`)
VALUES('大二'),('大一')
INSERT INTO `student`(`name`) VALUES ('张三')
INSERT INTO `student`(`name`,`pwd`,`sex`) VALUES ('张三','aaaaaa','男')
INSERT INTO `student`(`name`,`pwd`,`sex`)
VALUES ('李四','aaaaaa','男'),('王五','aaaaaa','男')
语法:insert into 表名(字段名1,字段2,字段3)values('值1'),('值2'),('值3',.....)
注意事项:
- 字段和字段之间使用 英文逗号 隔开
- 字段是可以省略的,但是后面的值必须要一一对应,不能少
- 可以同时插入多条数据,
VALUES后面的值,需要使用,隔开即可VALUES(),(),....
update修改
update 修改谁 (条件) set 原来的值 = 新值
-- 修改学员名字,带了简介
UPDATE `student` SET `name`='狂神' WHERE id = 1;
-- 不指定条件的情况下,会改动所有表!
UPDATE `student` SET `name`='长江7号'
-- 修改多个属性,逗号隔开
UPDATE `student` SET `name`='狂神',`email`='24736743@qq.com' WHERE id = 1;
-- 语法:
-- UPDATE 表名 set colnum_name = value,[colnum_name = value,...] where [条件]
- where子句可以有大于小于判断,between and
- 也可以通过and连接多个条件
3.5、删除
delete 命令
语法: delete from 表名 [where 条件]
-- 删除数据 (避免这样写,会全部删除)
DELETE FROM `student`
-- 删除指定数据
DELETE FROM `student` WHERE id = 1;
TRUNCATE 命令
作用: 完全清空一个数据库表,表的结构和索引约束不会变!
-- 清空 student 表
TRUNCATE `student`
4、DQL查询数据(最重要)
4.1、DQL
(Data Query LANGUAGE : 数据查询语言)
- 所有的查询操作都用它 Select
- 简单的查询,复杂的查询它都能做~
- 数据库中最核心的语言==
4.2、指定查询字段
-- 查询全部的学生 SELECT 字段 FROM 表
SELECT * FROM student
-- 查询指定字段
SELECT `StudentNo`,`StudentName` FROM student
-- 别名,给结果起一个名字 AS 可以给字段起别名,也可以给表起别名
SELECT `StudentNo` AS 学号,`StudentName` AS 学生姓名 FROM student AS s
-- 函数 Concat (a, b)
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 -- 查询自增的步长 (变量)
-- 学员考试成绩 + 1分查看
SELECT `StudentNo`,`StudentResult`+1 AS '提分后' FROM result
数据库中的表达式: 文本值,列,Null,函数,计算表达式,系统变量....
select 表达式 from 表
4.3、where 条件子句
作用: 检索数据中符合条件的值
搜索的条件由一个或者多个表达式组成!结果 布尔值
逻辑运算符
| 运算符 | 语法 | 描述 |
|---|---|---|
| and && | a and b a&&b | 逻辑与,两个都为真,结果为真 |
| or | ||
| Not ! | not a ! a | 逻辑非,真为假,假为真! |
-- ==================== where ====================
SELECT `studentNo`, `StudentResult` FROM `result`
-- 查询考试成绩在 95~100 分之间
SELECT `studentNo`, `StudentResult` FROM `result`
WHERE `StudentResult` >= 95 AND `StudentResult` <= 100
-- and &&
SELECT `studentNo`, `StudentResult` FROM `result`
WHERE `StudentResult` >= 95 && `StudentResult` <= 100
-- 模糊查询(区间)
SELECT `studentNo`, `StudentResult` FROM `result`
WHERE `StudentResult` BETWEEN 95 AND 100
-- 除了1000号学生之外的同学的成绩
SELECT `studentNo`, `StudentResult` FROM `result`
WHERE `studentNo` != 1000;
-- != not
SELECT `studentNo`, `StudentResult` FROM `result`
WHERE NOT `studentNo` = 1000
模糊查询 : 比较运算符
| 运算符 | 语法 | 描述 |
|---|---|---|
| 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在a1,或者a2.... 其中的某一个值中,结果为真 |
联表查询 JOIN
/* 思路
1. 分析需求,分析查询的字段来自哪些表,(连接查询)
2. 确定使用哪种连接查询? 7种
确定交叉点(这两个表中哪个数据是相同的)
判断的条件 : 学生表的中 studentNo = 成绩表 studentNo
*/
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 s
RIGHT JOIN result r
ON s.studentNO = r.studentNO
-- Left Join
SELECT s.studentNO,studentName,SubjectNo,StudentResult
FROM student s
LEFT JOIN result r
ON s.studentNO = r.studentNO
| 操作 | 描述 |
|---|---|
| Inner join | 返回的是两个表的交集数据,即同时满足连接条件的行 |
| left join | 会从左表中返回所有的值,即使右表中没有匹配 |
| right join | 会从右表中返回所有的值,即使左表中没有匹配 |
自连接
自己的表和自己的表连接,核心:一张表拆为两张一样的表即可
父类
| categoryid | categoryName |
|---|---|
| 2 | 信息技术 |
| 3 | 软件开发 |
| 5 | 美术设计 |
子类
| pid | categoryid | categoryName |
|---|---|---|
| 3 | 4 | 数据库 |
| 2 | 8 | 办公信息 |
| 3 | 6 | web开发 |
| 5 | 7 | ps技术 |
操作:如何操作一张表得到下面的查询父类对应的子类关系
| 父类 | 子类 |
|---|---|
| 信息技术 | 办公信息 |
| 软件开发 | 数据库 |
| 软件开发 | web开发 |
| 美术设计 | ps技术 |
-- 查询父子信息:把一张表看为两个一模一样的表
SELECT a.`categoryName` AS '父栏目',b.`categoryName` AS '子栏目'
FROM `category` AS a,`category` AS b
WHERE a.`categoryid` = b.`pid`
SQL 常用函数
一、常用数学函数
| 函数 | 说明 |
|---|---|
ABS(x) | 返回x的绝对值 |
CEIL(x) | 向上取整 |
FLOOR(x) | 向下取整 |
RAND() | 返回0-1的随机数 |
SIGN(x) | 返回x的符号,正数返回1,负数返回-1,0返回0 |
PI() | 返回圆周率 |
TRUNCATE(x, y) | 返回数值x保留到小数点后y位的值 |
ROUND(x) | 返回离x最近的整数(四舍五入) |
MOD(x, y) | 返回x除以y以后的余数 |
POW(x, y) | 返回x的y次方 |
SQRT(x) | 返回x的平方根 |
EXP(x) | 返回e的x次方 |
-- 示例
SELECT ABS(-10), -- 10
CEIL(4.3), -- 5
FLOOR(4.7), -- 4
RAND(), -- 随机数
ROUND(3.14159, 2), -- 3.14
MOD(10, 3); -- 1
二、常用字符串函数
| 函数 | 说明 |
|---|---|
CHAR_LENGTH(s) | 返回字符串s的字符数 |
LENGTH(s) | 返回字符串s的字节长度 |
CONCAT(s1, s2...) | 将字符串s1,s2等多个字符串合并为一个字符串 |
CONCAT_WS(x, s1, s2...) | 同CONCAT(s1,s2,...),但每个字符串之间要加上x |
UPPER(s) / UCASE(s) | 将字符串s的所有字母变成大写字母 |
LOWER(s) / LCASE(s) | 将字符串s的所有字母变成小写字母 |
LEFT(s, n) | 返回字符串s的前n个字符 |
RIGHT(s, n) | 返回字符串s的后n个字符 |
TRIM(s) | 去掉字符串s开始和结尾处的空格 |
REPLACE(s, s1, s2) | 将字符串s2替代字符串s中的字符串s1 |
SUBSTR(s, start, length) | 从字符串s的start位置截取长度为length的子字符串 |
REVERSE(s) | 将字符串s的顺序反过来 |
STRCMP(s1, s2) | 比较字符串s1和s2 |
-- 示例
SELECT CHAR_LENGTH('hello'), -- 5
CONCAT('Hello', ' ', 'World'), -- Hello World
UPPER('hello'), -- HELLO
LEFT('Hello World', 5), -- Hello
REPLACE('Hello World', 'World', 'SQL'), -- Hello SQL
SUBSTR('Hello World', 7, 5); -- World
三、日期和时间函数
| 函数 | 说明 |
|---|---|
CURDATE() / CURRENT_DATE() | 返回当前日期 |
CURTIME() / CURRENT_TIME() | 返回当前时间 |
NOW() / SYSDATE() | 返回当前日期和时间 |
YEAR(date) | 返回日期date的年份 |
MONTH(date) | 返回日期date的月份 |
DAY(date) | 返回日期date的天数 |
HOUR(time) | 返回时间time的小时值 |
MINUTE(time) | 返回时间time的分钟值 |
SECOND(time) | 返回时间time的秒数 |
DATE_FORMAT(date, format) | 以不同格式显示日期/时间 |
DATEDIFF(date1, date2) | 计算两个日期相差的天数(date1 - date2) |
DATE_ADD(date, INTERVAL expr type) | 日期加上一个时间间隔 |
DATE_SUB(date, INTERVAL expr type) | 日期减去一个时间间隔 |
-- 示例
SELECT CURDATE(), -- 2023-10-01
CURTIME(), -- 14:30:25
NOW(), -- 2023-10-01 14:30:25
YEAR('2023-10-01'), -- 2023
MONTH('2023-10-01'), -- 10
DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'), -- 2023-10-01 14:30:25
DATEDIFF('2023-10-10', '2023-10-01'), -- 9
DATE_ADD('2023-10-01', INTERVAL 7 DAY); -- 2023-10-08
四、聚合函数(常用)
| 函数 | 说明 |
|---|---|
COUNT() | 返回指定列的总行数 |
SUM() | 返回指定列的总和 |
AVG() | 返回指定列的平均值 |
MAX() | 返回指定列的最大值 |
MIN() | 返回指定列的最小值 |
GROUP_CONCAT() | 将分组后的列值连接成字符串 |
-- 示例
SELECT COUNT(*) AS 总记录数,
SUM(score) AS 总分,
AVG(score) AS 平均分,
MAX(score) AS 最高分,
MIN(score) AS 最低分
FROM student_scores;
五、流程控制函数
| 函数 | 说明 |
|---|---|
IF(expr, v1, v2) | 如果表达式expr成立,返回v1,否则返回v2 |
IFNULL(v1, v2) | 如果v1不为NULL,返回v1,否则返回v2 |
CASE WHEN expr1 THEN v1 [WHEN expr2 THEN v2...] [ELSE vn] END | 多条件判断 |
-- 示例
SELECT name, score,
IF(score >= 60, '及格', '不及格') AS 状态,
CASE
WHEN score >= 90 THEN '优秀'
WHEN score >= 80 THEN '良好'
WHEN score >= 60 THEN '及格'
ELSE '不及格'
END AS 等级
FROM student_scores;
六、系统信息函数
| 函数 | 说明 |
|---|---|
VERSION() | 返回数据库的版本号 |
USER() | 返回当前用户 |
DATABASE() | 返回当前数据库名 |
CONNECTION_ID() | 返回当前连接的连接ID |
0 | 返回最后插入的ID值 |
-- 示例
SELECT VERSION(), USER(), DATABASE();
七、加密函数
| 函数 | 说明 |
|---|---|
MD5(str) | 计算字符串str的MD5 32位哈希值 |
SHA1(str) | 计算字符串str的SHA1 40位哈希值 |
PASSWORD(str) | 返回字符串str的加密版本(MySQL 5.7.6+已弃用) |
-- 示例
SELECT MD5('password'),
SHA1('password');
使用注意事项:
- 函数嵌套:可以嵌套使用函数,如
UPPER(CONCAT(first_name, ' ', last_name)) - 性能影响:在WHERE子句中对列使用函数会影响索引使用
- NULL处理:大部分函数在参数为NULL时返回NULL
- 数据类型:注意函数的参数和返回值的数据类型
5.3、数据库级别的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,'xiaoming',MD5('123456'))
-- 如何校验:将用户传递进来的密码,进行md5加密,然后比对加密后的值
SELECT * FROM testmd5 WHERE `name`='xiaoming' AND pwd=MD5('123456')
6、事务
6.1、什么是事务
要么都成功,要么都失败
——————
1、SQL 执行 A 给 B 转账 A 1000 ---->200 B 200
2、SQL 执行 B 收到 A 的钱 A 800 —> B 400
——————
将一组SQL 放在一个批次中去执行~
事务原则: ACID 原则 原子性,一致性,隔离性,持久性 (脏读,幻读....)
- 参考博客连接:blog.csdn.net/dengjili/ar…
原子性(Atomicity)
要么都成功,要么都失败
一致性(Consistency)
事务前后的数据完整性要保证一致 ,1000
持久性(Durability)--- 事务提交
事务一旦提交则不可逆,被持久化到数据库中!
隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,务之间要相互隔离。
隔离所导致的一些问题
脏读:
指一个事务读取了另外一个事务未提交的数据。
不可重复读:
在一个事务内读取表中的某一行数据,多次读取结果不同。(这个不一定是错误,只是某些场合不对)
虚读(幻读)
是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。
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' -- A加500
COMMIT; -- 提交事务,就被持久化了!
ROLLBACK; -- 回滚
SET autocommit = 1; -- 恢复默认值
7、索引
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。 0.5s 0.00001s
提取句子主干,就可以得到索引的本质:索引是数据结构。
7.1、索引的分类
- 主键索引 (PRIMARY KEY)
- 唯一索引 (UNIQUE KEY)
- 常规索引 (KEY/INDEX)
- 全文索引 (FullText)
-- 索引的使用
-- 1、在创建表的时候给字段增加索引
-- 2、创建完毕后,增加索引
-- 显示所有的索引信息
SHOW INDEX FROM student
-- 增加一个全文索引 (索引名) 列名
ALTER TABLE school.student ADD FULLTEXT INDEX `studentName` (`studentName`);
-- EXPLAIN 分析sql执行的状况
EXPLAIN SELECT * FROM student; -- 非全文索引
EXPLAIN SELECT * FROM student WHERE MATCH(studentName) AGAINST('刘');
权限管理
- 选择所有的数据库.数据表*.*
-- 创建用户 CREATE USER 用户名 IDENTIFIED BY '密码'
CREATE USER kuangshen IDENTIFIED BY '123456'
-- 修改密码 (修改当前用户密码)
SET PASSWORD = PASSWORD('123456')
-- 修改密码 (修改指定用户密码)
SET PASSWORD FOR kuangshen = PASSWORD('123456')
-- 重命名 RENAME USER 原来名字 TO 新的名字
RENAME USER kuangshen TO kuangshen2
-- 用户授权 ALL PRIVILEGES 全部的权限 , 库.表
-- ALL PRIVILEGES 除了给别人授权,其他都能
GRANT ALL PRIVILEGES ON *.* TO kuangshen2
-- 查询权限
SHOW GRANTS FOR kuangshen2 -- 查看指定用户的权限
SHOW GRANTS FOR root@localhost
-- ROOT用户权限:GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-- 撤销权限 REVOKE 哪些权限, 在哪个库撤销,给谁撤销
REVOKE ALL PRIVILEGES ON *.* FROM kuangshen2
-- 删除用户
DROP USER kuangshen
数据库备份
- 使用命令行导出
mysqldump命令行使用
# mysqldump -h 主机 -u 用户名 -p 密码 数据库 表名 > 物理磁盘位置/文件名
mysqldump -hlocalhost -uroot -p123456 school1 student >D:/a.sql
# mysqldump -h 主机 -u 用户名 -p 密码 数据库 表1 表2 表3 > 物理磁盘位置/文件名
mysqldump -hlocalhost -uroot -p123456 school1 student >D:/a.sql
# mysqldump -h 主机 -u 用户名 -p 密码 数据库> 物理磁盘位置/文件名
mysqldump -hlocalhost -uroot -p123456 school1 >D:/c.sql
# 导入
# 登录的情况下,切换到指定的数据库
# source 备份文件
source d:/a.sql
数据库设计
9.1、为什么需要设计
当数据库比较复杂的时候,我们就需要设计了
糟糕的数据库设计:
- 数据冗余,浪费空间
- 数据库插入和删除都会麻烦、异常【屏蔽使用物理外键】
- 程序的性能差
良好的数据库设计:
- 节省内存空间
- 保证数据库的完整性
- 方便我们开发系统
三大范式
第一范式(1NF)
原子性 :保证每一列不可再分
第二范式(2NF)
前提:满足第一范式
每张表只描述一件事情
第三范式(3NF)
前提:满足第一范式 和 第二范式
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。 (规范数据库的设计)
规范性和性能的问题
关联查询的表不得超过三张表
- 考虑商业化的需求和目标,(成本,用户体验!)数据库的性能更加重要
- 在规范性能的问题的时候,需要适当的考虑一下 规范性!
- 故意给某些表增加一些冗余的字段。(从多表查询中变为单表查询)
- 故意增加一些计算列(从大数据量降低为小数据量的查询:索引)
10、JDBC(重点)
10.1、数据库驱动
驱动:声卡,显卡、数据库
10.2、JDBC
SUN公司为了简化开发人员的(对数据库的统一)操作,提供了一个(Java操作数据库的)规范,俗称JDBC
这些规范的实现由具体的厂商去做~
对于开发人员来说,我们只需要掌握JDBC接口的操作即可!
java.sql
javax.sql
还需要导入一个数据库驱动包 mysql-connector-java-5.1.47.jar
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,'lisi','123456','lisi@sina.com','1981-12-04'),
(3,'wangwu','123456','wangwu@sina.com','1979-12-04');
- 创建一个普通项目
- 导入一个数据库驱动
- 建一个lib目录,粘贴驱动到此目录,右键添加为library
3. 编写测试代码
public class JDBCTestDome01 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.加载驱动
//8.0以前没有.cj
Class.forName("com.mysql.cj.jdbc.Driver");
//2.用户信息与url,大部分默认端口都是3306
//可以执行SHOW GLOBAL VARIABLES LIKE 'port';看端口
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
String user = "root";
String password = "123456";
//3.通过连接创建数据库对象,connection就代表了数据库
Connection connection = DriverManager.getConnection(url, user, password);
//4,创建执行sql的对象 statement
Statement statement = connection.createStatement();
//5,执行sql,得到结果集
ResultSet resultSet = statement.executeQuery("select * from users");
while (resultSet.next()) {
System.out.println(resultSet.getString("id"));
System.out.println(resultSet.getString("NAME"));
System.out.println(resultSet.getString("PASSWORD"));
System.out.println(resultSet.getString("email"));
System.out.println(resultSet.getString("birthday"));
}
//6.关闭资源
resultSet.close();
statement.close();
connection.close();
}
}
步骤总结:
1、加载驱动
2、连接数据库 DriverManager
3、获得执行sql的对象 Statement
4、获得返回的结果集
5、释放连接
DriveManger
// DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver"); // 固定写法,加载驱动
connection connection = DriverManager.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&参数3
// oralce -- 1521
//jdbc:oracle:thin:@localhost:1521:sid
Statement 执行SQL的对象 PreparedStatement 执行SQL的对象
String sql = "SELECT * FROM users"; // 编写SQL
statement.executeQuery(); //查询操作返回 ResultSet
statement.execute(); // 执行任何SQL
statement.executeUpdate(); // 更新、插入、删除。都是用这个,返回一个受影响的行数
ResultSet 查询的结果集:封装了所有的查询结果
获得指定的数据类型
-
getString()是通用的:可以读取任何数据库类型并转为字符串
-
性能考虑:使用对应的类型方法(
getInt()、getDate()等)性能更好 -
类型安全:明确数据类型可以避免意外错误
-
可读性:使用对应的方法让代码意图更清晰
resultset.getObject(); // 在不知道列类型的情况下使用
// 如果知道列的类型就使用指定的类型
resultset.getString();
resultset.getInt();
resultset.getFloat();
resultset.getDate();
resultset.getObject();
......
指针,光标
resultSet.beforeFirst(); // 移动到最前面
resultSet.afterLast(); // 移动到最后面
resultSet.next(); // 移动到下一个数据
resultSet.previous(); // 移动到前一个
resultSet.absolute(row); // 移动到指定个
释放资源
释放资源
//6、释放连接
resultset.close();
statement.close();
connection.close(); // 耗资源,用完关掉!
10.4、statement对象
jdbc中的statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可 。
Statement对象的executeUpdate方法,用于向数据库发送增、删、改的sql语句,executeUpdate执行完后,将会返回一个整数(即增删改语句导致了数据库几行数据发生了变化)。
Statement.executeQuery方法用于向数据库发送查询语句,executeQuery方法返回代表查询结果的ResultSet对象。
提取工具类
package com.zhang.test01.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JDBCUtils {
private static String url ;
private static String user ;
private static String password ;
static {
//properties文件是Java中简单、轻量、易用的配置文件解决方案,特别适合存储键值对形式的配置信息。
InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
try {
properties.load(in);
String driver = properties.getProperty("driver");
url=properties.getProperty("url");
user=properties.getProperty("user");
password=properties.getProperty("password");
Class.forName(driver);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
public static void close(Connection conn, Statement stmt, ResultSet rs) throws SQLException {
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
}
}
driver=com.mysql.cj.jdbc.Driver
user=root
password=123456
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
public class JDBCTestDome01 {
public static void main(String[] args) throws SQLException {
Connection conn = null;
Statement statement = null;
ResultSet rs = null;
try{ //3.通过连接创建数据库对象,connection就代表了数据库
conn = JDBCUtils.getConnection();
//4,创建执行sql的对象 statement
statement = conn.createStatement();
//5,执行sql,得到结果集
rs = statement.executeQuery("select * from users");
while (rs.next()) {
System.out.println(rs.getString("id"));
System.out.println(rs.getString("NAME"));
System.out.println(rs.getString("PASSWORD"));
System.out.println(rs.getString("email"));
System.out.println(rs.getString("birthday"));
}
}catch (Exception e){
e.printStackTrace();
}finally {//6.关闭资源
JDBCUtils.close(conn, statement, rs);
}
}
}
SQL 注入的问题
sql 存在漏洞,会被攻击导致数据泄露,SQL会被拼接 or
- 下面是查询逻辑sql
"select * from users where NAME='"+username+"' and PASSWORD='"+pwd+"'"
- 这个时候·假如有人输入
这个拼接后逻辑直接全部通过1=1,所有数据库数据都会被查出
PrepareStatment 预编译的声明
public class PrepareStatementTest {
public static void main(String[] args) throws SQLException, ParseException {
Connection connection = JDBCUtil02.getConnection();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date parse = sdf.parse("2004-10-16");
//PreparedStatement预编译Statement,直接传一个sql,用?当占位符来当参数
//没有字符串拼接,避免出现SQL注入
PreparedStatement preparedStatement = connection.prepareStatement(
"insert into users values(?,?,?,?,?)"
);
preparedStatement.setInt(1, 5);
preparedStatement.setString(2, "zhanglei");
preparedStatement.setString(3, "123456");
preparedStatement.setString(4, "zhang@123.com");
//数据库的Date类来自java.sql.Date包,需要传入一个long类型的时间戳来设置值
preparedStatement.setDate(5, new java.sql.Date(parse.getTime()));
System.out.println(parse.getTime());
preparedStatement.executeUpdate();
JDBCUtil02.close(connection, preparedStatement, null);
}
}
-
如果
username = "admin' OR '1'='1" -
PreparedStatement会自动将其转换为字符串值:
"admin' OR '1'='1" -
而不是SQL语法的一部分
IDEA的数据库管理功能
10.8、事务
要么都成功,要么都失败
ACID原则
- 原子性:要么全部完成,要么都不完成
- 一致性:总数不变
- 隔离性:多个进程互不干扰
- 持久性:一旦提交不可逆,持久化到数据库了
隔离性的问题:
- 脏读:一个事务读取了另一个没有提交的事务
- 不可重复读:在同一个事务内,重复读取表中的数据,表数据发生了改变
- 虚读(幻读) :在一个事务内,读取到了别人插入的数据,导致前后读出来结果不一致
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
// 关闭数据库的自动提交,自动会开启事务
conn.setAutoCommit(false); // 开启事务
String sql1 = "update account set money = money-100 where name = 'A'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
int x = 1/0; // 报错
String sql2 = "update account set money = money+100 where name = 'B'";
st = conn.prepareStatement(sql2);
st.executeUpdate();
//业务完毕,提交事务
conn.commit();
System.out.println("成功!");
} catch (SQLException e) {
// 如果失败,则默认回滚
try {
conn.rollback(); // 如果失败则回滚事务
} catch (SQLException e1) {
}
}
}
开启事务后,必须显式提交或回滚。不要依赖任何"自动"行为,否则会导致数据不一致。
数据库连接池
连接池是一个预先创建并维护一组数据库连接的技术,当应用程序需要连接时,直接从池中获取,用完后归还给池,而不是真正关闭。
-
传统JDBC连接的问题:
-
创建开销大:每次TCP三次握手、数据库认证、资源分配
-
响应慢:连接建立通常需要100ms-500ms
-
并发限制:数据库连接数有限制
-
资源浪费:频繁创建/销毁连接
开源数据源实现
-
HikariCP
-
DBCP
-
C3P0
-
Druid:阿里巴巴
使用了这些数据库连接池之后,我们在项目开发中就不需要编写连接数据库的代码了!
DBCP
package com.zhang.test02.util;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class DBCPUtils {
private static BasicDataSource dataSource=null;
static {
InputStream in = JDBCUtil02.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties properties = new Properties();
try {
properties.load(in);
dataSource = BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static void close(Connection conn, Statement stmt, ResultSet rs) throws SQLException {
dataSource.close();
}
}
为什么 BasicDataSourceFactory 需要 commons-logging?
看一下 DBCP2 源码:
// BasicDataSourceFactory.java 第 64 行
private static final Log log = LogFactory.getLog(BasicDataSourceFactory.class);
BasicDataSourceFactory类静态初始化时就创建了日志对象,所以即使你不用日志功能,这个类也会尝试加载 commons-logging。
C3P0
需要用到的 jar 包
c3p0-0.9.5.5、mchange-commons-java-0.2.19