MySQL JavaEE: 企业级Java开发 Web
前端(页面:展示数据)
后台(连接点:连接数据库JDBC,连接前端(控制视图跳转,和给前端传递数据))
数据库(存数据,txt, excel, word… …)
初识MySQL 数据库的重要性
大数据时代,得数据者得天下
存数据
数据库是所有软件体系最核心的存在
什么是数据库 数据库(DB,DataBase)
概念:数据仓库,软件 ,安装在操作系统(Windows,Linux,Mac)之上!SQL,可以存储大量的数据。500万!
作用:存储数据,管理数据
数据库分类 关系型数据库:(SQL)
MySQL,Oracle,Sql Server, DB2, SQLlite
通过表和表之间,行和列之间的关系进行数据的存储, 学员信息表,考勤表,… …
非关系型数据库:(NoSQL)Not Only SQL
Redis/MongDB
非关系型数据库,对象存储,通过对象自身的属性来决定。
DBMS(数据库管理系统)
数据库的管理软件,科学有效地管理我们的数据。维护和获取数据;
MySQL
MySQL简介 MySQL是一个关系型数据库管理系统
以前属于瑞典MySQL AB公司,在属于Oracle旗下产品。
MySQL是最好的RDBMS(Relational DataBase Management System)应用软件之一。
开源软件。
体积小、速度快、总体拥有成本低。
中小型网站,或者大型网站,可以集群。
官网:https://www.mysql.com/
MySQL安装 安装地址:https://dev.mysql.com/downloads/mysql/
安装建议:
尽量不要使用exe安装,会牵涉到注册表
尽可能使用压缩包安装
步骤:
解压zip
添加环境变量:在path中添加bin的目录
在mysql文件夹下新建my.ini文件
1 2 3 4 5 [mysqld] basedir =D:\Program Files (x86)\mysql-8.0 .21 \datadir =D:\Program Files (x86)\mysql-8.0 .21 \data\port =3306
启动管理员模式 下的CMD,并将路径切换至mysql下的bin目录,然后输入mysqld-install(安装mysql)。
再输入mysqld –initialize-insecure –user=mysql 初始化数据文件。
然后再次启动mysql(使用net start mysql),然后用命令mysql -u root -p进入mysql管理页面
进入页面以后更改root密码
1 ALTER USER 'root' @'localhost' IDENTIFIED WITH mysql_native_password BY 'pwd' ;
输入flush privileges; 刷新权限
重启mysql(net stop mysql, net start mysql)
启动 mysql -u root -p
安装SQLyog
下载客户端后输入注册码
打开,连接数据库
新建一个数据库school
新建一张表student
每一个sqlyog操作本质就是对应一些命令
然后保存
右键student打开表即可添加数据
连接数据库 1 2 3 4 5 6 7 8 9 10 mysql - uroot - p flush privileges; show databases; use school; show tables; describe student; create database westos; exit;
数据库语言 CRUD增删改查
DML:操作
DDL:定义
DQL:查询
DCL:控制
操作数据库 操作数据库-> 操作数据库中的表-> 操作数据库中表的数据
mySQL的关键字不区分大小写
操作数据库
创建数据库
1 CREATE DATABASE [IF NOT EXISTS ] westos;
删除数据库
1 DROP DATABASE IF EXISTS westos
使用数据库
查看数据库
数据库的列类型
数值
tinyint 十分小的数据 1字节
smallint 较小数据 2字节
int 4字节
mediumint 中等数据 3字节
bigint 较大数据 8字节
float 4字节
double 8字节(精度问题,计算机无法处理无限循环小数)
decimal 字符串形式浮点数 金融计算的时候,一般是使用decimal
字符串
char 字符串 0~255
varchar 可变字符串 0~65535 对应String类型,常用变量
tinytext 微型文本 $2^{8} - 1$
text 文本串 $2^{16} - 1$ 保存大文本
时间日期
java.util.Date
date YYYY-MM-DD,日期格式
time HH:mm:ss 时间格式
datetime YYYY-MM-DD HH:mm:ss 最常用的时间格式
timestamp 时间戳 1970.1.1 到现在的毫秒数 也较为常用
year 年份表示
null
没有值,未知
注意:不要使用null进行运算,结果为null
数据库的字段属性 Unsigned:
Zerofill:
自增:
自动在上一条的基础上+1(默认)
通常用来设计唯一的主键,必须是整数类型
可以自定义设计主键自增的起始值和步长
非空:NULL not null
假设设置为not null,如果不给它赋值就会报错
NULL,如果不填写值,默认就是NULL
默认:
设置默认值
sex,默认为男,如果不制定值,则为默认值
创建数据库表 1 2 3 4 5 6 7 8 9 10 11 CREATE TABLE `student` ( `id` INT (4 ) NOT NULL AUTO_INCREMENT COMMENT '学员id' , `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
格式
1 2 3 4 5 CREATE TABLE [IF NOT EXISTS ] `table name`( `char name` col_type [attribute] [index] [comment], ... `char name` col_type [attribute] [index] [comment] )[table_type][charset][comment]
常用命令
1 2 3 SHOW CREATE DATABASE name ... TABLE nameDESC TABLE name
数据表类型
MYISAM
INNODB
事务支持
不支持
支持
数据行锁定
不支持
支持
外键约束
不支持
支持
全文索引
支持
不支持
表空间的大小
较小
较大,约为2倍
常规使用操作:
MYISAM 节约空间,速度较快
INNODB 安全性高,支持事务处理,多表多用户操作
在物理空间存在的位置
所有的数据库文件都存在data目录下,一个文件夹对应一个数据库
本质还是文件存储
设置数据库表的字符集编码
不设置的话,会使用mysql默认的字符集编码~
MySQL默认编码是Latin1,不支持中文
在my.ini中配置默认的编码
1 character-set-server =utf8
修改删除表
修改
1 2 3 4 5 6 7 8 9 ALTER TABLE student RENAME AS teacherALTER TABLE `teacher` ADD `age` INT (11 )ALTER TABLE `teacher` MODIFY age VARCHAR (10 ) ALTER TABLE `teacher` CHANGE age age1 INT (1 ) ALTER TABLE `teacher` DROP age1
删除
1 2 DROP TABLE [iF NOT EXISTS ] `teacher`
所有创建和删除的操作尽量加上判断,以免报错
注意点:
所有字段名尽量用``包裹起来
注释 – 、/**/
sql 关键字大小写不敏感,建议小写
所有符号用英文
MySQL数据管理 外键
方式1:在创建表的时候,增加约束(比较复杂)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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= utf8CREATE TABLE `student` ( `id` INT (4 ) NOT NULL AUTO_INCREMENT COMMENT '学员id' , `name` VARCHAR (30 ) NOT NULL DEFAULT '匿名' COMMENT '姓名' , `pwd` VARCHAR (20 ) NOT NULL DEFAULT '123456' COMMENT '密码' , `sex` VARCHAR (2 ) NOT NULL DEFAULT '女' COMMENT '性别' , `birthday` DATETIME DEFAULT NULL COMMENT '出生日期' , `gradeid` INT (10 ) NOT NULL COMMENT '学生年级' , `address` VARCHAR (100 ) DEFAULT NULL COMMENT '家庭住址' , `email` VARCHAR (50 ) DEFAULT NULL COMMENT '邮箱' , PRIMARY KEY(`id`), KEY `FK_gradeid` (`gradeid`), CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`) )ENGINE= INNODB DEFAULT CHARSET= utf8
删除有外键关系的表的时候,必须先删除引用别人的表(从表),再删除被引用的表(主表)
方式2:添加表成功后,再添加外键约束
1 2 3 4 5 ALTER TABLE `student`ADD CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`);
以上操作都是物理外键,数据库级别的外键,不建议使用!避免数据库过多造成困扰,这里了解即可
最佳实现:
数据库就是单纯的表,只用来存数据,只有行(数据)和列(字段)
我们想使用多张表的数据,想使用外键(程序去实现)
DML语言 数据库意义: 数据存储,数据管理
DML语言:数据操作语言
添加 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 INSERT INTO `grade`(`gradename`) VALUES ('大四' )INSERT INTO `grade` VALUES ('大三' ) INSERT INTO `grade` (`gradename`)VALUES ('大一' ),('大二' ),('大三' );INSERT INTO `student` (`name`,`pwd`)VALUES ('张三' ,'123456' ), ('李四' ,'345678' );
注意事项:
字段和字段之间使用英文逗号隔开
字段可以省略,但是值必须一一对应(不建议,容易出错)
可以同时插入多条数据,VALUES后面的值需要使用逗号隔开
修改
update 修改对象(条件) set原来的值 -> 新值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 UPDATE `student`SET `name`= 'shuwen' WHERE `id`= 1 ;UPDATE `student`SET `name`= 'Apocalypse' ;UPDATE `student`SET `name`= 'chen' , `email`= '1136@qq.com' WHERE id= 1 ;
条件:where子句 运算符 id 等于,大于,小于某个值,在某个区间内修改… …
操作符会返回boolean值
操作符
意义
范围
结果
=
等于
<> 或 !=
不等于
>
<
>=
<=
BETWEEN … AND …
闭区间[x,y]
BETWEEN id=2 AND id=5
AND
相当于&&
5>1 and 1>2
false
OR
相当于||
5>1 or 1>2
true
1 2 3 4 UPDATE `student`SET `name`= 'hao' WHERE `name`= 'shuwen' AND id= 2 ;
注意:
使用column列尽量带上``
筛选条件,如果没有指定则会修改所有的列
value是一个具体的值,也可以是一个变量
1 2 3 4 UPDATE `student`SET `birthday`= CURRENT_TIME WHERE `name`= 'hao' ;
多个设置的属性之间使用英文逗号隔开
删除
delete命令
语法:delete from 表名 [where 条件]
1 2 3 4 5 DELETE FROM `student` WHERE id= 1 ;DELETE FROM `student`
TRUNCATE 命令
作用:完全清空一个数据库表,表的结果和索引约束不会变!
delete 和truncate的区别:
delete删除的问题:重启数据库,会发生现象:
INNODB 自增列会从1开始,(存在内存当中,断点即失)
MyISAM 继续从上一个自增量开始 (存在文件中,不会丢失)
DQL查询数据 DQL (Data Query Language: 数据查询语言)
所有的查询操作都使用它 Select
简单的查询,复杂的查询都能做
数据库中最核心的语言
使用频率最高
1 2 3 4 5 6 7 8 9 10 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 }];
顺序很重要:
select 去重 查询的字段(主义:表和字段可以取别名)
xx join 要连接的表 on 等值判断
where (具体的值, 子查询语句)
group by(通过哪个字段来分组)
having (过滤分组后的信息,条件和where一样,位置不同)
order by(通过哪个字段排序)[升序/降序]
limit startindex, pagesize
查询:跨表,跨数据库
指定查询字段 1 2 3 4 5 6 7 8 9 10 11 12 SELECT * FROM studentSELECT * FROM result SELECT `studentno`,`studentname` FROM studentSELECT `studentno` AS 学号,`studentname` AS 姓名 FROM studentSELECT CONCAT('姓名:' ,studentname) AS 新名字 FROM student
语法:select 字段1,… from 表
有的时候,列名字不是那么见名知意,可以其别名 AS 字段名 as 别名; 表名 as 别名
去重 distinct
作用:去除SELECT查询出来的结果中重复的数据,重复的数据只显示一条
1 2 3 4 5 SELECT * FROM result SELECT `studentno` FROM result SELECT DISTINCT `studentno` FROM result
数据库的列(表达式)
1 2 3 4 5 6 7 8 9 SELECT VERSION() SELECT 100 * 3 -1 AS 计算结果 SELECT @@auto _increment_increment SELECT `studentno`,`studentresult`+ 1 AS 提分后 FROM result
数据库中的表达式:文本的值,列,NULL,函数,计算表达式… …
select 表达式 from 表
where条件子句 作用:检索数据中符合条件的值
搜索的条件由一个或者多个表达式组成
逻辑运算符
与或非
and &&
or ||
Not !
尽量使用英文字母
1 2 3 4 5 6 7 8 9 10 11 12 SELECT `studentno`,`studentresult` FROM result WHERE `studentresult` >= 90 AND `studentresult` <= 120 SELECT `studentno`,`studentresult` FROM result WHERE `studentresult` BETWEEN 95 AND 120 SELECT `studentno`,`studentresult` FROM result WHERE NOT `studentno`= 1000
模糊查询:比较运算符
运算符
语法
描述
IS NULL
a is null
如果操作符为null,则结果为真
IS NOT NULL
a is not null
如果操作符不为null,则结果为真
BETWEEN… AND…
a between b and c
a 在 b 和 c之间则返回真
Like
A LIke b
SQL匹配:如果a能够匹配到b,结果为真
In
a in (a1,a2,a3… )
假设a在a1,a2,a3…其中的某一个值中,则结果为真
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 SELECT `studentno`,`studentresult` FROM result WHERE `studentresult` BETWEEN 95 AND 120 SELECT `studentno`,`studentresult` FROM result WHERE NOT `studentno`= 1000 SELECT `studentno`,`studentname` FROM studentWHERE `studentname` LIKE '刘%' SELECT `studentno`,`studentname` FROM studentWHERE `studentname` LIKE '刘_' SELECT `studentno`,`studentname` FROM studentWHERE `studentname` LIKE '%伟%' SELECT `studentno`,`studentname` FROM studentWHERE `studentno` IN (1001 ,1002 ,1003 );SELECT `studentno`,`studentname` FROM studentWHERE `address` LIKE '%湖北%' ;SELECT `studentno`,`studentname` FROM studentWHERE `address`= '' OR address IS NULL ;SELECT `studentno`,`studentname` FROM studentWHERE `borndate` IS NOT NULL ;
联表查询
JOIN 对比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 -- 联表查询 -- 查询参加了考试的同学(学号,姓名,科目编号,分数) SELECT * FROM student SELECT * FROM result /* 思路: 1.需求:分析查询的字段来自哪些表,(连接查询) 2.确定使用哪种连接查询?7种 3.确定一个交叉点(这两个表中哪个数据是相同的) 判断的条件:学生表中的studentno = 成绩表中的studentno */ -- join(连接的表) on(判断条件) 连接查询 -- where 等值查询 SELECT s.studentno, studentname, subjectno, studentresult FROM student AS s INNER JOIN result AS r WHERE s.studentno = r.studentno -- Right Join SELECT s.studentno, studentname, subjectno, studentresult FROM student AS s RIGHT JOIN result 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 -- 查询了参加的考试的同学信息:学号,学生姓名,科目名称,分数 /* 思路: 1.需求:分析查询的字段来自哪些表,student, result, subject(连接查询) 2.确定使用哪种连接查询?7种 3.确定一个交叉点(这两个表中哪个数据是相同的) 判断的条件:学生表中的studentno = 成绩表中的studentno */ SELECT s.studentno, studentname, subjectname, studentresult FROM student s RIGHT JOIN result r ON r.studentno = s.studentno INNER JOIN `subject` sub ON r.subjectno = sub.subjectno -- 我要查询哪些数据 select ... -- 从哪几个表中查 from 表 xxx join 表 -- on 交叉条件 -- 假设存在一种多张表查询,先查询两张表,然后再慢慢增加 -- from A left join B -- from B right join A
操作
描述
inner join
如果表中至少有一个匹配,就返回行
left join
即使右表中没有匹配,也会从左表中返回对应的值
right join
和left join相反
自连接
自己的表和自己的表连接,核心:一张表拆为两张一样的表即可
示例表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 CREATE TABLE `category`( `categoryid` INT (10 ) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主题id' , `pid` INT (10 ) NOT NULL COMMENT '父id' , `categoryName` VARCHAR (50 ) NOT NULL COMMENT '主题名字' , PRIMARY KEY(`categoryid`) )ENGINE= INNODB AUTO_INCREMENT= 9 DEFAULT CHARSET= utf8INSERT INTO `category` (`categoryid`, `pid`, `categoryName`)VALUES ('2' ,'1' ,'信息技术' ), ('3' ,'1' ,'软件开发' ), ('4' ,'3' ,'数据库' ), ('5' ,'1' ,'美术设计' ), ('6' ,'3' ,'web开发' ), ('7' ,'5' ,'ps技术' ), ('8' ,'2' ,'办公信息' );
父类
categoryid
categoryName
2
信息技术
3
软件开发
5
美术设计
子类
pid
categoryid
categoryName
3
4
数据库
2
8
办公信息
3
6
web开发
5
7
美术设计
操作:查询父类对应的子类关系
父类
子类
信息技术
办公信息
软件开发
数据库
软件开发
web开发
美术设计
ps技术
1 2 3 4 SELECT a.categoryName AS '父栏目' , b.categoryName AS '子栏目' FROM category AS a, category AS bWHERE a.`categoryid` = b.`pid`
分页和排序
排序
1 2 3 4 5 6 7 8 9 10 11 12 13 SELECT s.studentno, studentname, subjectname, studentresultFROM student sINNER JOIN `result ` rON s.studentno = r.studentnoINNER JOIN `subject` subON r.subjectno = sub.subjectnoWHERE subjectname = '高等数学-1' ORDER BY studentresult ASC
分页
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 SELECT * FROM `subject` LIMIT 6 ,5
语法:limit (查询起始下标,pageSize)
子查询 where (这个值是计算出来的)
本质:在where语句中嵌套一个子查询语句
where(select * from )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 SELECT studentno, r.subjectno, studentresultFROM result rINNER JOIN `subject` subON r.subjectno = sub.subjectnoWHERE subjectname LIKE '高等数学-1' ORDER BY studentresult DESC SELECT studentno, subjectno, studentresultFROM result WHERE subjectno = (SELECT subjectno FROM `subject` WHERE subjectname LIKE '高等数学-1' )ORDER BY studentresult DESC SELECT studentname, s.studentno, studentresultFROM student sINNER JOIN result rON s.studentno = r.studentnoWHERE r.studentresult >= 80 AND subjectno = ( SELECT subjectno FROM `subject` WHERE subjectname = '高等数学-2' )
嵌套查询
在子查询语句里面再嵌套一层子查询语句
1 2 3 4 5 6 7 8 9 10 11 SELECT studentno, studentnameFROM student sWHERE studentno IN ( SELECT studentno FROM result WHERE studentresult >= 80 AND subjectno = ( SELECT subjectno FROM `subject` WHERE subjectname = '高等数学-2' ) )
分组和过滤 1 2 3 4 5 6 7 8 SELECT SubjectName, AVG (StudentResult) AS 平均分, MAX (StudentResult) AS 最高分, MIN (StudentResult) AS 最低分FROM result rINNER JOIN `subject` subON r.`subjectno` = sub.`subjectno`GROUP BY r.`subjectno` HAVING 平均分 > 80
MySQL函数 官网地址:https://dev.mysql.com/doc/refman/5.7/en/func-op-summary-ref.html
常用函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 SELECT ABS (-8 ) SELECT CEILING (1.1 ) SELECT FLOOR (1.3 ) SELECT RAND() SELECT SIGN(-5 ) SELECT CHAR_LENGTH ('好' ) AS len SELECT CONCAT('我爱' ,'Java' ) AS str SELECT INSERT ('I love code' , 3 , 0 , 'truly ' ) SELECT LOWER ('ABC' )SELECT UPPER ('abc' )SELECT INSTR('kuang' ,'a' ) SELECT REPLACE('life' ,'f' ,'v' ) SELECT SUBSTR('livvve' , 3 , 3 ) SELECT REVERSE('hello' ) SELECT REPLACE(studentname, '刘' , '邹' ) FROM studentWHERE studentname LIKE '刘%' SELECT CURRENT_DATE () SELECT CURDATE()SELECT NOW() SELECT LOCALTIME () SELECT SYSDATE() SELECT YEAR (NOW());SELECT MONTH (NOW());SELECT DAY (NOW());SELECT HOUR (NOW());SELECT MINUTE (NOW());SELECT SECOND (NOW())SELECT SYSTEM_USER ()SELECT USER ()SELECT VERSION()
聚合函数(更常用)
函数名称
描述
COUNT()
SUM()
AVG()
MAX()
MIN()
…
1 2 3 4 5 6 SELECT COUNT (studentname) FROM student; SELECT COUNT (* ) FROM student; SELECT COUNT (1 ) FROM result ;
数据库级别的MD5加密 MD5:主要增强算法的复杂度和不可逆性。
MD5不可逆,具体的值md5是一样的
MD5破解网站的原理:背后有一个字典,MD5加密后的值 对比 加密前的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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= utf8INSERT INTO `testmd5` VALUES (1 , 'zhangsan' , '123456' ), (2 , 'lisi' , '134567' ), (3 , 'wangwu' , '134567' );UPDATE testmd5 SET pwd= MD5(pwd);INSERT INTO testmd5 VALUES (4 , 'xiaoming' , MD5('123456' ));SELECT * FROM testmd5 WHERE `name`= 'xiaoming' AND pwd = MD5('123456' )
事务 概念 核心:将一组SQL放在一个批次中去执行
事务的原则:ACID原则
原子性,一致性,隔离性,持久性 (脏读,幻读…)
详细介绍链接:https://blog.csdn.net/dengjili/article/details/82468576
原子性(Atomicity) 要么成功,要么失败
SQL执行 A 给 B 转账 A 1000 ->200 B 200
SQL执行 B 收到 A 的钱 A 800 B 400
一致性(Consistency) 事务前后的数据完整性保持一致,比如最终一致性,A和B的总钱数不会变化,始终为1200
隔离性(Isolation) 多个用户并发访问数据库时,数据库为每一个用户开启的事务,不会被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
隔离所导致的一些问题:
持久性(Durability) – 事务提交
事务一旦提交则不可逆,被持久化到数据库中
执行事务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 SET autocommit = 0 SET autocommit = 1 SET autocommit = 0 ; START TRANSACTION COMMIT ROLLBACK SET autocommit = 1 SAVEPOINT 保存点名 ROLLBACK TO SAVEPOINT 保存点名 RELEASE SAVEPOINT 保存点名
转账实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 CREATE DATABASE shop CHARACTER SET utf8 COLLATE utf8_general_ci USE shopCREATE TABLE `account`( `id` INT (3 ) NOT NULL AUTO_INCREMENT, `name` VARCHAR (30 ) NOT NULL , `money` DECIMAL (9 , 2 ) NOT NULL , PRIMARY KEY (`id`) )ENGINE= INNODB DEFAULT CHARSET= utf8INSERT INTO `account`(`name`, `money`)VALUES ('A' , 2000.00 ), ('B' , 10000.00 )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 ;
在Java中的使用情况:
1 2 3 4 5 6 7 8 public Function{ try (){ something; commit(); }catch (Exception){ rollback(); } }
索引 详细介绍链接:http://blog.codinglabs.org/articles/theory-of-mysql-index.html
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构 。
提取句子主干,就可以得到索引的本质:索引是数据结构。
索引的分类
在一个表中,主键索引只能有一个,唯一索引可以有多个
主键索引 PRIMARY KEY
唯一索引 UNIQUE KEY
避免重复的列出现,唯一索引可以重复,多个列都可以标识为唯一索引
常规索引 KEY/INDEX
全文索引 FullText
在特定的数据库引擎下才有,如MyISAM
快速定位数据
基础语法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 SHOW INDEX FROM studentALTER TABLE school.`student` ADD FULLTEXT INDEX `studentname`(`studentname`) EXPLAIN SELECT * FROM student; EXPLAIN SELECT * FROM student WHERE MATCH (studentname) AGAINST('刘' );
测试索引 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 CREATE TABLE `app_user`( `id` BIGINT (20 ) UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR (50 ) DEFAULT '' COMMENT '用户昵称' , `email` VARCHAR (50 ) NOT NULL COMMENT '用户邮箱' , `phone` VARCHAR (20 ) DEFAULT '' COMMENT '手机号' , `gender` TINYINT(4 ) UNSIGNED DEFAULT '0' COMMENT '性别(0:男,1:女)' , `password` VARCHAR (100 ) NOT NULL COMMENT '密码' , `age` TINYINT(4 ) DEFAULT '0' COMMENT '年龄' , `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP , `update_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP , PRIMARY KEY (`id`) ) ENGINE= INNODB CHARSET= utf8mb4 COMMENT= 'app用户表' DELIMITER $$ CREATE FUNCTION `mock_data`()RETURNS INT DETERMINISTIC 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), '54323456@qq.com' , CONCAT('18' , FLOOR (RAND() * ((999999999 -100000000 ) + 100000000 )) ), FLOOR (RAND() * 2 ), UUID(), FLOOR (RAND() * 100 ) ); SET i = i + 1 ; END WHILE; RETURN i;END ;SELECT mock_data();SELECT * FROM app_user WHERE `name`= '用户9999' ; CREATE INDEX id_app_user_name ON app_user(`name`);SELECT * FROM app_user WHERE `name`= '用户9999' ;
索引在小数据量的时候,用处不大,但是在大数据的时候,区别十分明显~
索引原则
索引不是越多越好
不要对经常变动的数据加索引
小数据量的表不需要加索引
索引一般加在常用来查询的字段上(提高查询速度)
索引的数据结构
Hash 类型的索引
Btree:INNODB的默认数据结构
权限管理 用户管理
SQL yog 可视化管理
SQL 命令操作
用户表:mysql.user
本质:对这张表进行增删改查
详细命令链接:https://blog.csdn.net/zy_1995/article/details/102708482
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 CREATE USER kuangshen IDENTIFIED BY '123456' ALTER USER 'kuangshen' @'localhost' IDENTIFIED BY '111111' SET PASSWORD FOR 'kuangshen2' @'localhost' = '2222222' RENAME USER 'kuangshen' @'localhost' TO 'kuangshen2' @'localhost' GRANT ALL PRIVILEGES ON * .* TO 'kuangshen' @'localhost' DROP USER 'kuangshen' @'localhost' SHOW GRANTS FOR 'kuangshen' @'localhost' SHOW GRANTS FOR root@localhost REVOKE ALL PRIVILEGES ON * .* FROM kuangshen2@localhost
MySQL备份
MySQL数据库备份的方式:
直接拷贝物理文件…../data
在SQL yog可视化工具中手动导出
在左栏右键选择导出转储,选择结构和数据,指定导出的位置
将文件直接拉入yog中即可使用
使用命令行 mysqldump 在命令行中使用
1 2 # mysqldump -h主机 -u用户名 -p密码 数据库名 [表1 表2 表3 ...] > 物理磁盘位置 mysqldump -hlocalhost -uroot -pxxxxxx school student >D:/a.sql
导入方法:
1 2 3 # 登录后使用source命令 mysql -uroot -pxxxxxx source .sql文件位置
规范数据设计 为什么需要设计? 当数据库比较复杂的时候,就需要进行设计。
糟糕的数据库设计:
数据冗余,浪费空间
数据库插入和删除都会很麻烦,异常(屏蔽使用物理外键)
程序性能差
良好的数据库设计:
软件开发中,关于数据库的设计
分析需求:分析业务和需要处理的数据库的需求
概要设计:设计关系图 E-R 图
设计数据库的步骤:(个人博客)
收集信息,分析需求
用户表(用户登录注销,用户的个人信息,写博客,创建分类)
分类表(文章分类,谁创建的)
文章表(文章的信息)
评论表(评论)
友链表(友链信息)
自定义表(系统信息,某个关键的字或者一些主字段) key: value
说说表(发表心情 id content create_time)
标识实体(把需求落地到每个字段)
标识实体之间的关系
写博客 user -> blog
创建分类 user -> category
关注 user -> user
友链 links
评论 user-user-blog
三大范式 为什么需要数据规范化?
可能出现的问题:
详细链接:https://www.cnblogs.com/wsg25/p/9615100.html
第一范式 原子性:保证每一列不可再分
第二范式 前提:满足第一范式
每张表只描述一件事情
第三范式 前提:满足第一范式和第二范式
确保数据表中的每一列数据都和主键直接相关,而不能间接相关
规范性和性能的问题
关联查询的表不能超过三张表
考虑商业化的需求和目标,(成本,用户体验)数据库的性能更加重要
规范性能的问题时,需要适当考虑规范性
故意给某些表增加一些冗余的字段(从多表查询变为单表查询)
故意增加一些计算列(从大数据量降低为小数据量查询:索引)
JDBC 数据库驱动
程序会通过数据库驱动,和数据库打交道。
JDBC概念 SUN公司为了简化开发人员的(对数据库统一)的操作,提供了一个(java操作数据库的)规范,称为JDBC。
这些规范的实现由具体的厂商去做。
对于开发人员,只需要操纵API即可。
java.sql
javax.sql
还需要导入一个数据库驱动包
mysql-connector-java.jar
第一个JDBC程序
创建测试数据库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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' , 'zs@sina.com' , '1980-12-04' ), (2 , 'lisi' , '123456' , 'lisi@sina.com' , '1981-12-04' ), (3 , 'wangwu' , '123456' , 'wangwu@sina.com' , '1979-12-04' );
创建一个普通项目
导入数据库驱动
编写测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import java.sql.*;public class jdbcFirstDemo { public static void main (String[] args) throws ClassNotFoundException, SQLException { Class.forName("com.mysql.cj.jdbc.Driver" ); String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT" ; String username = "root" ; String password = "123456" ; Connection connection = DriverManager.getConnection(url, username, password); Statement statement = connection.createStatement(); 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" )); System.out.println("===========================" ); } resultSet.close(); statement.close(); connection.close(); } }
步骤总结:
加载驱动
连接数据库 DriverManager
获取执行SQL的对象 Statement
获取返回的结果集
释放连接
DriveManager
1 2 3 4 5 6 7 8 9 10 Class.forName("com.mysql.cj.jdbc.Driver" ); Connection connection = DriverManager.getConnection(url, username, password); connection.setAutoCommit(); connection.commit(); connection.rollback();
url
1 2 3 4 5 6 String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT" ;
Statement –执行SQL 的对象 PrepareStatement
1 2 3 4 5 6 String sql = "select * from users" ; statement.executeQuery("" ); statement.execute("" ); statement.executeUpdate("" );
ResultSet查询的结果集:封装了所有的查询结果
获取指定的数据类型
1 2 3 4 5 resultSet.getInt(); resultSet.getString(); resultSet.getDate(); resultSet.getObject(); ...
遍历,指针
1 2 3 4 5 resultSet.beforeFirst(); resultSet.afterLast(); resultSet.next(); resultSet.previous(); resultSet.absolute(row);
释放资源
1 2 3 4 resultSet.close(); statement.close(); connection.close();
statement对象 jdbc中的statement对象用于向数据库发送SQL语句,想完成数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可。
Statement对象的executeUpdate方法,用于向数据库发送增删改的SQL语句,executeUpdate执行完后,将会返回一个整数(变化的行数)。
Statement.executeQuery方法用于向数据库发送查询语句,executeQuery方法返回查询结果的ResultSet对象。
CRUD操作- create
使用executeUpdate(String sql)方法完成数据添加操作:
1 2 3 4 5 6 Statement st = connection.createStatement();String sql = "insert into user(...) values(...)" ;int num = st.executeUpdate(sql);if (num > 0 ){ System.out.println("插入成功!" ); }
CRUD操作- delete
1 2 3 4 5 6 Statement st = connection.createStatement();String sql = "delete from user where id = 1" ;int num = st.executeUpdate(sql);if (num > 0 ){ System.out.println("删除成功!" ); }
CRUD操作- update
1 2 3 4 ...;String sql = "update user set name = '' where name = '' " ;int num = st.executeUpdate(sql); ...;
CRUD操作- read
1 2 3 4 5 6 ...;String sql = "select * from user where id = 1" ;ResultSet rs = st.executeQuery(sql);while (rs.next()){ ...; }
代码实现
提取工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package utils;import java.io.IOException;import java.io.InputStream;import java.sql.*;import java.util.Properties;public class JdbcUtils { private static String driver = null ; private static String url = null ; private static String username = null ; private static String password = null ; static { try { InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties" ); Properties properties = new Properties (); properties.load(inputStream); driver = properties.getProperty("driver" ); url = properties.getProperty("url" ); username = properties.getProperty("username" ); password = properties.getProperty("password" ); Class.forName(driver); }catch (IOException e){ e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection () throws SQLException { return DriverManager.getConnection(url, username, password); } public static void release (Connection connection, Statement statement, ResultSet resultSet) { if (resultSet != null ){ try { resultSet.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (statement != null ){ try { statement.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (connection != null ){ try { connection.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } }
db.properties文件:
1 2 3 4 driver =com.mysql.cj.jdbc.Driver url =jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT username =root password =123456
编写增删改查的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import utils.JdbcUtils;import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class TestInsert { public static void main (String[] args) { Connection connection = null ; Statement statement = null ; ResultSet resultSet = null ; try { connection = JdbcUtils.getConnection(); statement = connection.createStatement(); String sql = SQL语句; int i = statement.executeUpdate(sql); if (i > 0 ){ System.out.println("success!" ); } } catch (SQLException sqlException) { sqlException.printStackTrace(); } finally { JdbcUtils.release(connection, statement, resultSet); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import utils.JdbcUtils;import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class TestInsert { public static void main (String[] args) { Connection connection = null ; Statement statement = null ; ResultSet resultSet = null ; try { connection = JdbcUtils.getConnection(); statement = connection.createStatement(); String sql = "select * from users" ; resultSet = statement.executeQuery(sql); if (resultSet != null ){ 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" )); System.out.println("===========================" ); } } } catch (SQLException sqlException) { sqlException.printStackTrace(); } finally { JdbcUtils.release(connection, statement, resultSet); } } }
SQL注入问题 (sql injection) sql存在漏洞,会被攻击导致数据泄露 SQL会拼接or
1 2 3 4 5 sql = "select * from users where name ='" + username + "' and password = '" + password + "'" ; username = "' or '1=1'" ; password = "' or '1=1'" ;
PreparedStatement对象 PreparedStatement 可以防止SQL注入,并且效率更高。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import utils.JdbcUtils;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;public class TestSelect1 { public static void main (String[] args) { Connection connection = null ; PreparedStatement preparedStatement = null ; ResultSet resultSet = null ; try { String username = "shuwen" ; String password = "123456" ; connection = JdbcUtils.getConnection(); String sql = "select * from users where name = ? and password = ?" ; preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1 , username); preparedStatement.setString(2 , password); resultSet = preparedStatement.executeQuery(); if (resultSet.next()){ System.out.println(resultSet.getString("name" )); } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JdbcUtils.release(connection, preparedStatement, resultSet); } } }
使用IDEA连接数据库 在右侧栏添加数据库
连接成功后,可以选择数据库
如果连接失败,可以在设置-》驱动里面查看版本是否正确,或者类是否正确。
JDBC处理事务 回顾
要么都成功,要么都失败
ACID原则
原子性:要么全部完成,要么都不完成
一致性:总数不变
隔离性:多个进程互不干扰
持久性:一旦提交不可逆,持久化到数据库
隔离性的问题:
脏读:一个事务读取了另一个没有提交的事务
不可重复读:在同一个事务内,重复读取表中的数据,数据发生了改变
幻读:在一个事务内,读取到了别人插入的数据,导致前后读出来的结果不一致
代码实现:
开启事务connection.setAutoCommit(false);
一组业务执行完毕,提交事务
可以在catch语句中显式定义回滚语句,但默认会回滚
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import utils.JdbcUtils;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;public class TestTransaction { public static void main (String[] args) { Connection connection = null ; PreparedStatement preparedStatement = null ; ResultSet resultSet = null ; try { connection = JdbcUtils.getConnection(); connection.setAutoCommit(false ); String sql1 = "update account set money = money - 100 where name = 'A'" ; preparedStatement = connection.prepareStatement(sql1); preparedStatement.execute(); String sql2 = "update account set money = money + 100 where name = 'B'" ; preparedStatement = connection.prepareStatement(sql2); preparedStatement.execute(); connection.commit(); System.out.println("success!" ); } catch (Exception throwables) { try { connection.rollback(); } catch (SQLException e) { e.printStackTrace(); } throwables.printStackTrace(); } finally { JdbcUtils.release(connection, preparedStatement, resultSet); } } }
数据库连接池 数据库连接–执行完毕–释放
但是连接到释放会十分浪费系统资源
池化技术:准备一些预先的资源,过来就连接预先准备好的
最小连接数:10
最大连接数:100 业务最高承载上限
等待超时:100ms
编写连接池,实现一个接口 DataSource
开源数据源实现(拿来即用)
DBCP
C3P0
Druid:阿里巴巴
使用了这些数据库连接池之后,我们在项目开发中就不需要编写连接数据库的代码了。
DBCP
需要用到的jar包:
commons-dbcp,commons-pool
dbcpconfig.properties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 # 连接设置 这里面的名字,是DBCP数据源定义好的 driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT username=root password=123456 #<!-- 初始化连接 --> initialSize=10 #最大连接数量 maxActive=50 #<!-- 最大空闲连接 --> maxIdle=20 #<!-- 最小空闲连接 --> minIdle=5 #<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 --> maxWait=60000 #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。 connectionProperties=useUnicode=true;characterEncoding=utf8 #指定由连接池所创建的连接的自动提交(auto-commit)状态。 defaultAutoCommit=true #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。 #可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE defaultTransactionIsolation=READ_UNCOMMITTED
JdbcUtils_DBCP.java 利用工厂模式创建数据库连接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package utils;import org.apache.commons.dbcp.BasicDataSourceFactory;import javax.sql.DataSource;import java.io.IOException;import java.io.InputStream;import java.sql.*;import java.util.Properties;public class JdbcUtils_DBCP { private static DataSource dataSource = null ; static { try { InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties" ); Properties properties = new Properties (); properties.load(inputStream); dataSource = BasicDataSourceFactory.createDataSource(properties); }catch (IOException e){ e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection () throws SQLException { return dataSource.getConnection(); } public static void release (Connection connection, Statement statement, ResultSet resultSet) { if (resultSet != null ){ try { resultSet.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (statement != null ){ try { statement.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (connection != null ){ try { connection.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } }
插入数据测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import utils.JdbcUtils;import utils.JdbcUtils_DBCP;import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class TestDBCP { public static void main (String[] args) { Connection connection = null ; Statement statement = null ; ResultSet resultSet = null ; try { connection = JdbcUtils_DBCP.getConnection(); statement = connection.createStatement(); String sql = "select * from users" ; resultSet = statement.executeQuery(sql); if (resultSet != null ){ 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" )); System.out.println("===========================" ); } } } catch (SQLException sqlException) { sqlException.printStackTrace(); } finally { JdbcUtils_DBCP.release(connection, statement, resultSet); } } }
C3P0
需要用到的jar包
c3p0, mchange-commons-java
结论
无论使用什么数据源,本质相同:DataSource接口不会变,方法就不会变
Druid