記事ディレクトリ
MySQL
1 はじめに
1.1 データベースの分類
-
リレーショナル データベース: 行、列 – (SQL )
- MySQL、Oracle、SQLサーバー
- データは、テーブルとテーブルの間、および行と列の間の関係を通じて格納されます。学生情報、出席簿
-
非リレーショナル データベース: {Key, Value} (NoSQL – SQL だけではありません)
- Redis、MongoDB
- オブジェクトに保存され、オブジェクト自体の属性によって決定されます。
DBMS – データベース管理システム
-
データベース管理ソフト!データを科学的かつ効果的に管理し、データを維持および取得する
-
MySQL - 本質的にはデータベース管理システムです。!!DB はデータの保存のみを目的とした純粋なデータベースです
データベース XXX 言語
- DDL: 定義
- DML: 操作
- DQL: クエリ
- DCL: 制御
1. データベースの操作 2. データベースのテーブルの操作 3. データベースのテーブルのデータの操作
mysql キーワードは大文字と小文字を区別しません。
テーブル名またはフィールド名が特殊文字の場合は、**``** を含める必要があります
1.2MySQL
- リレーショナル データベース管理システム、[行、列]
- 過去と現在 – MySQLAB – Oracle
- 最高のデータベース管理システムの 1 つ
- 小さいサイズ、高速、低コスト (全員が知っておくべき) - 中小規模の Web サイトまたは大規模な Web サイトに適しています。
- 5.7 安定//8.0 比較的安定
2. 基本
2.1 データ型
2.1.1 値
tinyint -- 十分小的数据,一个byte
smallint -- 较小的数据,2个byte
mediumint -- 中等的数据,3个byte
***int --标准的整数(常用) - 4个byte
bigint -- 较大的数据,8个byte
float -- 4个byte
double --8个byte
decimal -- 字符串形式的浮点数 - 精度问题!(金融,一般使用!)
2.1.2 文字列
char -- 字符串,0-255
varchar -- 常用,可变字符串,0-65535
tinytext -- 微型文本,2^8-1
text -- 大型文本,文本串,2^16-1(保存大文本)
2.1.3 時刻/日付
date -- YYYY-MM--DD
time -- HH:mm:ss
datetime -- YYYY-MM--DD HH:mm:ss - 最常用
timestamp -- 时间戳,1970/1/1/00:00:00 - 全球统一!
year -- 年份表示
2.1.4null
没有值,未知
不要使用null进行运算;如果使用,一定为null
2.2 フィールドのプロパティ
primary key --主键
notnull --非空
NULL -- 设置为notnull,如果不赋值,就会报错!
Unsigned -- 无符号的整数 不能声明为负数
zerofill -- 0填充
--不足的位数,全部用0填充,int(3)--005
autoincrement --自增
-- 自动在上一条记录的基础上+1
-- 通常用来设置唯一的主键
-- 必须是整数类型
-- 可以自己定义设置主键自增的初始值和步长
default --默认
2.3 データベースエンジン
INNODB (デフォルト)
MYISAM (5.5 の初期の年で使用)
ミサム | INNODB | |
---|---|---|
トランザクションサポート | サポートしません | サポート |
データ行のロック | サポートしません | サポート |
外部キー制約 | サポートしません | サポート |
全文インデックス | サポート | サポートしません |
テーブルスペースのサイズ | 小さい | 大きめ(MYISAMの約2倍) |
-
通常の使用法:
- MYISAM: スペースを節約し、より高速に
- INNODB: セキュリティ、トランザクション処理のサポート、マルチテーブル、マルチユーザー操作
-
物理空間が存在する場所:
- すべてのデータ ファイルはデータ ディレクトリに保存されます
- 本質は依然としてファイルストレージです
-
物理ファイル上の MySQL エンジンの違い:
- Innodb: データ テーブルには *.frm ファイルが 1 つだけあり、親ディレクトリの ibdata
- ミサム:
- * .frm - テーブル構造の定義ファイル
- *.MYD - データ ファイル (データ)
- *.MYI - インデックス ファイル (インデックス)
2.4 文字セットエンコーディングの設定
CHARSET=utf8
- 設定されていない場合は、mysql のデフォルトの文字セット エンコーディングになります (中国語はサポートされていません)。
2.5 データベース/テーブルの作成
--语句:所有的语句使用 ; 结尾!
create database “数据库名称” :创建数据库
show database
use “数据库名”:切换数据库
show tables; - 查看数据库中所有的表
describe “表名称” - 查看表中的信息
exit :退出连接
--单行注释
/* */ --多行注释!
--创建:
create datebase if not exist `数据库名称`
--删除:
drop database if exist `数据库名称`
--使用:
use `数据库名称` select `表名` from `数据库名称`
--查看:
show databases
2.6 テーブルの変更・削除
ALTER TABLE `旧表名` RENAME `新表名`;-- 修改表名:
ALTER TABLE `表名` ADD `字段` INT(3); -- 增加表的字段:
--修改
(1)重命名:ALTER TABLE `teacher01` CHANGE `age` `age01` INT(3);
【原来的`age`,现在叫·`age01`】
(2)修改字段约束:ALTER TABLE `teacher01` MODIFY `age` VARCHAR(10);
【原来的·`age`是int,现在变成varchar】
--删除
ALTER TABLE `teacher01` DROP `age01`;-- 删除表的字段:
DROP TABLE if EXISTS `teacher01` ;-- 删除表:
3. データ管理
3.1 外部キー (理解)
参照(外部キー)があり、気軽には削除できません。
削除する必要がある場合は、まずスレーブ テーブル (他人のテーブルを参照) を削除し、次にメイン テーブル (参照されるテーブル) を削除します。
-
方法1:テーブル作成時に制約を追加する(面倒、複雑)
- 最初のステップは、外部キー KEY を定義することです。
KEY `FK_gradeid` (`gradeid`)
- 2 番目のステップは、外部キーに制約を追加することです。
CONSTRAIN `FK_gradeid` FOREGIN KEY (`gradeid`)REFERENCES `grade` (`gradeid`)
-
方法 2: テーブルを作成した後、外部キー制約を追加します。
ALTER TABLE `student`
ADD CONSTRAINT `约束名` FOREIGN KEY(`作为外键的列`) REFERENCES `引用表` (`引用列`)
上記の操作は物理外部キー、データベース レベルの外部キーです。推奨されません。(テーブル間の関連付けは削除が非常に面倒!) データベースの多すぎによるトラブルを回避!!!ここで調べてください!
ベストプラクティス:
- データベースは単純なテーブルであり、データを保存するためにのみ使用され、行 (データ) と列 (フィールド) のみが使用されます。
-外部キーを使用したい場合は、それを実現するプログラムを使用してください。> (Ali 仕様: 一部の外部キーはアプリケーション層で解決されます。つまり、プログラムに実装されます)
3.2DML
(全部覚えて覚えて)追加・削除・修正・確認
データベース管理言語
-
挿入:追加
-
更新: 変更する
-
削除:削除
3.3挿入
INSERT INTO `student` (`name`) VALUES ('吴青峰'),
INSERT INTO `表名 ` (`字段名`) VALUES ('字段内容'),
-
フィールド間にはカンマを使用する必要があります
-
フィールド名は書かないでください。フィールドの内容はすべて書く必要があります。
3.4アップデート
UPDATE `student` SET `name`='告五人' WHERE `id` =1;-- 修改一个属性
UPDATE `student` SET `name`='告五人',`email`='[email protected]' WHERE `id` =1;-- 修改多个属性
UPDATE `student` SET `birthday`=CURRENT_TIME WHERE `id` =1;-- 修改一个属性(日期)
オペレーター | 意味 | 範囲 | 結果 |
---|---|---|---|
= | 同等 | 5=6 | 間違い |
<> または != | 等しくない | 5!=6 | 真実 |
> | |||
< | |||
>= | |||
<= | |||
間…と… | 一定の範囲内で | 2から5の間 | [2,5] |
と | && | 5>1 および 1>2 | 間違い |
また | || | 5>1 または 1>2 | 真実 |
3.5選択
DELETE FROM `student` WHERE `id`=2;
3.6 切り捨て
TRUNCATE `student`;
- データベーステーブルを空にしても、テーブルの構造とインデックスの制約は変わりません。
delete と TRUNCATE の違い:
-
同じです。データは削除できますが、テーブル構造は削除されません。
-
異なる点: TRUNCATE は自動インクリメント列をリセットし、カウンターは 0 に戻ります。これはトランザクションには影響しません。
-
異なる (理解): DELETE の問題: データベースを再起動する、現象
- INNODB: 自己インクリメント列は 1 から始まり、メモリに保存され、電源がオフになると失われます。
- MYISAM: 続行され、ファイルに保存され、失われることはありません
4. DQL クエリ データ (強調)
4.1. DQL (データクエリ言語)
順番に注意してください!!!順序が重要です! !!順序が重要です! !!順序が重要です! !!
SELECT 去重 要查询的字段 FROM 表(注意:表和字段要取别名)
LEFT/RIGHT/INNER JOIN 要联接的表 ON 等值判断
WHERE 具体的值/或者子查询语句
GROUP BY 通过哪个字段来分组
HAVING 过滤分组后的信息,条件和where是一样的,只是位置不同
ORDER BY 通过哪个字段排序 升序还是降序
LIMIT startindex,pagesize
- すべてのクエリ操作でこれが使用されます。
- 単純なクエリ、複雑なクエリ、使用
- データベースはコア言語です!
- 最も頻繁に使用される言語
4.2 クエリフィールドを指定します。
4.2.1 フィールドまたはテーブルに別名を付けることができます
SELECT `studentno` AS 学号 ,`studentname` AS 学生姓名 FROM `student` ;
4.2.2 重複排除: SELECT ステートメント クエリの結果から重複データを削除します。
SELECT DISTINCT `studentno` FROM `result`;
4.2.3 データベース列(式)
SELECT VERSION();--查询系统版本!
SELECT 100*3-1 AS 计算结果; --用来计算!
SELECT @@auto_increment_increment ; --查询自增的步长(变量)
SELECT `studentno`,`studentresult`+1 AS 增加之后 FROM `result`;
データベース内の式: テキスト値、列、NULL、関数、計算式、システム変数
からの SELECT 式表名
4.3 WHERE 条件句
-
機能: データ内の条件を満たす値を取得します。
-
検索条件は 1 つ以上の式で構成されています。結果はブール値です!
4.3.1 論理演算子
オペレーター | 文法 | 説明 |
---|---|---|
と && | aとbaと&b | ロジックと |
または || | a または ba||b | 論理的または |
いいえ ! | ではありません!a | 論理否定 |
できるだけ英字を使いましょう!!!
SELECT `studentno`,`studentresult` FROM `result` WHERE `studentresult`>=90 and `studentresult`<=100;
4.3.2 ファジークエリ
- 基本的には比較演算子
オペレーター | 文法 | 説明 |
---|---|---|
無効である | a は NULL です | ブール値 |
NULL ではありません | a は NULL ではありません | ブール値 |
…と…の間 | bとcの間のa | a が b と c の間にある場合は true |
好き | aはbに似ています | SQL 一致、a が b と一致する場合は true |
の | (a1、a2、a3、…) の a | a が a1、a2、a3 のいずれかにある場合は true |
SELECT `studentno`,`studentname` FROM `student` WHERE `studentname` LIKE '李%';--%任意个字符,_只有一个
SELECT `studentno`,`studentname` FROM `student` WHERE `studentno` IN (1001,1002,1003);
SELECT `studentno`,`studentname` FROM `student` WHERE `address` IN ('重庆','迈阿密');
4.3.3 結合テーブルクエリ
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-opYF2pGN-1690205301627)(MySQL.assets/image-20210115210115224101766.png)]
操作する | 説明 |
---|---|
内部結合 | テーブル内に一致するものがあれば、一致した結果を返します |
左結合 | 右のテーブルに一致するものがない場合でも、左のテーブルからすべての値を返します |
右結合 | 左側のテーブルに一致するものがない場合でも、右側のテーブルからすべての値を返します |
--inner join
SELECT s.`studentno`,`studentname`,`subjectno`,`studentresult`
FROM `student` AS s
INNER JOIN `result` AS r
WHERE s.studentno = r.studentno;--where用于单个
--right join
SELECT s.`studentno`,`studentname`,`subjectno`,`studentresult`
FROM `student` AS s
RIGHT JOIN `result` AS r
ON s.studentno=r.studentno;--on用于批量
--left join
SELECT s.`studentno`,`studentname`,`subjectno`,`studentresult`
FROM `student` AS s
LEFT JOIN `result` AS r
ON s.studentno=r.studentno;
--查询缺考的同学,看出左右连接的区别:
SELECT s.`studentno`,`studentname`,`subjectno`,`studentresult`
FROM `student` AS s
LEFT JOIN `result` AS r
ON s.studentno=r.studentno
WHERE `studentresult` IS NULL;
--join … on … 连接查询(批量)
--join… where… 等值查询(单个)
#联查三张!
SELECT s.`studentno`,`studentname`,`subjectname`,`studentresult`
FROM `student` AS s
RIGHT JOIN `result` AS r
ON s.studentno=r.studentno
INNER JOIN `subject`AS sub
ON r.subjectno=sub.subjectno;
ジョイントテーブルクエリ - アイデア
1. クエリしたいデータ SELECT XXX
2. どのテーブルから FROM テーブル XXXX JOIN テーブルが ON クロス条件であるかを確認します。
3. 複数テーブルのクエリという種類のクエリがあるとします。時間をかけて、最初に 2 つのテーブルをクエリしてから、ゆっくりと追加してください。
4.3.4 自己接続(理解)
- 独自のテーブルと独自のテーブルを接続します。コア: 1 つのテーブル。それを 2 つの同一のテーブルに分割するだけです。
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-jaeK7fHn-1690205301627)(MySQL.assets/image-20210115225544881.png)]
父親:
カテゴリID | 種別名 |
---|---|
2 | 情報技術 |
3 | ソフトウェア開発 |
5 | アートデザイン |
サブクラス:
ピド | カテゴリID | 種別名 |
---|---|---|
3 | 4 | データベース |
2 | 8 | オフィス情報 |
3 | 6 | ウェブ開発 |
5 | 7 | psテクノロジー |
操作: 親クラスに対応するサブクラスの関係を問い合わせます。
父親 | サブクラス |
---|---|
情報技術 | オフィス情報 |
ソフトウェア開発 | データベース |
ソフトウェア開発 | ウェブ開発 |
アートデザイン | psテクノロジー |
SELECT a.`categoryname` AS '父栏目',b.`categoryname` AS '子栏目'
FROM `category` AS a,`category` AS b
WHERE a.categoryid=b.pid;
4.4 ページング/ソート
4.4.1 並べ替え:
- (DESC-降順、ASC-昇順)
ORDER BY `studentresult`DESC
4.4.2 ページネーション:
LIMIT 0,2
LIMIT (n-1)*pagesize , pagesize
* LIMIT (n-1) pagesize、pagesize n は現在のページ、pagesize ページ サイズ、つまりページに配置されるデータ量を表します。
総ページ数 = データサイズ / ページサイズ
4.5 サブクエリ
本質: where ステートメント内で、サブクエリ ステートメントをネストします。
where (select * from .XXX)
SELECT DISTINCT s.`studentno`,s.`studentname`,`studentresult`
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`='高等数学-2'
)
4.6 グループ化とフィルタリング
SELECT `subjectname`,AVG(`studentresult`),MAX(`studentresult`),MIN(`studentresult`)
FROM `result` AS r
INNER JOIN `subject` AS sub
ON r.subjectno=sub.subjectno
GROUP BY r.subjectno --通过什么分组
HAVING AVG(`studentresult`)>80 --分组之后加限制
4.7MySQL 関数
4.7.1 よく使われる機能(あまり使われない機能)
数学の部分 | 説明 |
---|---|
ABSを選択(-8) | 絶対値 |
天井を選択(9.5) | 切り上げ、10 |
セレクトフロア(9.5) | 切り捨て |
ランドを選択() | 0 ~ 1 の間の乱数 |
符号選択(-10) | 数値の符号を決定する |
時刻日付関数 | 説明 |
---|---|
SELECT CURRENT_DATE() | 現在の日付を取得する |
今すぐ選択() | 現在の日付と時刻を取得する |
SELECT LOCALTIME() | 現地時間を取得する |
SELECT SYSDATE() | システム時刻を取得する |
システム | 説明 |
---|---|
SELECT SYSTEM_USER( ) | システムの現在のユーザーを表示します |
ユーザーを選択( ) | システムの現在のユーザーを表示します |
バージョンを選択( ) | システムバージョン |
4.7.2 集計関数(よく使われる関数)
関数名 | 説明 |
---|---|
カウント( ) | カウント |
SUM( ) | |
平均( ) | 平均値 |
マックス( ) | |
最小( ) |
SELECT COUNT(`studentname`) FROM `student` --count(指定列)
SELECT COUNT(*) FROM `student`
SELECT COUNT(1) FROM `student`
5.事務
/*关闭事务自动提交!*/
SET autocommit=0
--手动处理事务
--事务开启
START TRANSACTION --标记一个事务的开始!从此之后的sql都在同一个事务内,
INSERT XXXX
INSERT XXX
--提交:持久化
COMMIT
--回滚:失败的时候,回滚
ROLLBACK
--事务结束
SET autocommit=1
SAVEPOINT --设置一个事务的保存点!
ROLLBACK TO SAVEPOINT --回滚到保存点!
RELEASE SAVEPOINT --撤销、删除保存点
シミュレーションシーン
SET autocommit=0; --关闭自动提交
START TRANSACTION
UPDATE `account` SET `money`=`money`-500 WHERE `name`='A'
UPDATE `account` SET `money`=`money`+500 WHERE `name`='B'
COMMIT --提交
ROLLBACK;
SET autocommit=1;--恢复自动提交!
6. 索引
インデックスの正式な定義は次のとおりです:インデックスは MySQL が効率的にデータを取得するのに役立つデータ構造であり、文のバックボーンを抽出することでインデックスの本質を得ることができます。
6.1 インデックスの分類
在一个表中,主键索引只能有一个,唯一索引可以有多个!
- 主键索引:PRIMARY KEY
- 唯一标识,主键不可重复,只能有一个字段被列为主键
- 唯一索引:UNIQUE KEY
- 避免重复的列出现,可以重复,多个列都能被标为唯一索引
- 常规索引:KEY/INDEX
- 默认的,index或者key关键字来设置
- 全文索引:FullText
- 在特定的数据库引擎下采用
- 快速定位数据
基础语法
--索引的使用
--1.创建表的时候,给字段增加索引
--2.创建完毕后,增加索引
--显示所有的索引信息:
SHOW INDEX FROM `student`
--增加索引 索引名(`列名`)
ALTER TABLE `student` ADD FULLTEXT INDEX `studentname`(`studentname`);
--分析SQL执行的状况
EXPLAIN SELECT * FROM `student`;--常规索引!
EXPLAIN SELECT * FROM `student` WHERE MATCH(`studentname`) AGAINST ('李');
6.2测试索引
-- 插入100万数据.
delimiter $$
set global log_bin_trust_function_creators=TRUE;
-- 写函数之前必须要写,作为标志
CREATE FUNCTION mock_data2 ()
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`)VALUES(CONCAT('用户',i),'[email protected]',CONCAT('13',FLOOR(RAND()*(999999999-100000000)+100000000)),FLOOR(RAND()*2),123456);
SET i=i+1;
END WHILE;
RETURN i;
END;
SELECT mock_data2() -- 执行此函数 生成一百万条数据
SELECT * FROM `app_user` WHERE `name`='用户9999' --耗时:0.516
EXPLAIN SELECT * FROM `app_user` WHERE `name`='用户9999'
SELECT * FROM `student` WHERE `name`='用户9999' --耗时0.000
--添加索引:
--id_表名_字段名
--CREATE [ FULL TEXT ]INDEX 索引名 ON `表`(`字段`)
CREATE INDEX id_app_user_name ON `app_user`(`name`);
SELECT * FROM `app_user` WHERE `name`='用户777' --创建索引之后,耗时:0.047
EXPLAIN SELECT * FROM `app_user` WHERE `name`='用户777'
索引在小数据量的时候用处不大,但是在大数据量的时候,测试效果明显!
6.3索引原则
-
索引不是越多越好
-
不要对经常变动的数据加索引
-
小数据量的表,不需要加索引
-
索引一般加在经常用来查询的字段上
索引的数据结构:
-
hash类型的索引:
-
Btree:INNODB默认的数据结构
7.权限管理
7.1用户管理
SQL命令操作:
用户表:mysql.user
本质:对这张表进行增删改查
--创建用户 CREATE user 用户名 IDENTIFIED BY 密码
CREATE user chenyuqing IDENTIFIED BY '123456';
--修改当前用户密码
SET PASSWORD=PASSWORD('111111')
--修改指定用户密码
SET PASSWORD FOR chenyuqing=PASSWORD('111111')
--给用户重命名
RENAME user chenyuqing TO CYQ;
--用户授权 ALL PRIVILEGES 库.表
--ALL PRIVILEGES 除了给别人授权,其他的都可以做!
GRANT ALL PRIVILEGES ON *.* TO CYQ;
--查看权限
SHOW GRANTS FOR CYQ;
SHOW GRANTS FOR root@localhost; --GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
--撤销权限 REVOKE 哪些权限,在哪个库撤销,给谁撤销
REVOKE ALL PRIVILEGES ON *.* FROM CYQ;
--删除用户
DROP user CYQ;
7.2数据库备份
为什么要备份?
- 保证重要的数据不丢失
- 方便数据转移
MySQL数据库备份的方式:
-
直接拷贝物理文件
-
在可视化工具中,手动导出
- 右键选择"备份/导出"
-
使用命令行:mysqldump
#导出表:mysqldump -h主机 -u用户名 -p密码 数据库 表 >物理磁盘位置:/文件名 mysqldump -hlocalhost -uroot -p123456 school student >D:/a.sql #导出库 mysqldump -h主机 -u用户名 -p密码 数据库 >物理磁盘位置:/文件名 mysqldump -hlocalhost -uroot -p123456 school >D:/a.sql #导入 #登录的情况下,切换到指定的数据库 #source 备份文件 source d:/a.sql
假设你要备份数据库,防止数据丢失。
把数据库给朋友!sql文件给别人即可。
8.数据库设计
8.1.为什么需要设计
当数据库比较复杂的时候,需要设计
糟糕的数据库设计
- 数据冗余,浪费空间
- 数据 插入/删除 都比较麻烦,而且会产生异常【屏蔽使用物理外键】
- 程序的性能差
良好的数据库设计
-
节省内存空间
-
保证数据的完整性
-
方便开发系统
软件开发中,关于数据库的设计:
- 分析需求:分析业务和需要处理的数据需求
- 概要设计:设计关系图E-R图(流程图)
设计数据库的步骤:
-
收集信息,分析需求
- 用户表(用户登录/注销,用户的个人信息,写博客,创建分类)
- 分类表(文章分类,谁创建的)
- 文章表(文章信息)
- 友链表(友链信息)
- 评论表
- 自定义表(系统信息,某个关键的字,或者一些字段)
- 说说表(发表心情,谁发表的,创建时间,…)
-
标识实体(把需求落地到每个字段)
-
表示实体之间的关系
-
user:写博客 blog
-
user:创建分类 catagory
…
-
antdesign - 设计的很好看!
8.2.三大范式(了解)
为什么需要数据规范化?
- 信息重复
- 更新异常
- 插入异常
- 无法正常显示信息
- 删除异常
- 比如:文章被删除了,但是标签还在
第一范式
原子性:保证每一列不可再分
第二范式
前提:满足第一范式
每张表只描述一件事情
第三范式
前提:满足第一范式和第二范式
确保数据表中的每一列数据都和主键直接相关,而不能间接相关
规范数据库的设计!
规范性和性能的问题!
阿里:关联查询的表,不得超过三张表
- 考虑-成本、用户体验
- 商业化的时候,数据库的性能更加重要
- 商业化的时候,适当的考虑规范性;有时会故意的增加冗余字段,以便从多表查询变成单表查询
- 估计增加计算列:从大数据量,减为小数据量的查询 (或者增加索引!)
9.JDBC(重点)
9.1.数据库驱动
驱动:声卡、显卡、数据库
我们的程序,通过一个数据库驱动,和数据库打交道!
9.2.JDBC
JDBC:java database connection
SUN公司,为了简化开发人员对数据库的统一操作,提供了一套统一(java操纵数据库)的规范,俗称JDBC
这些规范的实现,由具体的厂商去做。
对于开发者来说,只需要掌握JDBC接口的操作,即可
java.sql
javax.sql
还需要导入一个数据库驱动包:mysql-connector-java-5.1.47.jar
9.3.第一个JDBC程序
创建测试数据库
--创建测试jdbc
CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;
USE jdbcStudy;
CREATE TABLE `users`(
id INT PRIMARY KEY,
NAME VARCHAR(40),
PASSWORD VARCHAR(40),
email VARCHAR(60),
birthday DATE
);
INSERT INTO `users`(id,NAME,PASSWORD,email,birthday) VALUES
(1,'zhansan','123456','[email protected]','1980-12-04'),
(2,'lisi','123456','[email protected]','1981-12-04'),
(3,'wangwu','123456','[email protected]','1979-12-04')
1.创建一个普通项目:
2.导入数据库驱动:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PNaTzMUS-1690205301628)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201216172936882.png)]
3.编写测试代码:
//一个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的对象 去 执行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("PASSWORD:"+resultSet.getObject("PASSWORD"));
System.out.println("email:"+resultSet.getObject("email"));
System.out.println("birthday:"+resultSet.getObject("birthday"));
}
//6.释放连接
resultSet.close();
statement.close();
connection.close();
}
}
步骤:
1.加载驱动
2.连接数据库 connection(相当于 - 登录 - )
3.获取执行sql的对象 statement (java中万物皆对象,)
4.获取返回的结果集 resultSet
5.释放连接
DriverManager
//DriverManage.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动
Connection connection = DriverManager.getConnection(url,username,password);
//connection代表数据库
//数据库设置自动提交
//事务提交
//事务回滚
URL
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSl=true";
//mysql默认3306
//协议:mysql://主机地址:端口号/数据库名?参数1&参数2&参数3
//oracle默认1521
//jdbc:oracle:thin:@localhost:1521:sid
statement
Statement statement = connection.createStatement();//执行sql的对象
//编写sql
String sql="SELECT * FROM `users`";
statement.executeQuery(sql);//返回结果集,结果集中封装了我们全部的查询出来的结果
statement.execute(sql);//执行任何的操作
statement.executeUpdate(sql);//更新/插入/删除 都使用,返回受影响的行数
resultSet
查询query 的结果集,封装了全部的查询结果。
//获得指定的数据类型:
resultSet.getObject();//不知道列类型的情况下使用,如果知道,可以使用指定的数据类型
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getDate();
遍历:
resultSet.next();//移动到下一个数据
resultSet.previous();//移动到前一行
resultSet.beforeFirst();//移动到最前面
resultSet.afterLast();//移动到最后面
resultSet.absolute(row);//移动到指定行
释放资源必须做
resultSet.close();
statement.close();
connection.close();
9.4.statement对象
jdbc中的statement对象用于向数据库发送SQL语句,像完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可。
CURD操作 - create
使用statement.executeUpdate(sql);方法完成数据添加操作,示例操作:
Statement st=conn.createStatement();
String sql = "insert into user(...) values(...)";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("插入成功!");
}
CURD操作 - delete
使用statement.executeUpdate(sql);方法完成数据删除操作,示例操作:
Statement st=conn.createStatement();
String sql = "delete from user(...) where xxx";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("删除成功!");
}
CURD操作 - update
使用statement.executeUpdate(sql);方法完成数据修改操作,示例操作:
Statement st=conn.createStatement();
String sql = "UPDATE `users` SET `NAME`='fushan' WHERE `id`=2 ";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("插入成功!");
}
CURD操作 - read
使用statement.executeQuery(sql);方法完成数据查询操作,示例操作:
Statement st=conn.createStatement();
String sql = "select from user(...) where XXX";
ResultSet rs = st.executeQuery(sql);
while(rs.next()){
//根据获取到的列的数据类型,分别调用rs的相应方法映射到java对象中
//例如:rs.getObject("id")
}
9.5.代码实现
5.1提取工具类
package com.cyq.lesson02.util;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JdbcUtil {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
static {
try {
InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(in);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
// 驱动只用加载一次
Class.forName(driver);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
// 获取连接:
public static Connection getConnector() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
// 释放连接资源:
public static void release(Connection conn, Statement st, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (st != null) {
try {
st.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
5.2编写增删改的方法
均使用:executeUpdate(sql);
下面例子使用的:修改!!!
package com.cyq.lesson02;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import com.cyq.lesson02.util.JdbcUtil;
public class TestUpdate {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
//1.获取数据库连接
conn = JdbcUtil.getConnector();
//2.获得sql的执行对象
st = conn.createStatement();
//3.编写sql
String sql = "UPDATE `users` SET `NAME`='fushan',`email`='[email protected]' WHERE `id`=2";
//4.执行sql
int i = st.executeUpdate(sql);
if (i>0) {
System.out.println("修改成功!");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
JdbcUtil.release(conn, st, rs);
}
}
}
5.3编写查询的方法
通过execteQuery(sql);
package com.cyq.lesson02;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import com.cyq.lesson02.util.JdbcUtil;
public class TestSelect {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
// 1.获取数据库连接
conn = JdbcUtil.getConnector();
// 2.获得sql的执行对象
st = conn.createStatement();
// 3.编写sql
String sql = "SELECT * FROM `users` WHERE `id`=2";//一个空格都不能多!
// 4.执行sql
rs = st.executeQuery(sql);
while (rs.next()) {
System.out.print(rs.getString("name"));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
JdbcUtil.release(conn, st, rs);
}
}
}
5.4 SQL注入问题
sql存在漏洞,踩在攻击导致数据泄露,SQL会被拼接
5.5 PreparedStatement
PreparedStatement可以防止SQL注入,而且效率更高
新增/删除/修改 都使用update
本次举例使用:新增
package com.cyq.lesson03;
import java.sql.Connection;
import java.util.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.cyq.lesson02.util.JdbcUtil;
public class TestInsert {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnector();
//区别:
//1.使用问好占位符,代替参数!
String sqlString = "INSERT INTO `users`(`id`,`NAME`,`PASSWORD`,`email`,`birthday`) VALUES(?,?,?,?,?)";
st = conn.prepareStatement(sqlString);//预编译SQL,然后不执行
//手动给参数赋值
st.setInt(1, 1);
st.setString(2, "jinzixin");
st.setString(3,"123456");
st.setString(4, "[email protected]");
//注意:sql.Date java.sql.Date()
// util.Date new Date().getTime()-获得时间戳
st.setDate(5, new java.sql.Date(new Date().getTime()));
//执行
int i = st.executeUpdate();
if (i>0) {
System.out.println("插入成功!");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
JdbcUtil.release(conn, st, rs);
}
}
}
查询
package com.cyq.lesson03;
import java.sql.Connection;
import java.util.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.cyq.lesson02.util.JdbcUtil;
public class TestSelect {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnector();
// 区别:
// 1.使用问好占位符,代替参数!
String sqlString = "SELECT * FROM `users` WHERE `id`=? ";
//PreparedStatement防止SQL注入的本质是,把传递进来的参数当作字符!
//数据其中存在转义字符,直接忽略掉。""会被直接转义!
st = conn.prepareStatement(sqlString);// 预编译SQL,然后不执行
// 手动给参数赋值
st.setInt(1, 2);
// 执行
rs = st.executeQuery();
while (rs.next()) {
System.out.println(rs.getString("name"));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
JdbcUtil.release(conn, st, rs);
}
}
}
9.6.使用Eclipse连接数据库
Windows - showView - Others - dataManagagement - dataSourceExplorer
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PlmFUgl1-1690205301628)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201217142114451.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n8HSXUYU-1690205301629)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201217150910621.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xdYJle1r-1690205301630)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201217150758186.png)]
9.7.事务
要么都成功,要么都失败!
ACID:原子性、一致性、隔离性、持久性
原子性:要么全部完成,要么都不完成
一致性:总数不变
隔离性:多个进程互不干扰
持久性:一旦提交不可逆,持久化到数据库!
隔离性的问题:
- 脏读:一个事务读取了另一个没有提交的事务!
- 不可重复读:在同一个事务内,重复读取表中的数据,表数据发生了改变
- 幻读/虚读:在一个事务内,读取到别人插入的数据,导致前后读取出来的数据不一致!
代码实现:
package com.cyq.lesson04;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.cyq.lesson02.util.JdbcUtil;
public class TestTransaction {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;// 防止sql注入!
// ResultSet re = null;
try {
conn = JdbcUtil.getConnector();
// 1.关闭数据库的自动提交功能;会自动开启事务!
conn.setAutoCommit(false);
// 2.执行sql
String sql1 = "update `account` set `money`=`money`-100 where `name`='A'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
String sql2 = "update `account` set `money`=`money`+100 where `name`='B'";
st = conn.prepareStatement(sql2);
st.executeUpdate();
// 3.业务完毕,提交事务:
conn.commit();
System.out.println("操作成功!");
} catch (SQLException e) {
// TODO Auto-generated catch block
try {
conn.rollback();// 失败则回滚事务!
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
} finally {
JdbcUtil.release(conn, st, null);
}
}
}
步骤:
1.开启事务
2.事务执行完毕,提交事务;可以在Catch语句中显示的定义回滚语句,不定义也可,若失败,默认回滚。
9.8.数据库连接池
数据库连接 – 执行完毕 – 释放 由连接到释放十分浪费系统资源!
池化技术
准备一些预先的资源,过来就连接预先准备好的
– 开门 – 服务 – 关门 –
– 开门 – 业务员 --等待 --服务 --关门
常用连接数 100 个左右!
最小连接数:就设置100个左右!(根据常用连接数设置)
最大连接数: 业务最高承载上限;
待处理业务数大于最大连接数,排队等待!
等待超时:超过特定时间,抛出异常,(相当于让客户走人,别等了,浪费时间!)
编写连接池:实现一个接口DataSource
- 开源数据源实现
- DBCP
- C3P0
- Druid:阿里巴巴(德鲁伊)
使用了这些数据库连接池之后,我们在项目开发中,就不需要编写连接数据库的代码了!
-
DBCP
-
需要用到的jar包:commons-dbcp-1.4.jar commons-pool-1.6.jar
-
拿来即用,即可!
-
-
C3P0
- 需要用到的jar包:mchange-commons-java-0.2.19.jar c3p0-0.9.5.5.jar
结论
无论使用什么数据源。本质都是一样的,DataSource接口不会变,方法不会变!