Mysql自學之路(狂神說)


1.MYSQL簡介

MySQL是一個輕量級關系型數據庫管理系統,由瑞典MySQL AB公司開發,目前屬於Oracle公司。目前MySQL被廣泛地應用在Internet上的中小型網站中,由於體積小、速度快、總體擁有成本低,開放源碼、免費,一般中小型網站的開發都選擇Linux + MySQL作為網站數據庫。MySQL是一個關系型數據庫管理系統,MySQL是一種關聯數據庫管理系統,關聯數據庫將數據保存在不同的表中,而不是將所有數據放在一個大倉庫內,就增加了速度並提高了靈活性。

由於分布式和集群的出現,mysql也用於大型的網站上。

2.數據庫操作

學習流程: 操作數據庫 -> 操作數據庫中的表 -> 操作數據庫中的表的數據

**sql語言分類: **

名稱 解釋 命令
DDL 定義和管理數據對象,如:數據庫,數據表等 create,drop,alter
DML 用於操作數據庫對象所包含的數據 insert,delete,update
DQL 用於查詢數據庫對象所包含的數據 select
DCL 用於管理數據庫,包括管理權限和數據更改 grant,commit,rollback

2.1 連接數據庫

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
? --命令關鍵詞 : 尋求幫助
-- 表示注釋

2.2 操作數據庫

  1. 創建數據庫

     CREATE DATABASE [IF NOT EXISTS] webos;
    
  2. 刪除數據庫

    DROP DATABASE [IF EXISTS] webos;
    
  3. 查看所有的數據庫

    SHOW DATABASES;
    
  4. 使用數據庫

    USE student;
    

NOTE:

​ 操作sql語句時不區分大小寫:例如你運行:
​ SELECT * FROM table WHERE txt = 'a'
​ 那么在utf8_bin中你就找不到 txt = 'A' 的那一行, 而 utf8_general_ci 則可以.

image-20200717090941236

2.3數據值和列類型

列類型 : 規定數據庫中該列存放的數據類型

  1. 數值類型
  2. 字符串類型
  3. 日期和時間型數值類型
  4. null 值
    • 理解為 "沒有值" 或 "未知值"
    • 不要用NULL進行算術運算 , 結果仍為NULL

2.4 數據字段屬性

UnSigned

  • 無符號的
  • 聲明該數據列不允許負數 .

ZEROFILL

  • 0填充的
  • 不足位數的用0來填充 , 如int(3),5則為005

Auto_InCrement

  • 自動增長的 , 每添加一條數據 , 自動在上一個記錄數上加 1(默認)

  • 通常用於設置主鍵 , 且為整數類型

  • 可定義起始值和步長

    • 當前表設置步長(AUTO_INCREMENT=100) : 只影響當前表
    • SET @@auto_increment_increment=5 ; 影響所有使用自增的表(全局)

NULL 和 NOT NULL

  • 默認為NULL , 即沒有插入該列的數值
  • 如果設置為NOT NULL , 則該列必須有值

DEFAULT

  • 默認的
  • 用於設置默認值
  • 例如,性別字段,默認為"男" , 否則為 "女" ; 若無指定該列的值 , 則默認值為"男"的值

2.4 操作表

-- 目標 : 創建一個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; -- 顯示建表語句

2.5 MYISAM和INNODB的區別

名稱 MyISAM InnoDB
事務支持(ACID) 不支持 支持
行級鎖 不支持 支持
外鍵約束 不支持 支持
全文索引 支持 不支持
空間大小 比較小 約為MyISAM的兩倍

經驗 ( 適用場合 ) :

  • 適用 MyISAM : 節約空間及相應速度
  • 適用 InnoDB : 安全性 , 事務處理及多用戶操作數據表

2.6 修改和刪除表

修改表

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 字段名

刪除表

DROP TABLE teacher; --刪除表
drop table if exists 表名

3. MYSQL數據管理(DML)

3.1 外鍵

簡介:

  • 外鍵是用來連接數據庫的,保證數據庫的參照完整性。
  • 表的外鍵是另一表的主鍵,外鍵是可以有重復的,可以是空值。
  • 以另一個關系的外鍵作主關鍵字的表被稱為主表,具有此外鍵的表被稱為主表的從表
  • 保持數據一致性完整性,主要目的是控制存儲在外鍵表中的數據,約束。使兩張表形成關聯,外鍵只能引用外表中的列的值或使用空值。
  • 刪除表時,只能先刪除從表,再刪除主表。
  • 在開發的時候不會使用外鍵

在創建表的時候創建外鍵

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`)
) 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`; -- 刪除索引

3.2 添加

INSERT INTO `表名`(`字段1`,`字段2`,...) VALUES (值1,值2,...),(值1,值2,...),...

NOTE: 可以省略字段名,但是值和字段一定要一一對應

3.3 修改

UPDATE `grade` SET `gradeName` = '研一' WHERE `gradeID` = 1;
UPDATE 表名 SET 字段名 = 新值,... WHERE 條件

3.4 刪除

刪除一行或者多行數據

-- 刪除數據
DELETE FROM `grade` WHERE `gradeID` BETWEEN 1 AND 4 
DELETE FROM 表名 WHERE 條件

刪除整張表 delete 和 truncate

DELETE FROM `grade`

INSERT INTO `grade`(`gradeName`) VALUES ('大一'),('大二'),('大三'),('大四')

TRUNCATE `grade`

NOTE :

  • delete刪除表時,自動增量不會變,truncate 刪除表時,自動增量會置一
  • 使用TRUNCATE TABLE不會對事務有影響

4. 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

–第二步:WHERE條件過濾

–第三步:GROUP BY分組

–第四步:執行SELECT投影列

–第五步:HAVING條件過濾

–第六步:執行ORDER BY 排序

去重(DISTINCT): 去掉SELECT查詢返回的記錄結果中重復的記錄 ( 返回所有列的值都相同 ) , 只返回一條

-- 用as重命名 將結果中表意不清的數據列重新命名
SELECT DISTINCT `studentno` AS '參加過考試的學生' FROM result

邏輯表達式

模糊查詢:比較操作符

NOTE :

  • 使用 like 操作符時 %:表示任意字符 _:表示一個字符
  • in(一個或者多個具體的結果)

4.1 連接查詢

join連接

join 連接的區別

操作符名稱 描述
inner join 如果表中至少有一個匹配則返回行
left join (以左表作為基准,右邊表來一一匹配,匹配不上的,返回左表的記錄,右表以NULL填充)
right join (以右表作為基准,左邊表來一一匹配,匹配不上的,返回右表的記錄,左表以NULL填充)

思路:

  1. 分析需求,確定查詢的列來源於那些表
  2. 是否使用連接查詢,使用那種查詢
  3. 確認表與表之間的交叉點
-- 查詢參加了考試的同學信息(學號,學生姓名,科目編號,分數)
-- 左連接實現
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 與 join on的區別

-- s.`studentno` != 1002的記錄還在,這是由left join的特性決定的,使用left join時on后面的條件只對右表有效(可以看到右表的id=4的記錄沒了)
SELECT s.`studentno`,`studentname`,`subjectno`,`studentresult`
FROM student s
LEFT JOIN `result` r ON s.studentno = r.studentno AND s.`studentno` != 1002

image-20200720184657293

-- 先用on后面的條件過濾,再用where條件過濾
SELECT s.`studentno`,`studentname`,`subjectno`,`studentresult`
FROM student s
LEFT JOIN `result` r ON s.studentno = r.studentno
WHERE s.`studentno` != 1002

image-20200720184742508

自連接

image-20200720213719994

-- 編寫SQL語句,將欄目的父子關系呈現出來 (父欄目名稱,子欄目名稱)
-- 核心思想:把一張表看成兩張一模一樣的表,然后將這兩張表連接查詢(自連接)
SELECT c1.`categoryName` AS '父欄目',c2.`categoryName` AS '子欄目'
FROM category AS c1
INNER JOIN category AS c2 ON c1.`categoryid` = c2.`pid`

image-20200720213734788

4.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

/*============== 分頁 ================
語法 : 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

4.3 子查詢

子查詢的定義:

  • 子查詢是將一個查詢語句嵌套在另一個查詢語句中;
  • 在特定情況下,一個查詢語句的條件需要另一個查詢語句來獲取,內層查詢(inner query)語句的查詢結果,可以為外層查詢(outer query)語句提供查詢條件。
-- 查詢課程為 高等數學-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'
	)
)

5、MYSQL函數

5.1 常用函數

數據函數

 SELECT ABS(-8);  /*絕對值*/
 SELECT CEILING(9.4); /*向上取整*/
 SELECT FLOOR(9.4);   /*向下取整*/
 SELECT RAND();  /*隨機數,返回一個0-1之間的隨機數*/
 SELECT SIGN(0); /*符號函數: 負數返回-1,正數返回1,0返回0*/

字符串函數

 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('狂神說堅持就能成功'); /*反轉

日期和時間函數

 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 VERSION();  /*版本*/
 SELECT USER();     /*用戶*/

5.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

分組

NOTE:在含有Group by子句的查詢語句中,對select關鍵字后的目標列,存在以下規律

  1. 使用group by 時,select 涉及的列要么是參與分組的列,要么列包含在聚合函數中
  2. 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`

5.3 MD5加密函數

一、MD5簡介

MD5即Message-Digest Algorithm 5(信息-摘要算法5),用於確保信息傳輸完整一致。是計算機廣泛使用的雜湊算法之一(又譯摘要算法、哈希算法),主流編程語言普遍已有MD5實現。將數據(如漢字)運算為另一固定長度值,是雜湊算法的基礎原理,MD5的前身有MD2、MD3和MD4。
二、實現數據加密

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'));

三、md5實現數據匹配

SELECT * FROM md5test WHERE `name` = 'zhangsan' AND pwd = MD5('123456')

四、md5特點

不可逆性 --- 根據 MD5 值計算不出原始數據

唯一性 --- 不同原始數據會有不同的 MD5 值 (不完全可靠,后面說)

6、數據庫事務的四大特性以及隔離級別

什么是事務

  • 事務就是將一組SQL語句放在同一批次內去執行
  • 如果一個SQL語句出錯,則該批次內的所有SQL都將被取消執行

事務的ACID四大特性

⑴ 原子性(Atomicity)

  原子性是指事務包含的所有操作要么全部成功,要么全部失敗回滾,這和前面兩篇博客介紹事務的功能是一樣的概念,因此事務的操作如果成功就必須要完全應用到數據庫,如果操作失敗則不能對數據庫有任何影響。

⑵ 一致性(Consistency)

  一致性是指事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之后都必須處於一致性狀態。

  拿轉賬來說,假設用戶A和用戶B兩者的錢加起來一共是5000,那么不管A和B之間如何轉賬,轉幾次賬,事務結束后兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。

⑶ 隔離性(Isolation)

  隔離性是當多個用戶並發訪問數據庫時,比如操作同一張表時,數據庫為每一個用戶開啟的事務,不能被其他事務的操作所干擾,多個並發事務之間要相互隔離。

  即要達到這么一種效果:對於任意兩個並發的事務T1和T2,在事務T1看來,T2要么在T1開始之前就已經結束,要么在T1結束之后才開始,這樣每個事務都感覺不到有其他事務在並發地執行。

  關於事務的隔離性數據庫提供了多種隔離級別,稍后會介紹到。

⑷ 持久性(Durability)

  持久性是指一個事務一旦被提交了,那么對數據庫中的數據的改變就是永久性的,即便是在數據庫系統遇到故障的情況下也不會丟失提交事務的操作。

  例如我們在使用JDBC操作數據庫時,在提交事務方法后,提示用戶事務操作完成,當我們程序執行完成直到看到提示后,就可以認定事務以及正確提交,即使這時候數據庫出現了問題,也必須要將我們的事務完全執行完成,否則就會造成我們看到提示事務處理完畢,但是數據庫因為故障而沒有執行事務的重大錯誤。

基本語法

SET autocommit = 0 -- 關閉事務自動提交
START TRANSACTION -- 開啟事務

COMMIT -- 提交事務

ROLLBACK -- 將事務回滾
SET autocommit = 1 -- 開啟事務自動提交
-- 保存點
SAVEPOINT 保存點名稱 -- 設置一個事務保存點
ROLLBACK TO SAVEPOINT 保存點名稱 -- 把事務回滾到保存點
RELEASE SAVEPOINT 保存點名稱 -- 釋放保存點

事務實踐

/*
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

7、索引

索引的作用

  • 提高查詢速度
  • 確保數據的唯一性
  • 可以加速表和表之間的連接 , 實現表與表之間的參照完整性
  • 使用分組和排序子句進行數據檢索時 , 可以顯著減少分組和排序的時間
  • 全文檢索字段進行搜索優化.

分類

  • 主鍵索引 (Primary Key)
  • 唯一索引 (Unique)
  • 常規索引 (Index)
  • 全文索引 (FullText)

主鍵索引

主鍵 : 某一個屬性組能唯一標識一條記錄

特點 :

  • 最常見的索引類型
  • 確保數據記錄的唯一性
  • 確定特定數據記錄在數據庫中的位置

唯一索引

作用 : 避免同一個表中某數據列中的值重復

與主鍵索引的區別

  • 主鍵索引只能有一個
  • 唯一索引可能有多個

常規索引

作用 : 快速定位特定數據

注意 :

  • index 和 key 關鍵字都可以設置常規索引
  • 應加在查詢找條件的字段
  • 不宜添加太多常規索引,影響數據的插入,刪除和修改操作
CREATE TABLE `result`(
    -- 省略一些代碼
    INDEX/KEY `ind` (`studentNo`,`subjectNo`) -- 創建表時添加
)

-- 創建后添加
ALTER TABLE `result` ADD INDEX `ind`(`studentNo`,`subjectNo`);

全文索引

百度搜索:全文索引

作用 : 快速定位特定數據

注意 :

  • 只能用於CHAR , VARCHAR , TEXT數據列類型
  • 適合大型數據集
/*
開始之前,先說一下全文索引的版本、存儲引擎、數據類型的支持情況
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'

image-20200721220352516

/*使用全文索引*/
-- 全文搜索通過 MATCH() 函數完成。
-- 搜索字符串作為 against() 的參數被給定。搜索以忽略字母大小寫的方式執行。對於表中的每個記錄行,MATCH() 返回一個相關性值。即,在搜索字符串與記錄行在 MATCH() 列表中指定的列的文本之間的相似性尺度。
EXPLAIN SELECT * FROM student WHERE MATCH(`studentname`) AGAINST('haungaa')

image-20200721220516472

索引創建的語法:

/*
#方法一:創建表時
      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;
*/

索引的效果 耗時測試

USE school
CREATE TABLE `app_user` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) DEFAULT '',
`email` VARCHAR(50) NOT NULL,
`phone` VARCHAR(20) DEFAULT '',
`gender` TINYINT(4) UNSIGNED DEFAULT '0',
`password` VARCHAR(100) NOT NULL DEFAULT '',
`age` TINYINT(4) DEFAULT NULL,
`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

-- 插入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`)VALUES(CONCAT('用戶',i),'19224305@qq.com','123456789',FLOOR(RAND()*2));
SET i=i+1;
END WHILE;
RETURN i;
END;
SELECT mock_data() -- 執行此函數 生成一百萬條數據

-- 建立索引前
EXPLAIN SELECT * FROM app_user WHERE NAME = '用戶9999'; -- 查看耗時  0.530 sec
-- 建立索引
CREATE INDEX index_app_user_name ON app_user(`name`)
-- 建立索引后
EXPLAIN SELECT * FROM app_user WHERE NAME = '用戶9999'; -- 查看耗時  0.001 sec

image-20200721221152727

image-20200721221228386

索引准則

  • 索引不是越多越好
  • 不要對經常變動的數據加索引
  • 小數據量的表建議不要加索引
  • 索引一般應加在查找條件的字段

8、用戶管理

基本命令

-- ========================================語法=============================================
/* 用戶和權限管理 */ ------------------
用戶信息表: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] ...

9、MYSQL備份

數據庫備份必要性

  1. 保證重要數據不丟失

  2. 數據轉移

MySQL數據庫備份方法

  1. mysqldump備份工具

  2. 數據庫管理工具,如SQLyog

  3. 直接拷貝數據庫文件和相關配置文件

mysqldump客戶端作用 :

  1. 轉儲數據庫

  2. 搜集數據庫進行備份

  3. 將數據轉移到另一個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密碼 庫名 < 備份文件

10、JDBC

10.1 簡介

JDBC代表Java數據庫連接(Java Database Connectivity),它是用於Java編程語言和數據庫之間的數據庫無關連接的標准Java API,換句話說:JDBC是用於在Java語言編程中與數據庫連接的API。從根本上說,JDBC是一個規范,它提供了一整套接口,允許以一種可移植的訪問底層數據庫API。 Java可以用它來編寫不同類型的可執行文件。

jdbc體系架構由兩層組成:

  1. JDBC API: 提供應用程序到JDBC管理器的連接
  2. JDBC 驅動程序API: 支持JDBC管理器到驅動程序的連接

JDBC API使用驅動程序管理器並指定數據庫的驅動程序來提供與異構數據庫的透明連接。

JDBC驅動程序管理器確保使用正確的驅動程序來訪問每個數據源。 驅動程序管理器能夠支持連接到多個異構數據庫的多個並發驅動程序。

以下是架構圖,它顯示了驅動程序管理器相對於JDBC驅動程序和Java應用程序的位置

img

10.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);
    //執行sql
    String sql = "SELECT * FROM users";
    Statement statement = connection.createStatement();
    ResultSet resultSet = statement.executeQuery(sql);
    //拿到結果集
    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();
    connection.close();
}

10.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);
    }

10.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);
        }
    }

10.5 數據庫連接池

什么是數據庫連接池?

官方:數據庫連接池(Connection pooling)是程序啟動時建立足夠的數據庫連接,並將這些連接組成一個連接池,由程序動態地對池中的連接進行申請,使用,釋放。
個人理解:創建數據庫連接是一個很耗時的操作,也容易對數據庫造成安全隱患。所以,在程序初始化的時候,集中創建多個數據庫連接,並把他們集中管理,供程序使用,可以保證較快的數據庫讀寫速度,還更加安全可靠。

傳統的連接機制與數據庫連接池的運行機制區別

傳統統鏈接: 一般來說,Java應用程序訪問數據庫的過程是:

  1. 裝載數據庫驅動程序;
  2. 通過JDBC建立數據庫連接;
  3. 訪問數據庫,執行SQL語句;
  4. 斷開數據庫連接。

使用了數據庫連接池的機制:

  1. 程序初始化時創建連接池
  2. 使用時向連接池申請可用連接
  3. 使用完畢,將連接返還給連接池
  4. 程序退出時,斷開所有連接,並釋放資源

img

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();
            }
        }
    }
}
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();
            }
        }
    }
}

11、sql 注入和PreparedStatement

11.1 什么是SQL注入?

看起來很復雜,其實很簡單就能解釋,SQL注入就是一種通過操作輸入來修改后台SQL語句達到代碼執行進行攻擊目的的技術。

11.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);
    }

11.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);
    }

總結:

​ 簡單的在參數后邊加一個單引號,就可以快速判斷是否可以進行SQL注入,這個百試百靈,如果有漏洞的話,一般會報錯。 之所以PreparedStatement能防止注入,是因為它把單引號轉義了,變成了',這樣一來,就無法截斷SQL語句,進而無法拼接SQL語句,基本上沒有辦法注入了。所以,如果不用PreparedStatement,又想防止注入,最簡單粗暴的辦法就是過濾單引號,過濾之后,單純從SQL的角度,無法進行任何注入。其實,剛剛我們提到的是String參數類型的注入,大多數注入,還是發生在數值類型上,幸運的是PreparedStatement為我們提供了st.setInt(1, 999);這種數值參數賦值API,基本就避免了注入,因為如果用戶輸入的不是數值類型,類型轉換的時候就報錯了。

轉義字符:

一、轉義字符什么時候使用:
//1、在每門計算機語言里,都有一些字符代表着特殊意義。
//如果,我們需要使用字符的本意(如:我就希望使用左尖括號),就得用轉義字符
//2、有些字符是沒有直接輸出的。就需要轉義字符;如:回車;

//如:在HTML里,左尖括號代表標簽開始,右尖括號代表標簽結束。
//如果,我們需要使用字符的本意(如:我就希望使用左尖括號),就得用轉義字符。
//如:在JS中,雙引號表示字符串的開始和結束,如果,我們就像使用雙引號,就得用轉義字符。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM