Mysql详解笔记

文章目录

  • Mysql
    • **1、什么要学习数据库**
    • **2、什么是数据库**
    • **3、数据库分类**
      • **关系型数据库(SQL)**
      • **非关系型数据库(NoSQL(NotOnly))**
    • **4、BMS(数据库管理系统)**
      • **MySQL简介**
    • **5、数据库的列类型**
      • **数值**
      • **字符串**
      • **时间日期**
      • **null**
    • **6、数据库的字段属性(重点)**
      • **Unsigned**
      • **zerofill**
      • **自增**
      • **非空NULL not null**
      • **默认**
    • **7、建数据库**
    • **8、关于数据库的引擎**
    • **9、MySQL数据管理**
      • **外键(了解)**
      • **DML语言(全部记住)**
        • **insert语句**
        • **update修改**
        • **delete/Truncate删除**
          • delete和truncate区别
    • **10、联表查询JoinOn**
    • **11、分页**
    • **12、MySQL常用函数**
    • **13、聚合函数(常用)**
    • **14、数据库级别的MD5加密**
      • **什么是MD5?**
      • **MD5()**
    • **15、事务**
      • **原子性(Atomicity)**
      • **一致性(Consistency)**
      • **持久性(Durability)**
      • **隔离性(Isolation)**
      • **脏读:**
      • **不可重复读:**
      • **虚读(幻读)**
    • **16、索引**
      • **索引的分类**
      • **测试索引**
      • **索引原则**
    • **17、权限管理和备份**
      • **用户管理**
        • **SQL命令操作**
    • **18、MySQL备份**
      • **导出:**
      • **导入:**
    • **19、规范数据库设计**
      • **为什么需要设计**
      • **三大范式**
    • **20、JDBC**
      • **第一个JDBC程序**
      • **SQL注入问题**
      • **PreparedStatement对象**
    • **21、数据库连接池**

Mysql

JavaEE:企业级java开发 Web

前端(页面:展示,数据!)

后台(连接点:连接数据库JDBC,连接前端(控制视图跳转和给前端传递数据))

数据库(存数据,Txt,Excel,Word)

1、什么要学习数据库

  1. 岗位需求
  2. 现在的世界,大数据时代~,得数据者得天下。
  3. 被迫需求:存数据
  4. 数据库是所有软件体系中最核心得存在 DBA

2、什么是数据库

数据库(DB,DataBase)

概念:数据仓库,软件,安装在操作系统之上!SQL,可以存储大量的数据

作用:存储数据,管理数据

3、数据库分类

关系型数据库(SQL)

  • Mysql
  • Oracle
  • SqlServer
  • DB2
  • SQLlite

通过表和表之间,行和列之间的关系进行数据的存储。学员信息表,考勤表,···

非关系型数据库(NoSQL(NotOnly))

  • Redis
  • MongDB

非关系型数据库,对象存储,通过对象自身的属性来决定。


4、BMS(数据库管理系统)

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

MySQL简介

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

前世:瑞典MySQLAB公司

今生:属于Oracle旗下产品

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

体积小、速度快、总体拥有成本低

中小型网站、或者大型网站,集群!

官方:https://www.mysql.com

官网下载地址:https://dev.mysql.com/downloads/mysql/

安装建议:

  1. 尽量不要使用exe,因为卸载麻烦,它会往注册表里走
  2. 尽可能使用压缩包安装~

5、数据库的列类型

数值

  • tinyint 十分小的数据 1个字节
  • smalint 较小的数据 2个字节
  • mediumint 中等大小的数据 3个字节
  • int 标准的整数 4个字节 常用的int
  • bigint 较大的数据 8个字节
  • float 浮点数 4个字节
  • double 浮点数 8个字节(精度问题)
  • decimal 字符串形式的浮点数 金融计算的时候,一般是使用decimal

字符串

  • char 字符串固定大小的 0~255
  • varchar 可变字符串 0~65535 常用的String
  • tinytext 微型为本 2^8-1
  • text 文本串 2^16-1 保存大文本

时间日期

  • data YYYY-MM-DD,日期格式
  • time HH:mm:ss,时间格式
  • datetime YYYY-MM-DD HH:mm:ss 最常用的时间格式
  • timestamp 时间戳,1970,1,1到现在的毫秒数! 也比较常用
  • year 年份表示

null

  • 没有值,未知
  • 注意,不要使用NULL进行运算,结果为NULL

6、数据库的字段属性(重点)

Unsigned

  • 无符号的整数
  • 声明了该列不能声明为负数

zerofill

  • 0填充的
  • 不足的位数,使用0来填充,int(3),5----005

自增

  • 通常理解为自增,自动在上一条记录的基础上+1(默认)
  • 通常用来设计唯一的主键~index,必须是整数类型
  • 可以自定义设计主键自增的起始值和步长

非空NULL not null

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

默认

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

7、建数据库

create database kuangshenMySQL character set 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 '邮箱',
	primary key(`id`)
)Engine =innodb DEFAULT CHARSET=utf8;

常用命令

show create database kuangshenMySQL -- 查看创建数据库的语句
show create table student -- 查看student数据表的定义语句
desc student -- 显示表的结构

8、关于数据库的引擎

INNODB,MYISAM早些年使用

MYISAM **INNODB
事务支持 不支持 支持
数据行锁定 不支持 支持
外键约定 不支持 支持
全文索引 支持 不支持
表空间的大小 较小 较大,约为MYSAM两倍

常规使用操作:

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

在物理空间存在的位置

所有的数据库文件都存在data目录下!,一个文件夹对应一个数据库

本质还是文件的存储!

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

  • InnoDB在数据库表中只有一个*.frm文件,以及上级目录下的ibdata1文件
  • MYISAM对应文件
    • *.frm -表结构的定义文件
    • *.MYD 数据文件(data)
    • *.MYI 索引文件(index)

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

charset=utf8

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

MySQL的默认编码是Latin1,不支持中文

在my.ini中配置默认编码

character-set-server=utf8

9、MySQL数据管理

外键(了解)

方式一:在创建表的时候,增加约束

create table `grade`(
	`gradeId` int(10) not null auto_increment comment '年纪Id',
	`gradeName` varchar(50) not null comment '年级名称',
	primary key(`gradeId`)
)engine=innodb default charset=utf8;

create table if not exists `student`(
	`id`	int(4) not null auto_increment comment '学号',
	`name` varchar(30) not null default '匿名' comment '姓名',
	`pwd` varchar(20) not null default '123456' comment '密码',
	`sex` varchar (2) not null default '女' comment '性别',
	`birthday` datetime default null comment '出生日期',
	`gradeId` int(10) not null comment '学生的年级',
	`address` varchar(100) default null comment '家庭地址',
	`email` varchar(50) default null comment '邮箱',
	primary key(`id`),
	-- 学生表的gradeId字段 要去引用年级表的 gradeId
	-- 定义外键key
	-- 给这个外键添加约束(执行引用)
	key `FK_gradeId`(`gradeId`),
	constraint foreign key (`gradeId`) references `grade`(`gradeId`)
)Engine =innodb DEFAULT CHARSET=utf8;

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

方式二:创建表成功后,添加外键约束

create table if not exists `student`(
	`id`	int(4) not null auto_increment comment '学号',
	`name` varchar(30) not null default '匿名' comment '姓名',
	`pwd` varchar(20) not null default '123456' comment '密码',
	`sex` varchar (2) not null default '女' comment '性别',
	`birthday` datetime default null comment '出生日期',
	`gradeId` int(10) not null comment '学生的年级',
	`address` varchar(100) default null comment '家庭地址',
	`email` varchar(50) default null comment '邮箱',
	primary key(`id`)
)Engine =innodb DEFAULT CHARSET=utf8;

-- 创建表的时候没有外键
alter table `student`
add constraint `FK_gradeId` FOREIGN KEY(`gradeId`) references `grade`(`gradeId`);
-- alter table 表 add constraint `约束名` FOREIGN KEY (`作为外键的字段`)  references `哪个表`(`哪个字段`)

以上操作都是物理外键,数据库级别的外键,我们不建议使用!(避免数据过多造成困扰,了解就好)

最佳实践

  • 数据库就是单纯的表,只用来存数据,只有行(数据)和列(字段)
  • 我们想使用多张表的数据, 想使用外键(程序去实现)

DML语言(全部记住)

insert语句

INSERT INTO `student`(`name`) VALUES('张三');
INSERT INTO `student`(`name`,`pwd`,`sex`) VALUES('张三','aaaaa','男');
INSERT INTO `student`(`name`,`pwd`,`sex`) 
VALUES('张三','aaaaa','男'),('李四','aaaaa','男');

语法:insert into `表名` (`字段名1`,`字段名2`) VALUES (‘值1’,‘值2’), (‘值1’,‘值2’)…;

注意事项:

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

update修改

update `student` set name='Can同学' where name = '张三' AND sex = '男'

语法:UPDATE `表名` set colnum_name = value,[colnum_name=value,…] where [条件]

注意事项:

  1. colnum_name是数据库的列,尽量带上
  2. 条件,筛选的条件,如果没有指定,则会修改所有的列
  3. value,是一个具体的值,也可以是一个变量
  4. 多个设置的属性之间,使用英文逗号隔开

delete/Truncate删除

delete命令

-- 删除数据,如果没有条件where则会删除全部数据
delete from `student` where id = 1;

语法:delete from 表名 where 条件

truncate命令

作用:完全清空


一个数据库表,表的结构和索引约束不会变!

-- 清空 student表
truncate `student`
delete和truncate区别

相同点

  • 都能删除数据,都不会删除表结构

不同点

  • truncate 重新设置自增列,计数器会归零
  • truncate 不会影响事务

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

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

10、联表查询JoinOn

操作 描述
inner join 如果表中至少有一个匹配,就返回行,只返回匹配的行
left join 会从左表中返回所有的值,即使右边中没有匹配
right join 会从右表中返回所有的值,即使左边中没有匹配

11、分页

-- 第一页 limit 0,5	(1-1)*5
-- 第二页 limit 5,5	(2-1)*5
-- 第三页 limit 10,5	(3-1)*5
-- 第N页 limit 0,5	(n-1)*pageSize
-- 【pageSize:页面大小】
-- 【(n-1)*pageSize:起始值】
-- 【n:当前页】
-- 【数据总数/页面大小 = 总页数】

语法:limit(查询起始下标,pageSize)

12、MySQL常用函数

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

-- 字符串函数
SELECT CHAR_LENGTH('即使再小的帆也能远航'); -- 字符串长度
SELECT CONCAT('哈','撒','给'); -- 拼接字符串
SELECT INSERT('我爱编程helloworld',2,4,'嗯'); -- 查询,从某个位置开始替换某个长度,替换下标到哪个下标
SELECT LOWER('Can'); -- 转成小写字母
SELECT UPPER('Can'); -- 转成大写字母
SELECT INSTR('Ccan','c'); -- 返回第一次出现的字串的索引,不区分大小写
SELECT REPLACE('明天还要继续','继续','看'); -- 替换出现的指定字符串
SELECT SUBSTR('明天还要继续哈哈',4,5); -- 返回指定的字符串,(源字符串,截取的位置,截取的长度)
SELECT REVERSE('明天还要继续哈'); -- 反转 哈续继要还天明

-- 时间和日期函数(记住)
SELECT CURRENT_DATE();  -- 获取当前日期 2020-04-21
SELECT CURDATE(); -- 获取当前日期 2020-04-21
SELECT NOW(); -- 获取当前的时间 2020-04-21 22:16:59
SELECT LOCALTIME(); -- 本地时间 2020-04-21 22:17:29
SELECT SYSDATE(); -- 系统时间 2020-04-21 22:17:45
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();

13、聚合函数(常用)

函数名称 描述
COUNT() 计数
SUM() 求和
AVG() 平均值
MAX() 最大值
MIN() 最小值
-- =============聚合函数=============
-- 都能够统计 表中的数据
-- 列名为主键时,count(列名)比count(1)快,否则count(1)快
-- 如果表多个列并且没有主键,则count(1)的执行效率优于count(*)
-- 如果有主键。则count(主键)的执行效率最优的
-- 如果表只有一个字段,则count(*)最优
SELECT COUNT(birthday) from student; -- count(字段),会忽略所有的null值
SELECT COUNT(*) from student; -- count(*),不会忽略Null值,本质 计算行数
SELECT COUNT(1) from student; -- count(1),不会忽略Null值

SELECT SUM(pwd) as 总和 from student;
SELECT AVG(pwd) as 平均分 from student;
SELECT MAX(pwd) as 最高分 from student;
SELECT MIN(pwd) as 最低分 from student;

14、数据库级别的MD5加密

什么是MD5?

  • 主要增强算法复杂度和不可逆性。
  • MD5不可逆,具体的值的md5是一样的
  • MD5破解网站的原理,背后有一个字典,MD5加密后的值,加密前的值

MD5()

create table `testmd5`(
	`id` int(4) not null,
	`name` varchar(20) not null,
	`pwd` varchar(50) not null,
	primary key(`id`)
)engine=innodb default charset=utf8;

-- 明文密码
insert into testmd5 values(1,'张三','123456'),(2,'李四','123456'),(3,'张里','123456');

-- 加密
UPDATE testmd5 set pwd = MD5(pwd) where id=1;

UPDATE testmd5 set pwd = MD5(pwd);  -- 加密全部的密码

-- 插入的时候加密
insert into testmd5 values (4,'小明',MD5('123456'));

-- 如何校验:将用户传递进来的密码,进行MD5加密,然后对比加密后的值。
select * from testmd5 where `name` = '小明' AND PWD =MD5(MD5('123456'));

15、事务

事务原则:ACID原则 原子性,一致性,隔离性,持久性 (脏读,幻读。。。)

原子性(Atomicity)

  • 要么都成功,要么都失败

一致性(Consistency)

  • 事务前后的数据完整性要保证一致

持久性(Durability)

  • 事务一旦提交则不可逆,被持久化到数据库中.

隔离性(Isolation)

  • 在并发情况下,多个事务访问同一个资源时,事务之间相互不影响。

隔离所导致的一些问题

脏读:

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

不可重复读:

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

虚读(幻读)

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

16、索引

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

索引的分类

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

  • 主键索引(PRIMARY KEY)
    • 唯一的标识,主键不可重复,只能有一个列作为主键
  • 唯一索引(UNIQUE KEY)
    • 避免重复的列出现,唯一索引可以重复,多个列都可以标识为 唯一索引
  • 常规索引(KEY/INDEX)
    • 默认的,index。key关键字来设置
  • 全文索引(FullText)
    • 在特定的数据库引擎下才有,MYISAM
    • 快速定位数据
create table if not exists `student_index`(
	`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(`email`)
)Engine =MYISAM DEFAULT CHARSET=utf8;

insert into student_index values(null,'谢3','123','男',NOW(),3,'address','@qq'),(null,'谢4','123','男',NOW(),4,'address','@qq'),(null,'谢5','123','男',NOW(),5,'address','@qq'),(null,'谢6','123','男',NOW(),6,'address','@qq');

-- 显示所有的索引信息
show index from student_index;

-- 增加一个全文索引(索引名)
alter table kuangshenmysql.student_index add fulltext index `studentName`(`name`);

-- EXPLAIN 分析sql执行的状况
EXPLAIN SELECT * FROM student_index; -- 非全文索引

EXPLAIN SELECT * FROM student_index WHERE MATCH(`name`) AGAINST('谢');

测试索引

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 SYSDATE(),
-- 	`update_time` BIGINT(10) default unix_timestamp(now()),
	primary key (`id`)
)ENGINE=INNODB default charset=utf8mb4 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),'[email protected]',
CONCAT('13',FLOOR(RAND()*((9999999999-100000000)+100000000))),FLOOR(RAND()*2),UUID(),FLOOR(RAND()*100));
		SET i = i+1;
	END WHILE;
	RETURN i;
END;
-- 执行方法
-- 栈不够的问题 打开.ini 改配置thread_stack = 128K#改为512K
select mock_data();
image-20200422011611460

1001506才找到它,很慢

加上索引

-- 命名方式:id_表名_字段名
-- CREATE INDEX 索引名 on 表(字段)
CREATE INDEX id_app_user_name ON app_user(`name`);
-- 明显变快了 
select * from APP_USER WHERE `name` = '用户99999'; -- 0.001s
image-20200422012035182

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

索引原则

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

索引的数据结构

image-20200422012524800

Hash类型的索引

Btree:InnoDB的默认数据结构(B树)


17、权限管理和备份

用户管理

SQL命令操作

用户表:mysql.user

本质:对这张表进行增删改查

select * from mysql.user;

-- 创建用户
CREATE USER can identified by '123456';

-- 修改密码(修改当前用户密码)
SET PASSWORD = PASSWORD('root')

-- 修改密码(修改指定用户密码)
SET PASSWORD FOR can = PASSWORD('root')

-- 重命名 rename user 原来的名 to 新的名
rename user can to can2;

-- 用户授权 ALL PRIVILEGES 全部的权限,库.表
-- ALL PRIVILEGES 除了给别人授权,其他都能干
GRANT ALL privileges ON *.* to can2;

-- 查看权限
SHOW grants for can2; -- 查看指定用户的权限

-- 查看root权限 要加上@主机
show grants for root@localhost;

-- 撤销权限 REVOKE 哪些权限,在哪个库撤销,给谁撤销
REVOKE ALL PRIVILEGES ON *.* from can2;

-- 删除用户
DROP USER can2;

18、MySQL备份

为什么要备份:

  • 保证重要的数据不丢失
  • 数据转移A—>B

MySQL备份的方式

  • 直接拷贝物理文件(data文件夹)
  • 在可视化工具(Navicat,Sqlyog)上手动导出
  • 使用命令行导出 mysqldump 命令行使用

导出:

#命令行mysqldump导出SQL
#mysqldump -h主机 -u用户名 -p密码 数据库 表名 > 物理磁盘位置/文件名
mysqldump -hlocalhost -uroot -proot kuangshenmysql student > D:/a.sql

#导出多张表
#mysqldump -h主机 -u用户名 -p密码 数据库 表名1 表名2  > 物理磁盘位置/文件名
# 注意这里-B `B`必须是大写,代表导出有建库的语句
mysqldump -hlocalhost -uroot -proot -B kuangshenmysql student grade > D:/b.sql

#导出数据库所有数据
#mysqldump -h主机 -u用户名 -p密码 数据库 > 物理磁盘位置/文件名
mysqldump -hlocalhost -uroot -proot -B kuangshenmysql > D:/b.sql

导入:

# 登录的情况下,如果导入表,切换到指定的数据库,如果导入数据库的话就没必要切换了
# source 备份文件
source d:/a.sql

# 没有登录的情况,不建议用这个
#导入表
mysql -u用户名 -p密码 库名< 备份文件
#导入库
mysql -u用户名 -p密码 <备份文件

假设你要备份数据库,防止数据丢失。

把数据库给朋友!sql文件给别人即可!


19、规范数据库设计

为什么需要设计

当数据库比较复杂的时候,我们就需要设计了

糟糕的数据设计:

  • 数据冗余,浪费空间
  • 数据插入和删除都会麻烦、异常【屏蔽使用物理外键】
  • 程序的性能差

良好的数据库设计:

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

软件开发中关于数据库的设计

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

设计数据库的步骤:(个人博客)

  • 收集信息,分析需求

    • 用户表(用户登录注销,用户的个人信息,写博客,创建分类)
    • 分类表(文章分类,谁创建的)
    • 文章表(文章的信息)
    • 友链表(友联信息,友情链接)
    • 自定义表(系统信息,某个关键的字,或者一些主字段)
  • 标识实体(把需求落地到每个字段)

    • 写博客:user–>blog
    • 创建分类:user–>category
    • 关注:user–>user
    • 友联:links
    • 评论:user-user-blog

三大范式

为什么需要数据规范化?

  • 信息重复
  • 更新异常
  • 插入异常
    • 无法正常显示信息
  • 删除异常
    • 丢失有效的信息

三大范式

第一范式(1NF)

原子性:保证每一列不可再分

image-20200422171738180

第二范式(2NF)

前提:满足第一范式

每张表只描述一件事情

image-20200422172137218

第三范式(3NF)
前提:满足第一范式和第二范式

第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。

image-20200422172336905

(规范数据库的设计)

规范性 和 性能的问题

阿里的规范:关联查询的表不能超过三张表

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

20、JDBC

SUN公司为了简化开发人员(对数据库的统一)操作,提供了一个(Java操作数据库的)规范,俗称JDBC这些规范的实现由具体的厂商去做~

image-20200422173129225

驱动下载地址:https://dev.mysql.com/downloads/file/?id=489462

mysql-connector-java.jar

第一个JDBC程序

创建测试数据库

create database kuangshenjdbc character set utf8 collate utf8_general_ci;

use kuangshenjdbc;

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,'can1','123456','@qq.com','1997-03-03'),
	(2,'can2','123456','@qq.com','1997-03-03'),
	(3,'can2','123456','@qq.com','1997-03-03');
  1. idea创建项目
  2. 导入数据库驱动
image-20200422174515970

3.编写测试代码

package com.can.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,useSSL使用安全的连接
        String url = "jdbc:mysql://localhost:3306/kuangshenjdbc?useUnicode=true&characterEncoding=utf-8&useSSL=true";
        String username = "root";
        String password = "root";
        //3.连接成功,返回数据库对象
        Connection connection = DriverManager.getConnection(url,username,password);
        //默认会开启事务
        connection.setAutoCommit(false);//关闭事务
        //4.执行SQL的对象
        Statement statement = connection.createStatement();
        //5.执行SQL的对象, 去执行SQL,可能存在结果,查询返回结果
        ResultSet resultSet = statement.executeQuery("SELECT * FROM users");    //返回结果集
        PreparedStatement r1 = connection.prepareStatement("update  users set password='123' where name ='can1' ");
        int i = r1.executeUpdate();
        System.out.println(i);
        //提交事务
        connection.commit();
        //归滚
//        connection.rollback();
        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. 加载驱动Class.forName(“com.mysql.jdbc.Driver”)
  2. 连接数据库DriverManager.getConnection
  3. 获得执行SQL的对象 Statement statement = connection.createStatement();
  4. 获得返回的结果集ResultSet
  5. 释放连接

JdbcUtils

package com.can.lesson01;

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 {
            //getClassLoader获得该类的装载器
            //getResourceAsStream用给定的名查找资源
            InputStream in = JdbcUtils.class.getClass().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("com.mysql.jdbc.Driver");

        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    //获取连接
    public static Connection getConnection() 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) {
                e.printStackTrace();
            }
        }
        if (st!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

SQL注入问题

SQL存在漏洞,会被攻击导致数据泄露

SQL会被拼接 or

package com.can.lesson01;

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

public class SQL注入 {
    public static void main(String[] args) {

//        login("can1","123"); //正常登录
        login(" ' or '1=1","123' or '1=1"); // SQL注入
    }

    //登录业务
    private static void login(String username,String password){
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            st = conn.createStatement();

            //SQL
            //select * from users where name = '' or '1=1' and password='123' or '1=1';
            String sql = "select * from users where `name`='"+username+"'"+"AND "+"`password`='"+password+"'";
            rs = st.executeQuery(sql);
            while (rs.next()){
                System.out.println(rs.getObject("name"));
                System.out.println(rs.getObject("password"));
                System.out.println("============");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.释放连接
            JdbcUtils.release(conn,st,rs);
        }
    }
}

PreparedStatement对象

preparedStatement可以防止SQL注入。效果更好!

package com.can.lesson01;


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

/**
 * prepareStatement
 * 预编译 防止SQL注入
 */
public class PreparedStatementDemo {
    public static void main(String[] args) {
//        PreparedStatementDemo.login("can1","123"); //正常登录
        login(" ' or '1=1","123' or '1=1"); // SQL注入
    }

    //登录业务
    private static void login(String username,String password){
        Connection conn = null;
//        Statement st = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            //区别
            //prepareStatement 防止SQL注入的本质: 把传递过来的参数 当作字符 包裹起来 '参数'
            //假设字符中存在转义字符,就直接忽略, '会被直接转义
            String sql = "select * from kuangshenjdbc.users where `name`= ? and `password` =?";//预编译SQL 参数用 '?'
            //            st = conn.createStatement();
            st = conn.prepareStatement(sql); //预编译SQL,先写sql,然后不执行
            //手动参数赋值
            st.setString(1,username);
            st.setString(2,password);
            //注意点:sql.Date 数据库 java.sql.Date()
            //       util.Date Java  new Date().getTime() 获得时间戳
//            st.setDate(2,new java.sql.Date(new Date().getTime()));
            //执行
            rs = st.executeQuery();
            while (rs.next()){
                System.out.println(rs.getObject("name"));
                System.out.println(rs.getObject("password"));
                System.out.println("============");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.释放连接
            JdbcUtils.release(conn,st,rs);
        }
    }
}

21、数据库连接池

数据库连接–执行完毕–释放

连接–释放 十分浪费系统资源

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

----开门—业务员:等待 --服务–

常用连接数100个

最小连接数:100

最大连接数:1000 |业务最高承载上限,超过1000个人会排队等待,

等待超时:100ms |会断掉

编写连接池,只需要实现一个接口,DataSrouce

开源数据源实现(拿来即用)

DBCP

C3P0

Druid:阿里巴巴

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

DBCP

需要用到的jar包

下载地址:http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi

commons-dbcp-1.4.jar、commons-pool-1.6.jar

package com.can.lesson01.utils;


import org.apache.commons.dbcp.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class poolUtil_DBCP {

    private static DataSource dataSource =  null;
//    private static BasicDataSource dataSource =  null;

    static{
        try {
            InputStream in = poolUtil_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 conn, Statement st, ResultSet rs){
        if (conn!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (st!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

}

C3P0

需要用到的jar包

c3p0-0.9.5.5、mchange-commons-java-0.2.19

package com.can.lesson01.utils;

import com.mchange.v2.c3p0.ComboPooledDataSource;

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

public class poolUtil_C3P0 {
    private static ComboPooledDataSource dataSource =  null;
//    private static BasicDataSource dataSource =  null;

    static{
        try {
            //代码版配置
//            dataSource = new ComboPooledDataSource();
//            dataSource.setDriverClass();
//            dataSource.setUser();
//            dataSource.setPassword();
//            dataSource.setJdbcUrl();
//
//            dataSource.setMaxPoolSize();
//            dataSource.setMinPoolSize();

            //配置文件写法
            dataSource = new ComboPooledDataSource("test"); //使用test数据源
//            dataSource = new ComboPooledDataSource(); //使用默认数据源
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获取连接
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection(); //从数据源中获取连接
    }

    //释放资源
    public static void release(Connection conn, Statement st, ResultSet rs){
        if (conn!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (st!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

}

结论

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

你可能感兴趣的:(Mysql)