MySQL详解
一、MySQL简介
- MySQL是一个轻量级关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司。目前MySQL被广泛地应用在Internet上的中小型网站中,由于体积小、速度快、总体拥有成本低,开放源码、免费,一般中小型网站的开发都选择Linux + MySQL作为网站数据库。MySQL是一个关系型数据库管理系统,MySQL是一种关联数据库管理系统,关联数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,就增加了速度并提高了灵活性。由于分布式和集群的出现,mysql也用于大型的网站上。
- MySQL官方文档
- 本文章参考博客
二、MySQL学习前言
- 学习流程:操作数据库 ->操作数据库中的表 ->操作数据库中表中的数据
- sql语言分类
名称 | 解释 | 命令 |
---|---|---|
DDL | 定义和管理数据对象,如:数据库,数据表等 | create,drop,alter |
DML | 用于操作数据库对象所包含的数据 | insert,delete,update |
DQL | 用于查询数据库对象所包含的数据 | select |
DCL | 用于管理数据库,包括管理权限和数据更改 | grant,commit,rollback |
三、数据库操作(DDL)
1、数据库操作
1.1、链接数据库
mysql -u root -p --链接本机mysql:进入目录mysql\bin,再键入命令mysql -u root -p,回车后提示你输密码.注意用户名前可以有空格也可以没有空格,但是密码前必须没有空格,否则让你重新输入密码。
update user set password=password('123456')where user='root'; --修改密码
flush privileges; --刷新数据库
show databases; --显示所有数据库
use dbname;--打开某个数据库
show tables; --显示数据库mysql中所有的表
describe user; --显示表mysql数据库中user表的列信息
create database name; --创建数据库
use databasename; --选择数据库
exit; --退出Mysql
? --命令关键词 : 寻求帮助
-- 表示注释
1.2、操作数据库
(1)创建数据库
CREATE DATABASE [IF NOT EXISTS] test;
(2)删除数据库
DROP DATABASE [IF EXISTS] test;
(3)查询数据库
SHOW DATABASES;
(4)使用数据库
USE mysql;
2、对数据库中表的操作
2.1、数据库中的表的列类型(列的数据类型)
- 列类型 : 规定数据库中该列存放的数据类型
(1)、数值类型
(2)、字符串类型
(3)、日期和时间型数值类型
(4)、null类型
- 理解为 “没有值” 或 “未知值”
- 不能用NULL进行算术运算 , 结果仍为NULL
2.2、字段属性(列的属性)
属性名 | 特点 |
---|---|
UnSigned | 无符号;声明后该数据列不允许负数 |
ZEROFILL | 0填充;声明后该数据列不足位数的用0来填充 , 如int(3),5则为005** |
Auto_InCrement | 自动增长 , 声明后每添加一条数据 , 自动在上一个记录数上加 1(默认); 通常用于设置主键 , 且为整数类型; 可定义起始值和步长 |
NULL 和 NOT NULL | MySQL默认为NULL , 即没有插入该列的数值;如果设置为NOT NULL , 则该列必须有值 |
DEFAULT | 默认值;声明后用于设置默认值; 例如,性别字段,默认为"男" , 否则为 “女” ; 若无指定该列的值 , 则默认值为"男"的值 |
注意: 当前表设置步长(AUTO_INCREMENT=100) : 只影响当前表;
而SET @@auto_increment_increment=5 ; 影响所有使用自增的表(全局)
2.3、创建数据库中的表
-- 目标 : 创建一个school数据库
-- 创建学生表(列,字段)
-- 学号int 登录密码varchar(20) 姓名,性别varchar(2),出生日期(datatime),家庭住址,email
-- 创建表之前 , 一定要先选择数据库
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 '表名' (
'字段名' 列类型 [属性] [索引] [注释],
'字段名' 列类型 [属性] [索引] [注释],
'字段名' 列类型 [属性] [索引] [注释]
)[表类型][字符集设置]
SHOW CREATE DATABASE student; -- 显示建数据库语句
SHOW CREATE TABLE student; -- 显示建表语句
//自定义练习
CREATE TABLE `students` (
`id` INT NOT NULL AUTO_INCREMENT COMMENT '学号',
`name` VARCHAR(10) NOT NULL DEFAULT '王老六' COMMENT '姓名',
`classname` INT NOT NULL DEFAULT '1905121' COMMENT '班级',
PRIMARY KEY (`id`,`name`) --主键可以为多个字段
) ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
引擎名称 | MyISAM | InnoDB |
---|---|---|
事务支持(ACID) | 不支持 | 支持 |
行级锁 | 不支持 | 支持 |
外键约束 | 不支持 | 支持 |
全文索引 | 支持 | 不支持 |
空间大小 | 比较小 | 约为MyISAM的两倍 |
经验 ( 适用场合 ) :
- 适用 MyISAM : 节约空间及相应速度
- 适用 InnoDB : 安全性 , 事务处理及多用户操作数据表
2.4、修改和删除数据库中的表
(1)修改表
ALTER TABLE student RENAME AS teacher; -- 修改表名
alter table 旧表名 rename as 新表名
ALTER TABLE teacher ADD sex INT(11); -- 添加字段
alter table 表名 add 字段名 字段属性
ALTER TABLE teacher MODIFY sex VARCHAR(11); -- 修改字段属性
alter table 表名 modify 字段名 字段属性
ALTER TABLE teacher CHANGE sex sex1 VARCHAR(12);-- 修改字段名和属性
alter table 表名 change 旧字段名 新字段名 字段属性[]
ALTER TABLE teacher DROP sex1; -- 删除表的字段
alter table 表名 drop 字段名
(2)删除表
DROP TABLE teacher; --删除表
drop table if exists 表名
四、MySQL数据管理(DML)
1、主键和外键
1.1、主键
简介:
主关键字是一种唯一关键字,表定义的一部分。一个表的主键可以由多个关键字共同组成,并且主关键字的列不能包含空值。主关键字是可选的,并且可在 CREATE TABLE 或 ALTER TABLE 语句中定义。即:
- 主关键字(primary key)是表中的一个或多个字段
- 主键的值用于唯一的标识表中的某一条记录。
- 在两个表的关系中,主关键字用来在一个表中引用来自于另一个表中的特定记录。
1.2、外键
简介:
如果公共关键字在一个关系中是主关键字,那么这个公共关键字被称为另一个关系的外键。由此可见,外键表示了两个关系之间的相关联系。以另一个关系的外键作主关键字的表被称为主表,具有此外键的表被称为主表的从表。外键又称作外关键字。即:
- 外键是用来连接数据库的,保证数据库的参照完整性。
- 表的外键是另一表的主键,外键是可以有重复的,可以是空值。
- 以另一个关系的外键作主关键字的表被称为主表,具有此外键的表被称为主表的从表。
- 保持数据一致性,完整性,主要目的是控制存储在外键表中的数据,约束。使两张表形成关联,外键只能引用外表中的列的值或使用空值。
- 删除表时,只能先删除从表,再删除主表。
- 在开发的时候不会使用外键
在创建表的时候创建外键:
CREATE TABLE `grade` (
`gradeID` INT(11) NOT NULL,
`gradeName` VARCHAR(11) NOT NULL,
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 '生日',
`address` VARCHAR(100) DEFAULT NULL COMMENT '地址',
`email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
`gradeID` INT(11) NOT NULL COMMENT '年级名称',
PRIMARY KEY (`id`),
KEY `FK_gradeID`(`gradeID`), --定义外键
CONSTRAINT `FK_gradeID` FOREIGN KEY (`gradeID`) REFERENCES `grade`(`gradeID`) --链接从表,即将外键 REFERENCES(参考)从表中的主键
) ENGINE=INNODB DEFAULT CHARSET=utf8
在创建表之后创建外键:
ALTER TABLE `student` ADD CONSTRAINT `FK_gradeID` FOREIGN KEY (`gradeID`) REFERENCES `grade`(`gradeID`);
删除外键:
ALTER TABLE `student` DROP FOREIGN KEY `FK_gradeID`; -- 删除表的外键,但是索引还存在;注:这个索引是在创建外键是自动生成的
ALTER TABLE `student` DROP INDEX `FK_gradeID`; -- 删除索引
-- 即:先删除外键再删除自动生成的索引
2、操作数据库中表中的数据
2.1、添加表中数据
INSERT INTO `表名`(`字段1`,`字段2`,...) VALUES (值1,值2,...),(值1,值2,...),...
2.2、修改表中数据
UPDATE `grade` SET `gradeName` = '研一' WHERE `gradeID` = 1;
UPDATE 表名 SET 字段名 = 新值,... WHERE 条件
2.3、删除表中数据
删除一行或者多行数据
-- 删除数据
DELETE FROM `grade` WHERE `gradeID` BETWEEN 1 AND 4
DELETE FROM 表名 WHERE 条件
删除整张表 delete 和 truncate
-- delete:
INSERT INTO `grade`(`gradeName`) VALUES ('大一'),('大二'),('大三'),('大四')
DELETE FROM `grade`
-- truncate:
INSERT INTO `grade`(`gradeName`) VALUES ('大一'),('大二'),('大三'),('大四')
TRUNCATE `grade`
NOTE :
- delete删除表时,自动增量不会变,truncate 删除表时,自动增量会置一
- 使用TRUNCATE TABLE不会对事务有影响
五、数据库查询语句(DQL,重点)
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}];
-- 指定查询的记录从哪条至哪条
SQL的执行顺序:
–第一步:执行FROM [JOIN ON]
–第二步:WHERE条件过滤
–第三步:GROUP BY分组
–第四步:执行SELECT投影列
–第五步:HAVING条件过滤
–第六步:执行ORDER BY 排序
查询所有(ALL):返回SELECT查询的所有记录结果,无论结果是否重复;SELECT语句默认为ALL
SELECT `name` AS '姓名' FROM `mystudent`;
SELECT ALL `name` AS '姓名' FROM `mystudent`;
-- 上述两条语句返回结果一致
去重(DISTINCT): 去掉SELECT查询返回的记录结果中重复的记录 ( 返回所有列的值都相同 ) , 只返回一条
-- 用as重命名 将结果中表意不清的数据列重新命名
SELECT DISTINCT `studentno` AS '参加过考试的学生' FROM result
- 逻辑表达式
操作符名称 | 语法 | 描述 |
---|---|---|
AND或&& | a AND b 或 a && b | 逻辑与,同时为真结果才为真;即:同时满足a条件和b条件才能返回结果 |
OR或 || | a OR b 或 a || b | 逻辑或,只要有一个为真,结果就为真;即:满足a条件或b条件其中一个,便能返回结果 |
NOT或! | NOT a或!a | 逻辑非,若操作数为假,则结果为真;即:不满足a条件的数据,便可返回 |
- 模糊查询:比较操作符
NOTE : - 使用 like 操作符时 %:表示任意字符 _:表示一个字符;即:a%可查询出a开头的任意字符长度的结果,而a
_
只能查询出a开头的两个字符的结果,a_ _
只能查询出a开头的三个字符的结果 - in(一个或者多个具体的结果)
1、连接查询
join 连接的区别:
操作符名称 | 描述 |
---|---|
inner join | 如果表中至少有一个匹配则返回行 ,即返回两表的交集 |
left join | (以左表作为基准,右边表来一一匹配,匹配不上的,返回左表的记录,右表以NULL填充),即左表的记录均返回,右表中无左表记录的其余字段用null填充 |
right join | (以右表作为基准,左边表来一一匹配,匹配不上的,返回右表的记录,左表以NULL填充) ,即右表的记录均返回,左表中无左表记录的其余字段用null填充 |
full join | 返回两表的并集 |
cross join | 生成来自多个表的行的笛卡尔乘积 |
inner join(内连接):
left join(左连接):
right join(右连接):
cross join(交叉连接):
思路:
- 分析需求,确定查询的列来源于那些表
- 是否使用连接查询,使用那种查询
- 确认表与表之间的交叉点
JOIN 语法:
SELECT [字段名1,字段名2,字段名3,...] --若左表和右表均有字段1,则需要指明字段1所属的一个表
FROM 左表 AS L
[INNER or LEFT or RIGHT or CROSS...] JOIN 右表 AS R ON L.字段名4 = R.字段名4----判别条件
-- 查询参加了考试的同学信息(学号,学生姓名,科目编号,分数)
-- 左连接实现
SELECT s.`studentno`,`studentname`,`subjectno`,`studentresult`
FROM student s
INNER JOIN `result` r ON s.studentno = r.studentno
-- 等值连接实现
SELECT s.studentno,studentname,subjectno,StudentResult
FROM student s , result r
WHERE r.studentno = s.studentno
-- 查询缺考学生(左连接实现)
SELECT s.`studentno`,`studentname`,`subjectno`,`studentresult`
FROM student s
LEFT JOIN `result` r ON s.studentno = r.studentno
WHERE `studentresult` IS NULL
-- 思考题:查询参加了考试的同学信息(学号,学生姓名,科目名,分数)
SELECT s.`studentno`,`studentname`,`subjectname`,`studentresult`
FROM student s
RIGHT JOIN result r ON s.studentno = r.studentno
INNER JOIN `subject` sub ON r.subjectno = sub.subjectno
where 与 on的区别
- on 条件是生成临时表时使用的条件,它不管 on 中的条件是否为真,都会返回左边表中的记录。
- where 条件是临时表生成好后,再对临时表进行过滤的条件。这时已经没有 left join 的含义(必须返回左边表的记录)了,条件不为真的就全部过滤掉。
2、排序和分页
/*============== 排序 ================
语法 : ORDER BY
ORDER BY 语句用于根据指定的列对结果集进行排序。
ORDER BY 语句默认按照ASC升序对记录进行排序。
如果您希望按照降序对记录进行排序,可以使用 DESC 关键字。
*/
SELECT s.`studentno`,`studentname`,`subjectname`,`studentresult`
FROM student s
INNER JOIN result r ON s.`studentno` = r.`studentno`
INNER JOIN `subject` sub ON sub.`subjectno` = r.`subjectno`
WHERE `subjectname` = '高等数学-1'
ORDER BY `studentresult` DESC --按studentresult字段进行降序
/*============== 分页 ================
语法 : SELECT * FROM table LIMIT (pageNo-1)*pageSzie,pageSzie
(pageNo-1)*pageSzie:起始值 pageSzie: 单页面显示条数
好处 : (用户体验,网络传输,查询压力)
推导:
第一页 : limit 0,5
第二页 : limit 5,5
第三页 : limit 10,5
......
第N页 : limit (pageNo-1)*pageSzie,pageSzie
[pageNo:页码,pageSize:单页面显示条数]
*/
SELECT * FROM `subject`
LIMIT 0,4
3、子查询
子查询的定义:
- 子查询是将一个查询语句嵌套在另一个查询语句中;
- 在特定情况下,一个查询语句的条件需要另一个查询语句来获取,内层查询(inner query)语句的查询结果,可以为外层查询(outer query)语句提供查询条件。
- 由内及外. where 语句中的条件就是两张表的交叉点
-- 查询课程为 高等数学-1 且分数不小于80分的学生的学号和姓名
-- 连接查询
SELECT s.studentno,studentname
FROM student s
INNER JOIN result r ON s.studentno = r.studentno
INNER JOIN `SUBJECT` sub ON sub.subjectno = r.subjectno
WHERE subjectname = '高等数学-1' AND studentresult >= 80
-- 子查询:由内及外. where 语句中的条件就是两张表的交叉点
SELECT studentno,studentname
FROM student
WHERE studentno IN(
SELECT studentno FROM result WHERE subjectno = (
SELECT subjectno FROM `subject` WHERE subjectname = '高等数学-1'
)
)
五、MySQL函数
1、常用函数
1.1、数据函数
SELECT ABS(-8); /*绝对值*/
SELECT CEILING(9.4); /*向上取整*/
SELECT FLOOR(9.4); /*向下取整*/
SELECT RAND(); /*随机数,返回一个0-1之间的随机数*/
SELECT SIGN(0); /*符号函数: 负数返回-1,正数返回1,0返回0*/
1.2、字符串函数
SELECT CHAR_LENGTH('狂神说坚持就能成功'); /*返回字符串包含的字符数*/
SELECT CONCAT('我','爱','程序'); /*合并字符串,参数可以有多个*/
SELECT INSERT('我爱编程helloworld',1,2,'超级热爱'); /*替换字符串,从某个位置开始替换某个长度*/
SELECT LOWER('KuangShen'); /*小写*/
SELECT UPPER('KuangShen'); /*大写*/
SELECT LEFT('hello,world',5); /*从左边截取*/
SELECT RIGHT('hello,world',5); /*从右边截取*/
SELECT REPLACE('狂神说坚持就能成功','坚持','努力'); /*替换字符串*/
SELECT SUBSTR('狂神说坚持就能成功',4,6); /*截取字符串,开始和长度*/
SELECT REVERSE('狂神说坚持就能成功'); /*反转
1.3、日期和时间函数
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());
1.4、系统信息函数
SELECT VERSION(); /*版本*/
SELECT USER(); /*用户*/
2、聚合函数
函数名 | 描述 |
---|---|
count() | 返回满足查询(Select)条件的 总和数,如select count(*) [不建议使用,效率低] |
min() | 可以为数值字段、字符字段或表达式列做统计,返回最小值 |
max() | 可以为数值字段,字符字段或表达式列作统计,返回最大的值 |
avg() | 返回一列的平均值 |
sum() | 返回一列的总和 |
NOTE: where不能使用聚合函数:
聚集函数也叫列函数,它们都是基于整列数据进行计算的,而where子句则是对数据行进行过滤的,在筛选过程中依赖“基于已经筛选完毕的数据得出的计算结果”是一种悖论,这是行不通的。更简单地说,因为聚集函数要对全列数据时行计算,因而使用它的前提是:结果集已经确定。
而where子句还处于“确定”结果集的过程中,因而不能使用聚集函数。
与where子句不能出现聚集函数正相反的是,我们几乎看不到不使用聚集函数的having子句。为什么?
因为在水平方向上根据外部指定条件的筛选(也就是对行的筛选),where子句可以独立完成,剩下的往往都是需要根据结果集自身的统计数据进一步筛选了,这时,几乎都需要通过having子句配合聚集函数来完成。
/*
count(*)包括了所有的列,统计行数,在统计结果的时候,不会忽略列值为NULL
count(1)忽略所有列,用1代表代码行,在统计结果的时候,不会忽略列值为NULL
count(列名)只包括列名那一列,在统计结果的时候,会忽略列值为空
(这里的空不是只空字符串或者0,而是表示null)的计数,即某个字段值为NULL时,不统计。
*/
SELECT COUNT(*) FROM `student`
SELECT COUNT(1) FROM `student`
SELECT COUNT(`identitycard`) FROM student
SELECT AVG(`studentresult`) AS 平均分 FROM `result` WHERE `studentno` = 1001
SELECT MIN(`studentresult`) AS 最低分 FROM `result` WHERE `studentno` = 1001
SELECT MAX(`studentresult`) AS 最高分 FROM `result` WHERE `studentno` = 1001
SELECT SUM(`studentresult`) AS 总分 FROM `result` WHERE `studentno` = 1001
3、分组
NOTE:在含有Group by子句的查询语句中,对select关键字后的目标列,存在以下规律:
- 使用group by 时,select 涉及的列要么是参与分组的列,要么列包含在聚合函数中
- where将对分组前的所有数据进行筛选。having将对分组后的一组数据进行过滤。
-- 查询不同课程的平均分,最高分,最低分
-- 前提:根据不同的课程进行分组
SELECT `subjectname`,AVG(`studentresult`),MAX(`studentresult`),MIN(`studentresult`)
FROM result r
INNER JOIN `subject` sub ON r.`subjectno` = sub.`subjectno`
GROUP BY `subjectname`
4、MD5加密函数
4.1、MD5简介
MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,MD5的前身有MD2、MD3和MD4。
4.2、实现数据加密
CREATE TABLE md5test (
id INT(11) NOT NULL ,
`name` VARCHAR(11) NOT NULL,
`pwd` VARCHAR(11) NOT NULL,
PRIMARY KEY (id)
)ENGINE=INNODB,DEFAULT CHARSET = utf8
INSERT INTO md5test VALUES (1,'zhangsan','123456'),(2,'lisi','123456'),(3,'wangwu','123456')
UPDATE md5test SET pwd = MD5(pwd) WHERE id = 1
INSERT INTO md5test VALUES(4,'kuangshen3',md5('123456'));
4.3、MD5实现数据匹配
SELECT * FROM md5test WHERE `name` = 'zhangsan' AND pwd = MD5('123456')
NOTE—MD5特点:
-
不可逆性 — 根据 MD5 值计算不出原始数据
-
唯一性 — 不同原始数据会有不同的 MD5 值
六、数据库事务的四大特性以及隔离级别
1、什么是事务
- 事务就是将一组SQL语句放在同一批次内去执行
- 如果一个SQL语句出错,则该批次内的所有SQL都将被取消执行,即 要不都执行,要不都不执行(原子性)。
2、事务ACID原则
2.1、原子性(Atomicity)
- 原子性是指 事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
2.2、一致性(Consistency)
- 一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说 一个事务执行之前和执行之后都必须处于一致性状态。
- 拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
2.3、隔离性(Isolation)
- 隔离性是 当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
- 即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
关于事务的隔离性数据库提供了多种隔离级别,稍后会介绍到。
2.4、持久性(Durability)
- 持久性是指 一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
- 例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。
2.5、事务语法
-- 1.关闭事务自动提交
SET autocommit = 0
-- 2.开启事务
START TRANSACTION
-- 3.设置一个事务保存点
SAVEPOINT 保存点名称
-- 4.sql语句
.....
-- 5.把事务回滚到保存点
ROLLBACK TO SAVEPOINT 保存点名称
-- 6.提交事务
COMMIT
-- 7.将事务回滚
ROLLBACK
-- 8.释放保存点
RELEASE SAVEPOINT 保存点名称
-- 9.开启事务自动提交
SET autocommit = 1
事务实践:
/*
A在线买一款价格为500元商品,网上银行转账.
A的银行卡余额为2000,然后给商家B支付500.
商家B一开始的银行卡余额为10000
创建数据库shop和创建表account并插入2条数据
*/
CREATE DATABASE shop CHARACTER SET utf8 COLLATE utf8_general_ci
USE shop;
CREATE TABLE account(
id INT(10) 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),('b',10000)
SET autocommit = 0
START TRANSACTION -- 开启事务
UPDATE account SET money = money - 500 WHERE `name` = 'a'
SAVEPOINT temp -- 创建保存点
UPDATE account SET money = money + 500 WHERE `name` = 'b'
ROLLBACK TO SAVEPOINT temp -- 回滚到保存点
COMMIT -- 事务一旦提交不可逆转,持久化到数据库文件了
ROLLBACK -- 回滚数据
SET autocommit = 1
七、索引
1、索引的作用
- 提高查询速度
- 确保数据的唯一性
- 可以 加速表和表之间的连接 , 实现表与表之间的参照完整性
- 使用分组和排序子句进行数据检索时 , 可以 显著减少分组和排序的时间
- 全文检索字段 进行搜索优化.
2、索引分类
2.1、主键索引(Primary Key)
-
主键 : 某一个属性组能唯一标识一条记录
-
主键索引特点 :
- 最常见 的索引类型
- 确保数据记录的唯一性
- 确定特定数据记录在数据库中的位置
2.2、唯一索引(Unique)
- 作用 : 避免同一个表中某数据列中的值重复
- 与主键索引的区别:
- 主键索引只能有一个
- 唯一索引可能有多个
2.3、常规索引(Index)
- 作用 : 快速定位特定数据
- 注意 :
- index 和 key 关键字都可以设置常规索引
- 应加在查询找条件的字段
- 不宜添加太多常规索引,影响数据的插入,删除和修改操作
- 创建表时添加常规索引:
-- 创建表时添加
CREATE TABLE `result`(
-- 省略一些代码
INDEX/KEY `ind` (`studentNo`,`subjectNo`)
)
- 创建后添加常规索引:
-- 创建后添加
ALTER TABLE `result` ADD INDEX `ind`(`studentNo`,`subjectNo`);
2.4、全文索引(FullText)
- 作用 : 快速定位特定数据
- 注意 :
- 只能用于 CHAR , VARCHAR , TEXT数据列类型
- 适合大型数据集,如百度搜索
- 增加全文索引(FullText):
/*
开始之前,先说一下全文索引的版本、存储引擎、数据类型的支持情况
MySQL 5.6 以前的版本,只有 MyISAM 存储引擎支持全文索引;
MySQL 5.6 及以后的版本,MyISAM 和 InnoDB 存储引擎均支持全文索引;
只有字段的数据类型为 char、varchar、text 及其系列才可以建全文索引。
测试或使用全文索引时,要先看一下自己的 MySQL 版本、存储引擎和数据类型是否支持全文索引。
*/
/*增加全文索引*/
ALTER TABLE student ADD FULLTEXT studentname(`studentname`)
SHOW INDEX FROM student -- 查看student表的索引
/*EXPLAIN : 分析SQL语句执行性能*/
EXPLAIN SELECT * FROM student WHERE studentname = 'huangaa'
- 使用全文索引:
/*使用全文索引*/
-- 全文搜索通过 MATCH() 函数完成。
-- 搜索字符串作为 against() 的参数被给定。搜索以忽略字母大小写的方式执行。对于表中的每个记录行,MATCH() 返回一个相关性值。即,在搜索字符串与记录行在 MATCH() 列表中指定的列的文本之间的相似性尺度。
EXPLAIN SELECT * FROM student WHERE MATCH(`studentname`) AGAINST('haungaa')
3、索引语法
#方法一:创建表时
CREATE TABLE 表名 (
字段名1 数据类型 [完整性约束条件…],
字段名2 数据类型 [完整性约束条件…],
[UNIQUE | FULLTEXT | SPATIAL ] INDEX | KEY
[索引名] (字段名[(长度)] [ASC |DESC])
);
#方法二:CREATE在已存在的表上创建索引
CREATE [UNIQUE | FULLTEXT | SPATIAL ] INDEX 索引名
ON 表名 (字段名[(长度)] [ASC |DESC]) ;
#方法三:ALTER TABLE在已存在的表上创建索引
ALTER TABLE 表名 ADD [UNIQUE | FULLTEXT | SPATIAL ] INDEX
索引名 (字段名[(长度)] [ASC |DESC]) ;
#删除索引:
DROP INDEX 索引名 ON 表名字;
#删除主键索引:
ALTER TABLE 表名 DROP PRIMARY KEY;
#显示索引信息:
SHOW INDEX FROM student;
4、索引准则
- 索引不是越多越好
- 不要对经常变动的数据加索引
- 小数据量的表建议不要加索引
- 索引一般应加在查找条件的字段
八、用户管理
基本命令:
-- ========================================语法=============================================
/* 用户和权限管理 */ ------------------
用户信息表:mysql.user
-- 刷新权限
FLUSH PRIVILEGES
-- 增加用户 CREATE USER kuangshen IDENTIFIED BY '123456'
CREATE USER 用户名 IDENTIFIED BY [PASSWORD] 密码(字符串)
- 必须拥有mysql数据库的全局CREATE USER权限,或拥有INSERT权限。
- 只能创建用户,不能赋予权限。
- 用户名,注意引号:如 'user_name'@'192.168.1.1'
- 密码也需引号,纯数字密码也要加引号
- 要在纯文本中指定密码,需忽略PASSWORD关键词。要把密码指定为由PASSWORD()函数返回的混编值,需包含关键字PASSWORD
-- 重命名用户 RENAME USER kuangshen TO kuangshen2
RENAME USER old_user TO new_user
-- 设置密码
SET PASSWORD = PASSWORD('密码') -- 为当前用户设置密码
SET PASSWORD FOR 用户名 = PASSWORD('密码') -- 为指定用户设置密码
-- 删除用户 DROP USER kuangshen2
DROP USER 用户名
-- 分配权限/添加用户
GRANT 权限列表 ON 表名 TO 用户名 [IDENTIFIED BY [PASSWORD] 'password']
- all privileges 表示所有权限
- *.* 表示所有库的所有表
- 库名.表名 表示某库下面的某表
-- 查看权限 SHOW GRANTS FOR root@localhost;
SHOW GRANTS FOR 用户名
-- 查看当前用户权限
SHOW GRANTS; 或 SHOW GRANTS FOR CURRENT_USER; 或 SHOW GRANTS FOR CURRENT_USER();
-- 撤消权限
REVOKE 权限列表 ON 表名 FROM 用户名
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 用户名 -- 撤销所有权限
-- =================================实践=================================================
-- 创建用户
CREATE USER huangaa IDENTIFIED BY '123456'
-- 修改用户名
RENAME USER huangaa TO huang
-- 修改密码
SET PASSWORD = PASSWORD('123456')
-- 授予权限
GRANT ALL PRIVILEGES ON *.* TO huanga
-- 撤销权限
REVOKE ALL PRIVILEGES ON *.* FROM huangaa
-- 删除用户
DROP USER huang
-- 显示用户权限
SHOW GRANTS FOR huangaa -- GRANT ALL PRIVILEGES ON *.* TO 'huangaa'@'%'
SHOW GRANTS FOR root@localhost -- GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
权限解释:
-- 权限列表
ALL [PRIVILEGES] -- 设置除GRANT OPTION之外的所有简单权限
ALTER -- 允许使用ALTER TABLE
ALTER ROUTINE -- 更改或取消已存储的子程序
CREATE -- 允许使用CREATE TABLE
CREATE ROUTINE -- 创建已存储的子程序
CREATE TEMPORARY TABLES -- 允许使用CREATE TEMPORARY TABLE
CREATE USER -- 允许使用CREATE USER, DROP USER, RENAME USER和REVOKE ALL PRIVILEGES。
CREATE VIEW -- 允许使用CREATE VIEW
DELETE -- 允许使用DELETE
DROP -- 允许使用DROP TABLE
EXECUTE -- 允许用户运行已存储的子程序
FILE -- 允许使用SELECT...INTO OUTFILE和LOAD DATA INFILE
INDEX -- 允许使用CREATE INDEX和DROP INDEX
INSERT -- 允许使用INSERT
LOCK TABLES -- 允许对您拥有SELECT权限的表使用LOCK TABLES
PROCESS -- 允许使用SHOW FULL PROCESSLIST
REFERENCES -- 未被实施
RELOAD -- 允许使用FLUSH
REPLICATION CLIENT -- 允许用户询问从属服务器或主服务器的地址
REPLICATION SLAVE -- 用于复制型从属服务器(从主服务器中读取二进制日志事件)
SELECT -- 允许使用SELECT
SHOW DATABASES -- 显示所有数据库
SHOW VIEW -- 允许使用SHOW CREATE VIEW
SHUTDOWN -- 允许使用mysqladmin shutdown
SUPER -- 允许使用CHANGE MASTER, KILL, PURGE MASTER LOGS和SET GLOBAL语句,mysqladmin debug命令;允许您连接(一次),即使已达到max_connections。
UPDATE -- 允许使用UPDATE
USAGE -- “无权限”的同义词
GRANT OPTION -- 允许授予权限
/* 表维护 */
-- 分析和存储表的关键字分布
ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE 表名 ...
-- 检查一个或多个表是否有错误
CHECK TABLE tbl_name [, tbl_name] ... [option] ...
option = {QUICK | FAST | MEDIUM | EXTENDED | CHANGED}
-- 整理数据文件的碎片
OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
九、MySQL备份
数据库备份必要性:
- 保证重要数据不丢失
- 数据转移
MySQL数据库备份方法:
- mysqldump备份工具
- 数据库管理工具,如SQLyog
- 直接拷贝数据库文件和相关配置文件
mysqldump客户端作用 :
- 转储数据库
- 搜集数据库进行备份
- 将数据转移到另一个SQL服务器,不一定是MySQL服务器
-- 导出
1. 导出一张表 -- mysqldump -uroot -p123456 school student >D:/a.sql
mysqldump -u用户名 -p密码 库名 表名 > 文件名(D:/a.sql)
2. 导出多张表 -- mysqldump -uroot -p123456 school student result >D:/a.sql
mysqldump -u用户名 -p密码 库名 表1 表2 表3 > 文件名(D:/a.sql)
3. 导出所有表 -- mysqldump -uroot -p123456 school >D:/a.sql
mysqldump -u用户名 -p密码 库名 > 文件名(D:/a.sql)
4. 导出一个库 -- mysqldump -uroot -p123456 -B school >D:/a.sql
mysqldump -u用户名 -p密码 -B 库名 > 文件名(D:/a.sql)
可以-w携带备份条件
-- 导入
1. 在登录mysql的情况下:-- source D:/a.sql
source 备份文件
2. 在不登录的情况下
mysql -u用户名 -p密码 库名 < 备份文件
十、JDBC(重点)
1、JDBC简介
- JDBC(Java DataBase Connectivity,即java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序,即 JDBC是用于在Java语言编程中与数据库连接的API,JDBC是一个规范,它提供了一整套接口,允许以一种可移植的访问底层数据库API。 Java可以用它来编写不同类型的可执行文件。
- JDBC体系架构由两层组成:
- JDBC API: 提供应用程序到JDBC管理器的连接
- JDBC 驱动程序API: 支持JDBC管理器到驱动程序的连接
JDBC API使用驱动程序管理器并指定数据库的驱动程序来提供与异构数据库的透明连接。
JDBC驱动程序管理器确保使用正确的驱动程序来访问每个数据源。 驱动程序管理器能够支持连接到多个异构数据库的多个并发驱动程序。
以下是架构图,它显示了驱动程序管理器相对于JDBC驱动程序和Java应用程序的位置:
2、常见的JDBC组件
JDBC API 提供以下接口和类:
DriverManager
:此类管理数据库驱动程序列表。 使用通信子协议将来自java应用程序的连接请求与适当的数据库驱动程序进行匹配。在JDBC下识别某个子协议的第一个驱动程序将用于建立数据库连接。Driver
:此接口处理与数据库服务器的通信。我们很少会直接与Driver对象进行交互。 但会使用DriverManager对象来管理这种类型的对象。 它还提取与使用Driver对象相关的信息。Connection
:此接口具有用于联系数据库的所有方法。 连接(Connection)对象表示通信上下文,即,与数据库的所有通信仅通过连接对象。Connection 代表数据库。
//数据库设置自动提交
//事务提交
//事务回滚
connection.rollback();
connection.commit();
connection.setAutoCommit(true);
Statement
:使用从此接口创建的对象将SQL语句提交到数据库。 除了执行存储过程之外,一些派生接口还接受参数。Statement执行sql的对象
statement.executeQuery(); //查询操作返回ResultSet
statement.execute();//执行任何sql
statement.executeUpdate(); //更新,插入,删除都是用这个,返回一个受影响的行数
ResultSet
:在使用Statement对象执行SQL查询后,这些对象保存从数据库检索的数据。 它 作为一个迭代器并可移动ResultSet对象查询的数据。
resultSet.getObject(); //在不知道列类型的情况使用
//获取指定类型
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getDate();
SQLException
:此类处理数据库应用程序中发生的任何错误。
代码测试:
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//连接数据库 需要url,name,pwd
//协议://主机地址:端口号/数据库名?参数1&参数2&参数3
//mysql默认端口号3306
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
String name = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, name, password); //利用DriverManager获得数据库对象Connection
========================================
//执行sql
String sql = "SELECT * FROM users";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
========================================
//预编译方式执行sql
String sql = " SELECT *\n" +
" FROM t_web\n" +
" WHERE id = ? and name like ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 1);
preparedStatement.setString(2, "%ing%");
ResultSet rst = preparedStatement.executeQuery();
========================================
//拿到结果集,注意ResultSet对象按照数据库中表的字段输出
while(resultSet.next()){
System.out.println(resultSet.getObject("id"));
System.out.println( resultSet.getObject("NAME"));
System.out.println( resultSet.getObject("PASSWORD"));
System.out.println(resultSet.getObject("email"));
System.out.println(resultSet.getObject("birthday"));
}
//释放连接
resultSet.close(); //关闭结果集
statement.close(); //关闭sql语句提交对象
connection.close(); //关闭数据库
}
3、自定义JDBC工具类
- 把数据库驱动和连接数据库所需要的参数放在配置文件中,实现代码解耦:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
useName=root
password=123456
- 提取工具类:加载驱动,建立连接,释放资源:
public class JDBCUtil {
static String driver = null;
static String userName = null;
static String password = null;
static String url = null;
//通过配置文件加载驱动,只用加载一次即可,再静态代码块中实现
static{
try{
InputStream inputStream = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(inputStream);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
userName = properties.getProperty("useName");
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 releaseResource(Connection connection, Statement statement, ResultSet resultSet){
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
- 使用工具类来实现查询和删除
private static void executeQuery() throws SQLException {
//加载驱动并建立连接
Connection connection = JDBCUtil.getConnection();
//执行sql
String query = "SELECT * FROM users";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(query);
while (resultSet.next()) {
System.out.println(resultSet.getInt("id"));
System.out.println(resultSet.getString("NAME"));
System.out.println(resultSet.getString("PASSWORD"));
System.out.println(resultSet.getString("email"));
System.out.println(resultSet.getDate("birthday"));
System.out.println("=============================================");
}
//释放资源
JDBCUtil.releaseResource(connection,statement,resultSet);
}
private static void executeDelete() throws SQLException {
//加载驱动并建立连接
Connection connection = JDBCUtil.getConnection();
//执行sql
String delete = "DELETE FROM users WHERE id = 1";
Statement statement = connection.createStatement();
int i = statement.executeUpdate(delete);
if (i > 0) {
System.out.println("删除成功");
}
JDBCUtil.releaseResource(connection,statement,null);
}
4、JDBC模拟事务
public static void main(String[] args) {
Connection connection = null;
PreparedStatement statement = null;
try {
connection = JDBCUtil.getConnection();
connection.setAutoCommit(false);//关闭自动提交事务,开启一个事务
//a 账户 - 100
String sql1 = "UPDATE account SET money = money - 100 WHERE NAME = 'a'";
statement = connection.prepareStatement(sql1);
statement.executeUpdate();
//模拟出错
int i = 1/0;
//b账户 + 100
String sql2 = "UPDATE account SET money = money + 100 WHERE NAME = 'b'";
statement = connection.prepareStatement(sql2);
statement.executeUpdate();
//提交事务
connection.commit();
System.out.println("执行事务成功");
} catch (SQLException throwables) {
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
throwables.printStackTrace();
}finally {
JDBCUtil.releaseResource(connection, statement,null);
}
}
5、数据库连接池
5.1、什么是数据库连接池?
- 数据库连接池(Connection pooling)是 程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对池中的连接进行申请,使用,释放。
- 个人理解:创建数据库连接是一个很耗时的操作,也容易对数据库造成安全隐患。所以,在程序初始化的时候,集中创建多个数据库连接,并把他们集中管理,供程序使用,可以保证较快的数据库读写速度,还更加安全可靠。
- 官方链接
5.2、传统的连接机制与数据库连接池的运行机制区别
-
传统统链接: 一般来说,Java应用程序访问数据库的过程是:
- 装载数据库驱动程序;
- 通过JDBC建立数据库连接;
- 访问数据库,执行SQL语句;
- 断开数据库连接。
-
使用了数据库连接池的机制:
- 程序初始化时创建连接池
- 使用时向连接池申请可用连接
- 使用完毕,将连接返还给连接池
- 程序退出时,断开所有连接,并释放资源
5.3、DBCP的使用
public class DBCP_JDBCUtil {
private static DataSource dataSource = null;
//通过配置文件加载驱动,只用加载一次即可,再静态代码块中实现
static{
try{
InputStream inputStream = DBCP_JDBCUtil.class.getClassLoader().getResourceAsStream("dbcp.properties");
Properties properties = new Properties();
properties.load(inputStream);
//创建数据源 工厂模式 - - - > 创建
dataSource = BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//建立连接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
//释放资源
public static void releaseResource(Connection connection, Statement statement, ResultSet resultSet){
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
5.4、C3P0的使用
public class C3PO_JDBCUtils {
private static ComboPooledDataSource 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 releaseResource(Connection connection, Statement statement, ResultSet resultSet) {
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
十一、SQL 注入和PreparedStatement防止sql注入
1、什么是SQL注入?
SQL注入就是一种通过操作输入来修改后台SQL语句达到代码执行进行攻击目的的技术。
2、sql注入是怎么产生的
-
构造动态字符串是一种编程技术,它允许开发人员 在运行过程中动态构造SQL语句。开发人员可以使用动态SQL来创建通用、灵活的应用。动态SQL语句是在执行过程中构造的,它根据不同的条件产生不同的SQL语句。当开发人员在运行过程中需要根据不同的查询标准来决定提取什么字段(如SELECT语句),或者根据不同的条件来选择不同的查询表时,动态构造SQL语句会非常有用。
-
在java中动态构造SQL语句字符串:
String sql ="select * from users where `name` = '"+username+"' and password = '"+password+"'";
看上面代码我们可以控制输入参数username和password,修改所要执行SQL语句,达到攻击的目的。
- java测试sql注入
//这样可以查询出所有的用户信息 拼接完成的sql语句是:SELECT * FROM users WHERE NAME = ''OR'1=1' AND PASSWORD = '123456'
public static void main(String[] args) throws SQLException {
login(" 'or'1=1","123456");
}
private static void login(String username, String password) throws SQLException {
Connection connection = JDBCUtil.getConnection();
Statement statement = connection.createStatement();
String sql = "select * from users where name = '"+username+"' and password = '"+password+"'";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
System.out.println(resultSet.getInt("id"));
System.out.println(resultSet.getString("NAME"));
System.out.println(resultSet.getString("PASSWORD"));
System.out.println(resultSet.getString("email"));
System.out.println(resultSet.getDate("birthday"));
System.out.println("=============================================");
}
//释放资源
JDBCUtil.releaseResource(connection,statement,resultSet);
}
3、PreparedStatement防止sql注入
public static void main(String[] args) throws SQLException {
//加载驱动连接数据库
Connection connection = JDBCUtil.getConnection();
//获取preparedStatement对象
//预先设置sql未知参数使用?代替
String sql = "select * from users where name = ? and password = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//给sql填充参数,preparedStatement对'进行了转义
preparedStatement.setString(1,"'OR'1=1");
preparedStatement.setString(2,"123456");
//com.mysql.jdbc.JDBC42PreparedStatement@579bb367: select * from users where name = '\'OR\'1=1' and password = '123456'
System.out.println(preparedStatement.toString());
//执行sql
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
System.out.println(resultSet.getInt("id"));
System.out.println(resultSet.getString("NAME"));
System.out.println(resultSet.getString("PASSWORD"));
System.out.println(resultSet.getString("email"));
System.out.println(resultSet.getDate("birthday"));
System.out.println("=============================================");
}
//释放资源
JDBCUtil.releaseResource(connection,preparedStatement,resultSet);
}
NOTE:
简单的在参数后边加一个单引号,就可以快速判断是否可以进行SQL注入,这个百试百灵,如果有漏洞的话,一般会报错。
之所以 PreparedStatement能防止注入,是因为它把单引号转义了,变成了’,这样一来,就无法截断SQL语句,进而无法拼接SQL语句,基本上没有办法注入了。所以,如果不用PreparedStatement,又想防止注入,最简单粗暴的办法就是过滤单引号,过滤之后,单纯从SQL的角度,无法进行任何注入。其实,刚刚我们提到的是String参数类型的注入,大多数注入,还是发生在数值类型上,幸运的是PreparedStatement为我们提供了st.setInt(1, 999);这种数值参数赋值API,基本就避免了注入,因为如果用户输入的不是数值类型,类型转换的时候就报错了。