MySQL

1. 初识MySQL

关系型数据库:(SQL)

  • MySQL Oracle Sql Server DB2 SQLite
  • 通过表和表之间 行和列直接按的关系进行数据的存储,学员信息表…

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

  • Redis MongDB
  • 对象存储,通过对象自身的属性来决定

DBMS:数据库管理系统

  • 数据库的管理软件科学有效的管理我们的数据。维护和获取数据。
  • MySQL 数据库管理系统,存数据,操作数据

MySQL简介

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

由瑞典MySQL AB 公司开发,属于Oracle旗下产品,

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

开源的数据库软件

体积小 速度快 总体成本低

SQLyog

创建数据库

在这里插入图片描述

新建表

在这里插入图片描述

连接数据库

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

------------------------------------------------------------------------------------------------------
show database;  --查询所有数据库
mysql> use school; --切换数据库
show tables; --查看数据库中所有的表
describe student; --显示数据库中所有表的信息
create database westos; --创建一个数据库

exit; --退出连接
--单行注释
/*
	多行注释
*/

DDL:数据库定义语言(create drop alter 对表结构进行修改)

DML:操作管理语言(insert delete update 对表数据进行修改)

DQL:查询语言(select …)

DCL:控制语言(授权grant 撤销权限revoke)

TCL:事务控制语言(事务提交commit 事务回滚rollback)

2.操作数据库

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

操作数据库(了解)

1.创建数据库

CREATE DATABASE [IF NOT EXISTS] westos

2.删除数据库

DROP DATABASE [IF EXISTS] westos;

3.使用数据库

USE `school`; --表明或者字段名是一个特殊字符,就需要带` `

4.查看数据库

SHOW DATABASE;

数据库的数据类型

数值

  • tinyint 十分小的数据 1个字节
  • smallint 较小的数据 2个字节
  • mediumint 中等大小的数据 3个字节
  • int 标准的整数 4个字节(常用)
  • bigint 较大的数据 8个字节
  • float 单精度浮点数 4个字节
  • double 双精度浮点数 8个字节(精度问题)
  • decimal 字符串形式的浮点数 金融计算的时候一般使用decimal
  • blob:Binary Large Object 二进制大对象 专门用于存储图片 声音 视频等流媒体数据 往BLOB类型的字段上插入数据的时候需要使用IO流

字符串

  • char 字符串 固定大小 0-255
  • varchar 可变字符串 常用变量 0-65535(常用)
  • tinytext 微型文本 2^8-1
  • text 文本串 保存大文本 2^16-1
  • clob:Character Large OBject 字符大对象 最多可以存储4G的字符串 超过255个字符的都要采用CLOB字符大对象来存储

时间日期

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
  • 通常用来设置唯一的主键 必须是整数类型
  • 可以自定义设置主键自增的起始值和步长

非空:

  • NULL 和 not NULL
  • 假设设置为not NULL,如果不给它赋值,就会报错
  • NULL,如果不填写值,默认就是NULL

默认:

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

创建数据库表

-- AUTO_INCREMENT  自增
-- COMMENT 注释
-- 字符串一般使用''括起来
-- 所有的语句后面加,  最后一个字段不用加
-- 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 comm`student`ent '家庭住址',
`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 -- 查看student数据表的定义语句
DESC student -- 显示表的结构

数据表的类型

/*
INNODB 默认使用
MYISAM 早些年使用
*/
MYISAMINNODB
事务支持不支持支持
数据行锁定不支持支持
外键约束不支持支持
全文索引支持不支持
表空间的大小较小较大,约为MYISAM两倍

常规使用操作

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

在物理空间存在的位置

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

本质还是文件的存储

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

  • INNODB 在数据库表中只有一个*.frm文件,以及上级目录下的 ibdata1 文件

  • MYISAM对应文件

    *.frm 表结构文件

    *.MID 数据文件(data)

    *.MYI 索引文件(index)

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

CHARSET=utf8 不设置的话,会是mysql 默认的字符集编码Latin1(不支持中文)

修改删除表

修改

change 用来字段重命名,不能修改字段类型和约束

modify 不用来字段重命名,只能修改字段类型和约束

-- 重命名 ALTER TABLE 表名 CHANGE 旧名字 新名字 [列属性];
ALTER TABLE teacher1 CHANGE age Age INT(10);
-- 修改表的约束 ALTER TABLE 表名 MODIFY 字段名 [列属性];
ALTER TABLE teacher1 MODIFY age VARCHAR(10);
-- 删除表的字段 ALTER TABLE 表名 DROP 字段名;
ALTER TABLE teacher1 DROP Age;

删除

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

-- 删除表 如果表存在再删除
DROP TABLE IF EXISTS teacher1;

注意点

  • ·· 字段名 使用这个包裹

  • 注释 –

  • sql关键字大小不敏感 建议写小写

  • 所有的符号用英文

3.MySQL数据管理

外键(了解即可)

方式1 再创建表的时候增加约束 麻烦,比较复杂

-- 学生表的gradeid字段 引用年级表的gradeid-- 1.定义外键key 2.给这个外键添加约束(执行引用)
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

删除有外键关系表的时候,必须要先删除引用别人的表(从表),再删除被引用的表(主表)

方式2 创建表成功后 添加外键约束

-- 创建表的时候没有外键关系
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` -- 一般写插入语句 一定要数据和字段一一对应
INSERT INTO `grade` (`gradename`) VALUES ('大一'),('大二');
INSERT INTO `student`(`name`) VALUES ('张三');
INSERT INTO `student`(`name`,`pwd`) VALUES ('张三','aaaaaaa');
INSERT INTO `student`(`name`,`pwd`,`sex`) VALUES ('李四','aaabbbaaaa','男'),('王五','qwerty','男');

注意事项:

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

修改

update set 原来的值 = 新值

-- 修改学院名字 带了条件
UPDATE `student` SET `name` = '赵信' WHERE id = 1;
-- 不指定条件  会修改全部的name
UPDATE `student` SET `name` = '萨科';
-- UPDATE 表名 SET 列名 = 值[, 列名2 = 值2, ...] where [条件]-- 修改多个属性 逗号隔开
UPDATE `student` SET `name` = '赵信',`email` = '163.com' WHERE id = 1;
-- 通过多个条件定位数据
UPDATE `student` SET `name` = '盖伦' WHERE `name` = '赵信' AND `sex` = '女';

条件: where子句

操作符含义范围结果
=等于5=6false
<> 或 !=不等于5<>6true
>
<
>=
<=
BETWEEN a AND b[a, b][a, b]
AND&&5 > 1 AND 1 > 2false
OR||5 > 1 OR1 > 2true

注意点:

  1. 列名 是数据库的列 尽量带上``
  2. 条件, 筛选的条件 如果没有指定则会修改所有的列
  3. 多个设置的属性之间,使用英文逗号隔开
  4. value 具体的值 也可以是一个变量
UPDATE `student` SET `birthday` = CURRENT_TIME WHERE `name` = '王翠花' AND `sex` = '女';
-- 练习
UPDATE `student` SET `name` = '轮子妈' WHERE pwd = 'aaaaaaa'
UPDATE `student` SET `name` = '扇子妈', `sex` = '女' WHERE `name` = '赵信' AND `pwd` = '123456'
UPDATE `student` SET `pwd` = 'qqqqqq' WHERE `name` = '赵信' AND `pwd` = 'qwerty'
UPDATE `student` SET `birthday` = NEW DATETIME() WHERE NAME '赵信' AND `pwd` = 'abcdefg'
-- 修改datetime类型字段增加7天
UPDATE `student` SET `birthday` = DATE_ADD(`birthday`,INTERVAL 7 DAY) WHERE `id` = 7
-- 修改datetime类型字段减少7天
UPDATE `student` SET `birthday` = DATE_ADD(`birthday`,INTERVAL -7 DAY) WHERE `id` = 7
UPDATE `student` SET `birthday` = '1998-01-10 06:05:04', `sex` = '女' WHERE `name` = '赵信' AND `sex` = '男'

删除

delete (DML)

语法: detele from 表名 [where(条件)]

-- 删除指定数据
DELETE FROM `student` WHERE `id` = 618;

表中的数据被删除了 但是数据在硬盘上的真正存储空间不会被释放 删除效率低 但支持回滚

TRUNCATE命令 (DDL)

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

-- 清空student表
TRUNCATE `student`

物理删除 效率较高 不支持回滚

  • 相同点: 都能删除数据,都不会删除表结构

  • 不同点:

    • TRUNCATE 重新设置自增列 计数器会归零
    • TRUNCATE 不会影响事务
    -- 测试
    CREATE TABLE `test`(`id` INT(4) NOT NULL AUTO_INCREMENT, `pwd` VARCHAR(20) NOT NULL DEFAULT '123456',PRIMARY KEY (`id`))ENGINE=INNODB DEFAULT CHARSET=utf8
    INSERT INTO `test` (`id`) VALUES (1), (3), (5)-- 不会影响自增DELETE FROM `test`TRUNCATE TABLE `test`
    

    查看计数器: 右键表 -> 改变表 -> 高级 -> 自动增量

了解即可: DELETE删除的问题, 重启数据库后的现象

  • InnoDB 自增列会从1开始 (存在内存当中, 断电即失)
  • MyISAM 继续从上一个自增量开始 (存在文件中的, 不会丢失)

4. DQL查询数据 (最重点)

在这里插入图片描述

Data Query Language: 数据查询语言

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

4.1 指定查询字段

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

-- 查询指定字段
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 表

有的时候 列名没有那么见名知意 可以起别名

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

-- 去重 distinct
-- 查询全部的考试成绩
SELECT * FROM result
-- 查询有哪些同学参加了考试
SELECT  `StudentNo` FROM result;
-- 发现重复数据
SELECT DISTINCT `StudentNo` FROM student;

tips

SELECT @@auto_increment_increment -- 查询自增的步长
SELECT VERSION() -- 查询系统版本
-- 学院考试成绩+1分查看
SELECT `StudentNo`,`StudentResult`+1 AS '提分' FROM result;

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

select 表达式 from 表

4.2 where条件子句

检索数据中符合条件的值

逻辑运算符

运算符语法描述
and &&a and b a&&b逻辑与
or ||a or b a||b逻辑或
not !not a !a逻辑非

and 和 or同时出现 and优先级较高

-- 查询考试成绩在95到100之间的
SELECT `StudentNo`,`StudentResult` FROM resultWHERE StudentResult > 95 AND StudentResult < 100;
-- 模糊查询(区间)
SELECT `StudentNo`,`StudentResult` FROM resultWHERE StudentResult BETWEEN 95 AND 100;
-- 除了1000号学生以外的成绩
SELECT `StudentNo`,`StudentResult` FROM resultWHERE NOT StudentNo = 1000;

模糊查询 比较运算符

运算符语法描述
IS NULLa IS NULL如果操作符为null 则结果为真
IS NOT NULLa IS NOT NULL如果操作符不为null 则结果为真
BETWEENa BETWEEN b AND c若a在b和c之间则结果为真
Likea LIKE bsql匹配 如果a能够匹配到b则结果为真
INa in (a1, a2, a3 …)假设a在a1或者a2…其中的某个值中,结果为真
-- ========== like ==================
-- 查询姓张的同学
-- like结合%  % 代表0到任意个字符  _一个字符
SELECT `studentno`,`studentname` FROM studentWHERE studentname LIKE '张%'
-- 查询姓张的同学 名字后面只有一个字的
SELECT `studentno`,`studentname` FROM studentWHERE studentname LIKE '张_'
-- 查询姓张的同学 名字后面只有两个字的
SELECT `studentno`,`studentname` FROM studentWHERE studentname LIKE '张__'
-- 查询名字中间有大字的同学
SELECT `studentno`,`studentname` FROM studentWHERE studentname LIKE '%大%'
-- ======== in 具体的一个或者多个值 ==========
-- 查询 1001 1002 1003号学员
SELECT `studentno`,`studentname` FROM studentWHERE studentno IN (1001, 1002, 1003);
-- 查询在湖南长沙的学生
SELECT `studentno`,`studentname` FROM studentWHERE `address` IN ('湖南长沙', '北京朝阳');
-- ==== null 和 not null ====
-- 查询地址为空的学生 
SELECT `studentno`,`studentname` FROM studentWHERE `address`='' OR address IS NULL;
-- 查询有出生日期的同学
SELECT `studentno`,`studentname` FROM studentWHERE `borndate` IS NOT NULL;

4.3 联表查询

JOIN 对比

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

-- 参加考试同学(学号 姓名 科目编号 分数)

SELECT * FROM student;
SELECT * FROM result;

-- 1.分析需求 分析查询的字段来自哪些表
-- 2.确定使用哪种连接查询 7种

SELECT s.studentNo, studentName, SubjectNo, StudentResult
FROM student AS s
INNER JOIN result AS r
WHERE s.studentNo = r.studentNo

-- RIGHT JOIN
SELECT s.studentNo, studentName, SubjectNo, StudentResult
FROM student AS s
RIGHT JOIN result r
ON s.studentNo = r.studentNo


-- LEFT JOIN
SELECT s.studentNo, studentName, SubjectNo, StudentResult
FROM student AS s
LEFT JOIN result r
ON s.studentNo = r.studentNo
操作描述
Inner join如果表中至少有一个匹配,就返回结果
left join会从左表中返回所有的值, 即使右表中没有匹配
right join会从右表中返回所有的值, 即使左表中没有匹配
-- 查询缺考的同学
-- join on 连接查询 where 等值查询
SELECT s.studentNo, studentName, SubjectNo, StudentResult
FROM student AS s
LEFT JOIN result r
ON s.studentNo = r.studentNo
WHERE StudentResult IS NULL

-- 参加考试同学的信息 (学号 学生姓名 科目名 分数)
SELECT s.studentNo, studentName, subjectName, StudentResult
FROM student AS s
RIGHT JOIN result AS r
ON r.studentNo = s.studentNo
INNER JOIN `subject` AS sub
ON r.subjectNo = sub.subjectNo

自连接

自己的表和自己的表连接

一张表拆为两张一样的表即可

父类:

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

子类:

pidcategoryIdcategoryName
34数据库
28办公信息
36web开发
57ps技术

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

父类子类
信息技术办公信息
软件开发数据库
软件开发web开发
美术设计ps技术

4.4 分页和排序

-- 根据查询的结果 成绩排序
SELECT s.`studentno`,`studentname`,`subjectname`,`StudentResult`
FROM student AS s
INNER JOIN result AS r
ON s.`StudentNo`=r.`StudentNo`
INNER JOIN `subject` AS sub
ON sub.`subjectno`=r.`subjectno`
ORDER BY `StudentResult` DESC
-- 需要多个字段排序
-- ORDER BY `StudentResult` DESC, `studentname` DESC
-- 分页 
-- 1.缓解数据库压力 2.给人更好的体验 3.瀑布流

-- 每页显示3条数据
-- limit 起始值,页面大小
SELECT s.`studentno`,`studentname`,`subjectname`,`StudentResult`
FROM student AS s
INNER JOIN result AS r
ON s.`StudentNo`=r.`StudentNo`
INNER JOIN `subject` AS sub
ON sub.`subjectno`=r.`subjectno`
ORDER BY `StudentResult` DESC
LIMIT 0,3

-- limit (n-1)*pageSize,pageSize
-- 查询 课程成绩排名前十的学生 并且分数大于80的学生信息 (学号 姓名 课程名称 分数)
SELECT s.`studentno`,`studentname`,`subjectname`,`StudentResult`
FROM student AS s 
INNER JOIN `result` AS r
ON s.`studentno` = r.`StudentNo`
INNER JOIN `subject` AS sub
ON r.`SubjectNo`=sub.`subjectno`
WHERE `StudentResult`>80 
ORDER BY `StudentResult` DESC
LIMIT 0,10

4.5 子查询

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

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

-- ==== 子查询 ====

-- 查询高等数学-1 的所有考试结果 学号 科目名称 成绩 降序排列
SELECT `studentno`,r.`SubjectNo`,`StudentResult`
FROM result AS r
INNER JOIN `subject` AS sub 
ON r.`SubjectNo` = sub.`subjectno`
WHERE SubjectName = '高等数学-1'
ORDER BY StudentResult DESC


-- 使用子查询
SELECT `StudentNo`,`SubjectNo`,`StudentResult`
FROM result 
WHERE `SubjectNo` = (
SELECT `SubjectNo` FROM `subject` 
WHERE `subjectname`='高等数学-1'
)
ORDER BY StudentResult DESC


-- 分数不小于80分的学生的学号和姓名
SELECT DISTINCT s.`studentno`,`studentname`
FROM `student` AS s
INNER JOIN `result` AS r
ON r.`StudentNo`=s.`studentno`
WHERE `StudentResult` > 80

-- 在这个基础上增加一个科目
SELECT DISTINCT s.`studentno`,`studentname`
FROM `student` AS s
INNER JOIN `result` AS r
ON r.`StudentNo`=s.`studentno`
WHERE `StudentResult` > 80 AND `subjectno` = (
  SELECT `subjectno` FROM `subject`
  WHERE `subjectname`='高等数学-1'
)

4.6 分组过滤

-- 查询不太课程的平均分 最高分 最低分 平均分大于80分
-- 核心: 根据不同的课程分组
SELECT `subjectname`, AVG(`StudentResult`), MAX(`StudentResult`), MIN(`StudentResult`)
FROM result r
INNER JOIN `subject` sub
ON r.`SubjectNo` = sub.`subjectno`
GROUP BY r.`SubjectNo` -- 通过什么字段来分组
HAVING AVG(`StudentResult`)>80

在一条select语句当中 如果有group by语句的话 select后面只能跟参加分组的字段 以及分组函数

5. MySQL函数

5.1 常用函数

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

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

-- 字符串函数
SELECT CHAR_LENGTH('德玛西亚') -- 字符串长度
SELECT CONCAT('wo', 'ai', 'ni') -- 字符串拼接
SELECT INSERT('我爱java', 1, 2, '超级热爱') -- 插入替换
SELECT LOWER('Chen') -- 转小写字母
SELECT UPPER('Xiii') -- 转大写字母
SELECT INSTR('ChenXi', 'e') -- 返回第一次出现的字串的索引
SELECT REPLACE('ChenXi', 'Xi', 'Xiii') -- 替换出现的指定字符串
SELECT SUBSTR('ChenXi', 1, 4) -- 返回指定的字符串(原字符串, 起始位置, 截取长度)
SELECT REVERSE('ChenXi') -- 反转字符串

-- 查询姓张的同学 将周改为章
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(`borndate`) FROM student; -- Count(字段) 会忽略所有的null值
SELECT COUNT(*) FROM student; -- Count(*) 不会忽略null值 本质是计算行数
SELECT COUNT(1) FROM student; -- Count(1) 不会忽略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


-- 查询不太课程的平均分 最高分 最低分 平均分大于80分
-- 核心: 根据不同的课程分组
SELECT `subjectname`, AVG(`StudentResult`), MAX(`StudentResult`), MIN(`StudentResult`)
FROM result r
INNER JOIN `subject` sub
ON r.`SubjectNo` = sub.`subjectno`
GROUP BY r.`SubjectNo` -- 通过什么字段来分组
HAVING AVG(`StudentResult`)>80

5.3 数据库级别的MD5加密

MD5消息摘要算法(英语:MD5 Message-Digest Algorithm)

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

-- ====MD5====
CREATE TABLE `testmd5` (
 `id` INT(4) NOT NULL,
 `name` VARCHAR(20) NOT NULL,
 `pwd` VARCHAR(20) 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. 事务

要么都成功 要么都失败

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

原则: ACID 原子性 一致性 隔离性 持久性

脏读 不可重复读 幻读 …

详细参考: https://blog.csdn.net/dengjili/article/details/82468576

6.1 特性

原子性(Atomicity)

要么都成功 要么都失败

一致性(Consistency)

事务前后数据的完整性必须保持一致。

隔离性(Isolation)

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

持久性(Durability)

持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

脏读:

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

不可重复读:

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

虚读(幻读)

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

6.2 隔离级别

事务和事务之间的隔离级别有哪些呢?4个级别

  1. 读未提交:read uncommitted(最低的隔离级别)《没有提交就读到了》
    • 什么是读未提交?事务A可以读取到事务B未提交的数据。
    • 这种隔离级别存在的问题就是:脏读现象!(Dirty Read)
    • 这种隔离级别一般都是理论上的,大多数的数据库隔离级别都是二档起步!
  2. 读已提交:read committed《提交之后才能读到》
    • 什么是读已提交?事务A只能读取到事务B提交之后的数据。
    • 这种隔离级别解决了什么问题?解决了脏读的现象。
    • 这种隔离级别存在什么问题?不可重复读取数据。
    • 什么是不可重复读取数据呢?在事务开启之后,第一次读到的数据是3条,当前事务还没有结束,可能第二次再读取的时候,读到的数据是4条,3不等于4称为不可重复读取。
    • 这种隔离级别是比较真实的数据,每一次读到的数据是绝对的真实。
    • oracle数据库默认的隔离级别是:read committed
  3. 可重复读:repeatable read《提交之后也读不到,永远读取的都是刚开启事务时的数据》
    • 什么是可重复读取?事务A开启之后,不管是多久,每一次在事务A中读取到的数据都是一致的。即使事务B将数据已经修改,并且提交了,事务A读取到的数据还是没有发生改变,这就是可重复读。
    • 可重复读解决了什么问题?解决了不可重复读取数据。
    • 可重复读存在的问题是什么?可以会出现幻读。
    • mysql中默认的事务隔离级别就是这个
  4. 序列化/串行化:serializable(最高的隔离级别)
    • 这是最高隔离级别,效率最低。解决了所有的问题。
    • 这种隔离级别表示事务排队,不能并发!
    • synchronized,线程同步(事务同步)
    • 每一次读取到的数据都是最真实的,并且效率是最低的。

6.3 执行事务

-- =========== 事务 ============

-- mysql默认开启事务自动提交
SET autocommit = 0 -- 关闭
SET autocommit = 1 -- 开启 默认
-- 手动处理事务
SET autocommit = 0 -- 1.关闭自动提交
-- 事务开启
START TRANSACTION -- 2.标记一个事务的开始 从这个之后的sql都在同一个事务内
-- 提交 3.持久化(成功)
COMMIT
-- 回滚 3.回到原来的位置(失败)
ROLLBACK
-- 事务结束
SET autocommit = 1 -- 4.开启自动提交
-- 了解
SAVEPOINT 保存点名称 -- 设置一个事务的保存点
ROLLBACK TO SAVEPOINT 保存点名称 -- 回滚到保存点
RELEASE SAVEPOINT 保存点名称 -- 撤销保存点

6.4 转账场景

-- 模拟场景 转账
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 -- 1.关闭自动提交
START TRANSACTION -- 2.开启事务
-- 一组事务
UPDATE `account` SET money = money - 500 WHERE `name`='A'
UPDATE `account` SET money = money + 500 WHERE `name`='B'
COMMIT; -- 3.提交事务
ROLLBACK; -- 3.回滚
SET autocommit = 1 -- 4.开启自动提交

7. 索引

https://blog.codinglabs.org/articles/theory-of-mysql-index.html

MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。提取句子主干,就可以得到索引的本质:索引是数据结构。

7.1 索引分类

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

  • 主键索引 PRIMARY KEY
    • 唯一的标识 主键不可重复且只能有一个列作为主键
  • 唯一索引 UNIQUE KEY
    • 避免重复的列出现 唯一索引可以重复 多个列都可以标识为唯一索引
  • 常规索引 KEY/INDEX
    • 默认的
  • 全文索引 FullText
    • 特定的数据库引擎下才有 快速定位数据
-- 索引的使用
-- 1.在创建表的时候给字段增加索引
-- 2.创建完毕后 增加索引

-- 显示所有的索引信息
SHOW INDEX FROM student

-- 增加索引 索引名(列名)
ALTER TABLE `student` ADD FULLTEXT `studentname`(`studentname`)

-- EXPLAIN 分析sql执行的状况
EXPLAIN SELECT * FROM student  -- 常规索引 (非全文索引)
EXPLAIN SELECT * FROM student WHERE MATCH(`studentname`) 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 '年龄',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 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), 
    '123123@qq.com', 
    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()

-- 执行耗时   : 1.217 sec 0.641 sec
SELECT * FROM `app_user`
WHERE `name` = '用户9999';

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

-- id_表名_字段名
-- create index 索引名 on 表(字段)
CREATE INDEX id_app_user_name ON `app_user`(`name`);

-- 执行耗时   : 0.074 sec  0.002 sec
SELECT * FROM `app_user`
WHERE `name` = '用户9999';

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

索引在小数据量的时候,用处不大,但是在大数据的时候,区别很明显

7.3 索引原则

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

索引的数据结构?

Btree: InnoDB的默认的底层结构

8. 权限管理和数据库备份

8.1 用户管理

SQLyog可视化管理

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

sql命令操作

用户表: mysql.user 本质是对这张表进行增删改查

-- 创建用户
-- CREATE USER 用户名 IDENTIFIED BY '密码'
CREATE USER chenxi IDENTIFIED BY '123456'
-- 修改密码(修改当前用户密码)
SET PASSWORD = PASSWORD('123456')
-- 修改密码(修改指定用户密码)
SET PASSWORD FOR chenxi = PASSWORD('123456');
-- 重命名
RENAME USER chenxi TO chenxiii
-- 授权 ALL PRIVILEGES 全部的权限, 库.表
-- ALL PRIVILEGES 除了给别人授权GRANT 其他权限都有
GRANT ALL PRIVILEGES ON *.* TO chenxiii
-- 查看指定用户的权限
SHOW GRANTS FOR chenxiii
-- 查看root用户权限
SHOW GRANTS FOR root@localhost
-- 撤销权限 REVOKE 哪些权限 哪个库撤销 给谁撤销
REVOKE ALL PRIVILEGES ON *.* FROM chenxiii
-- 删除用户
DROP USER chenxiii

8.2 数据库备份

  • 保证重要的数据不丢失
  • 数据转移

MySQL数据库备份的方式

  • 直接拷贝物理文件

  • 使用Sqlyog这种可视化工具中手动导出

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

    • mysqldump -p 主机 -u 用户名 -p 密码 数据库 表名 > 物理磁盘位置/文件名
    mysqldump -plocalhost -uroot -p123456 school student > 物理磁盘位置/文件名
    
    • 命令行登录数据库后导入
    # 切换到指定数据库
    use school
    source 备份文件
    

9. 规范数据库设计

9.1 为什么需要设计

当比较复杂的时候 需要数据库的设计

糟糕的数据库设计:

  • 数据冗余, 浪费空间
  • 数据插入删除麻烦 异常
  • 程序的性能差

良好的数据库设计:

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

软件开发中 数据

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

9.2 三大范式

为什么需要数据规范化?

  • 信息重复
  • 更新异常
  • 插入异常
  • 删除异常

三大范式

第一范式(1NF)

要求任何一张表必须有主键 每一个字段原子性不可再分

第二范式(2NF)

前提:满足第一范式

建立在第一范式的基础之上,要求所有非主键字段完全依赖主键,不要产生部分依赖。

每张表只描述一件事情

第三范式(3NF)

前提:满足第二范式

建立在第二范式的基础之上,要求所有非主键字段直接依赖主键,不要产生传递依赖。

规范性和性能的问题

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

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

10. JDBC

10.1 数据库驱动

在这里插入图片描述

10.2 JDBC

SUN公司为了简化开发人员的(对数据库的统一)操作,提供了一个(java操作数据库)规范,俗称JDBC

对于开发人员来说,我们只需要掌握JDBC接口的操作即可

在这里插入图片描述

10.3 第一个JDBC程序

1.创建数据库

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,'zhangsan','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')

2.导入数据库驱动

在这里插入图片描述

3.编写测试代码

package jdbc.lesson01;

import java.sql.*;

// 我的第一个JDBC程序
public class JdbcFirstDemo {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver"); // 固定写法
        // 2. 用户信息和url
        String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
        String username = "root";
        String password = "123456";
        // 3. 连接成功 数据库对象  Connection 代表数据库
        Connection connection = DriverManager.getConnection(url, username, password);

        // 4. 执行sql对象   Statement 执行sql的对象
        Statement statement = connection.createStatement();

        // 5. 执行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("birthday = " + resultSet.getObject("birthday"));
            System.out.println("----------------------------------");
        }
        // 6. 释放连接
        resultSet.close();
        statement.close();
        connection.close();
    }
}

url

String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
// jdbc:mysql://主机地址:端口号/数据库名?参数1&参数2&参数3
// mysql 默认端口 3306
// oracle 1521
// jdbc:oracle:thin@localhost:1521:sid

DriverManager

 Connection connection = DriverManager.getConnection(url, username, password);// connection 代表数据库// 数据库设置自动提交// 事务提交// 事务回滚connection.commit();connection.rollback();connection.setAutoCommit(true);

Statement 执行sql的对象 PrepareStatement 执行sql的对象

String sql = "SELECT * FROM `users`"; //编写sqlstatement.executeQuery(); //查询操作返回 ResultSetstatement.execute(); // 执行任何sqlstatement.executeUpdate(); // 更新 插入 删除 都是用这个 返回一个受影响的行数

ResultSet 查询的结果集: 封装了所有的查询结果

获得指定的数据类型

resultSet.getObject(); // 不知道类型的情况下使用// 知道列的类型就用指定的类型resultSet.getString();resultSet.getInt();resultSet.getFloat();resultSet.getDate();

遍历指针

resultSet.beforeFirst(); // 移动到最前面resultSet.afterLast(); // 移动到最后面resultSet.next(); // 移动到下一个数据resultSet.previous(); // 移动到前一行resultSet.absolute(row); // 移动到指定行

释放资源

resultSet.close();statement.close();connection.close();

10.4 statement对象

JDBC中statement对象用于向数据库发送sql请求,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可.

Statement对象的executeUpdate方法, 用于向数据库发送增、删、改的语句, executeUpdate执行完后, 将会返回一个整数(即增删改语句导致数据库几行数据发生了变化)

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

工具类

package jdbc.lesson02.utils;

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

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 (Exception e) {
            e.printStackTrace();
        }
    }

    // 获取连接
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }


    // 释放资源
    public static void release(Connection connection, Statement statement, ResultSet resultSet) {
        if (resultSet!=null) {
            try {
                resultSet.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

db.properties

driver = com.mysql.jdbc.Driverurl = jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=trueusername = rootpassword = 123456

package jdbc.lesson02;

import jdbc.lesson02.utils.JdbcUtils;

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

public class TestInsert {
    public static void main(String[] args) {
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection(); // 获取数据库连接
            statement = connection.createStatement();
            String sql = "INSERT INTO `users`(`id`,`NAME`,`PASSWORD`,`email`,`birthday`) VALUES(4, '德玛西亚', '123456', 'demaxiya@qq.com', '2020-12-12')";
            int i = statement.executeUpdate(sql);

            if (i > 0) {
                System.out.println("插入成功");
            }

            // Duplicate entry '4' for key 'PRIMARY' 重复插入
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(connection, statement, resultSet);
        }
    }
}

package jdbc.lesson02;

import jdbc.lesson02.utils.JdbcUtils;

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

public class TestDelete {
    public static void main(String[] args) {
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();
            statement = connection.createStatement();

            String sql = "DELETE FROM `users` WHERE id = 4";
            int i = statement.executeUpdate(sql);

            if (i > 0) {
                System.out.println("删除成功");
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(connection, statement, resultSet);
        }
    }
}

package jdbc.lesson02;

import jdbc.lesson02.utils.JdbcUtils;

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

public class TestUpdate {
    public static void main(String[] args) {
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();
            statement = connection.createStatement();

            String sql = "UPDATE `users` SET `name`='zhaoliu',`email`='888888@qq.com' WHERE id=3";
            int i = statement.executeUpdate(sql);

            if (i > 0) {
                System.out.println("更新成功");
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(connection, statement, resultSet);
        }
    }
}

package jdbc.lesson02;

import jdbc.lesson02.utils.JdbcUtils;

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

public class TestSelect {
    public static void main(String[] args) {
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();
            statement = connection.createStatement();

            String sql = "select * from users where id = 1";

            resultSet = statement.executeQuery(sql);  // 查询完毕返回结果集

            while (resultSet.next()) {
                System.out.println(resultSet.getString("NAME"));
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(connection, statement, resultSet);
        }
    }
}

10.5 sql注入

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

package jdbc.lesson02;

import jdbc.lesson02.utils.JdbcUtils;

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

public class SQLInjection {
    public static void main(String[] args) {

        // 正常登录
        login("zhangsan", "123456");

        // 非正常登录
        login(" 'or '1=1", "123456");

    }

    // 登录业务
    public static void login(String username, String password) {
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();
            statement = connection.createStatement();

            //"select * from users where `NAME` = ' 'or '1=1' AND `password` = '" + password + "'"
            String sql = "select * from users where `NAME` = '" + username + "' AND `password` = '" + password + "'";

            resultSet = statement.executeQuery(sql);  // 查询完毕返回结果集

            while (resultSet.next()) {
                System.out.println(resultSet.getString("NAME"));
                System.out.println(resultSet.getString("password"));
                System.out.println("--------------------");
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(connection, statement, resultSet);
        }
    }
}

10.6 PrepareStatement对象

PrepareStatement 可以防止sql注入 并且效率更高

package jdbc.lesson03;

import jdbc.lesson02.utils.JdbcUtils;
import java.util.Date;

import java.sql.*;

public class TestInsert {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();

            // 区别

            // 使用问号占位符代替参数
            String sql = "INSERT INTO `users`(`id`,`NAME`,`PASSWORD`,`email`,`birthday`) VALUES(?,?,?,?,?)";
            preparedStatement = connection.prepareStatement(sql); // 预编译sql  先写sql但不执行
            // 手动给参数赋值
            preparedStatement.setInt(1, 4);
            preparedStatement.setString(2, "德玛西亚");
            preparedStatement.setString(3, "123456");
            preparedStatement.setString(4, "demaxiya@qq.com");
            // 注意点   sql.Date   数据库
            //         util.Date  Java new Date().getTime() 获取时间戳
            preparedStatement.setDate(5, new java.sql.Date(new Date().getTime()));

            // 执行
            int i = preparedStatement.executeUpdate();

            if (i > 0) {
                System.out.println("插入成功");
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(connection, preparedStatement, resultSet);
        }
    }
}

package jdbc.lesson03;

import jdbc.lesson02.utils.JdbcUtils;

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

public class TestDelete {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();

            // 区别

            // 使用问号占位符代替参数
            String sql = "delete from `users` where id = ?";
            preparedStatement = connection.prepareStatement(sql); // 预编译sql  先写sql但不执行
            // 手动给参数赋值
            preparedStatement.setInt(1, 4);
            // 执行
            int i = preparedStatement.executeUpdate();
            if (i > 0) {
                System.out.println("删除成功");
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(connection, preparedStatement, resultSet);
        }
    }
}

package jdbc.lesson03;

import jdbc.lesson02.utils.JdbcUtils;

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

public class TestUpdate {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();

            // 区别

            // 使用问号占位符代替参数
            String sql = "update `users` set `name` = ? where `id` = ?;";
            preparedStatement = connection.prepareStatement(sql); // 预编译sql  先写sql但不执行
            // 手动给参数赋值
            preparedStatement.setString(1, "wangwu");
            preparedStatement.setInt(2, 3);
            // 执行
            int i = preparedStatement.executeUpdate();
            if (i > 0) {
                System.out.println("更新成功");
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(connection, preparedStatement, resultSet);
        }
    }
}

package jdbc.lesson03;

import jdbc.lesson02.utils.JdbcUtils;

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

public class TestSelect {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();

            // 区别

            // 使用问号占位符代替参数
            String sql = "select * from `users` where `id` = ?";
            preparedStatement = connection.prepareStatement(sql); // 预编译sql  先写sql但不执行
            // 手动给参数赋值
            preparedStatement.setInt(1, 1);
            // 执行
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                System.out.println(resultSet.getString("NAME"));
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(connection, preparedStatement, resultSet);
        }
    }
}

预防sql注入

package jdbc.lesson03;

import jdbc.lesson02.utils.JdbcUtils;

import java.sql.*;

public class SQLInjection {
    public static void main(String[] args) {

        // 正常登录
        //login("zhangsan", "123456");

        // 非正常登录
        login("'' or 1=1", "123456");

    }

    // 登录业务
    public static void login(String username, String password) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();

            // prepareStatement 防止SQL注入的本质 把传递进来的参数当作字符
            // 假设其中存在转义字符,就直接忽略,会被直接转义
            String sql = "select * from users where `NAME` = ? AND `password` = ?";
            statement = connection.prepareStatement(sql);

            statement.setString(1, username);
            statement.setString(2, password);

            resultSet = statement.executeQuery();  // 查询完毕返回结果集

            while (resultSet.next()) {
                System.out.println(resultSet.getString("NAME"));
                System.out.println(resultSet.getString("password"));
                System.out.println("--------------------");
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(connection, statement, resultSet);
        }
    }
}

10.7 IDEA连接数据库

在这里插入图片描述

修改数据需要提交

在这里插入图片描述

sql语句编写

在这里插入图片描述

查看sql语句
在这里插入图片描述

10.8 事务

package jdbc.lesson04;

import jdbc.lesson02.utils.JdbcUtils;

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

public class TestTransaction {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();

            // 关闭数据库的自动提交功能 自动开启事务
            connection.setAutoCommit(false); //开启事务
            String sql1 = "update account set `money` = `money` - 500 where `name` = 'A'";
            preparedStatement = connection.prepareStatement(sql1);
            preparedStatement.executeUpdate();

            // int x = 1/0; //报错

            String sql2 = "update account set `money` = `money` + 500 where `name` = 'B'";
            preparedStatement = connection.prepareStatement(sql2);
            preparedStatement.executeUpdate();

            // 业务完毕提交事务
            connection.commit();
            System.out.println("成功");

            // 关闭事务
            connection.setAutoCommit(true);
        } catch (SQLException throwables) {
            // 可以显示定义语句 但是默认失败就会回滚
            try {
                connection.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(connection, preparedStatement, resultSet);
        }
    }
}

10.9 数据库连接池

数据库连接 – 执行完毕 – 释放 十分浪费系统资源

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

编写连接池 实现接口DataSource

DBCP、C3P0、Druid(阿里巴巴)

使用数据库连接池后, 在项目开发中就不需要编写连接数据库的代码了

使用DBCP

使用 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=true
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

JdbcUtrils_DBCP.java

package jdbc.lesson05.utils;

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_DBCP.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 connection, Statement statement, ResultSet resultSet) {
        if (resultSet!=null) {
            try {
                resultSet.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

无论用什么数据源, 本质还是一样的, DataSource接口是不会变的, 方法就不会变

#指定由连接池所创建的连接的自动提交(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


JdbcUtrils_DBCP.java

```java
package jdbc.lesson05.utils;

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_DBCP.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 connection, Statement statement, ResultSet resultSet) {
        if (resultSet!=null) {
            try {
                resultSet.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

无论用什么数据源, 本质还是一样的, DataSource接口是不会变的, 方法就不会变

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值