MySql
-
什么是mysql -
有什么用 -
怎么用
1.简介
1.1概念
-
关系型数据库(SQL):通过表和表之间,行和列之间的关系进行存储
-
DBMS(数据库管理系统),用于管理数据库的一种软件 -
相当于打开一个excel表格需要一个软件打开它,而DBMS就是实现这样一个功能 -
mysql就是数据库管理系统软件之一 -
它是目前世界上最好的RDBMS(关系型数据库系统)应用软件之一 -
特点:体积小,速度快,总体拥有成本低
-
非关系型数据库(NO SQL:not only sql):通过对象存储,通过对象自身的属性来决定 -
严格上来说,非关系型数据库并不是一种数据库,它实际是一种数据结构化存储的集合,本质上来说就是一种集合(文档型,键值对型,图形数据库,列式数据库)
官网 MySQL
1.2作用
DBMS说白了就是用于存储数据,以及管理数据
所使用的语言是SQL语言,重点学习如何操作数据库
1.3安装
-
尽量不要安装exe,里面有大量的注册表,卸载麻烦 -
安装压缩包 MySQL :: MySQL Downloads
启动mysql:net start mysql
具体安装出现的问题查看 cmd下无法启动MySQL的几种原因及解决方法_打瞌睡_的博客-CSDN博客_cmd打不开mysql
2.操作数据库
2.1操作数据库
创建数据库
CREATE DATABASE school;
删除数据库
DROP DATABASE school;
查看数据库
SHOW DATABASES;
使用数据库
USE school;
2.2数据库的列类型
数值
字符串
时间日期
? java.util.Date
null
-
没有,未知 -
注意,不要使用null进行运算,结果为null
2.3数据库的字段属性
unsigned
zerofill
自增
-
auto_increment -
自增属性在整一张表中只有一个 -
表示数量会自动加1
非空
2.4创建表
CREATE TABLE IF not EXISTS`student` (
? `id` INT (10) NOT NULL auto_increment COMMENT '学号',
? `name` VARCHAR (20) NOT NULL DEFAULT '匿名' COMMENT '姓名',
? `pwd` VARCHAR (20) DEFAULT NULL COMMENT '密码',
? `address` VARCHAR (50) DEFAULT NULL COMMENT '家庭住址',
? `email` VARCHAR (30) DEFAULT NULL COMMENT '邮箱',
? `birthday` datetime DEFAULT null comment '生日',
? PRIMARY KEY (`id`)
?);
格式
?-- 注意字段使用飘包裹,字符串使用单引号
?-- 所有的符号全部用英文
?-- mysql关键字大小写不敏感,建议小写
?create table [if not exists] `表名`(
? ?--`字段名` 类型 [其他属性] 注释写在后面,
? ?--`字段名` 类型 [其他属性] 注释写在后面,
? ......
? ?--`字段名` 类型 [其他属性] 注释写在后面
?)[表类型 字符集 注释]
查询的命令符
-- 查询创建的数据库
?SHOW CREATE DATABASE school;
?-- 查询数据库中创建的表
?SHOW CREATE TABLE student;
?CREATE TABLE `student` (
? `id` int NOT NULL AUTO_INCREMENT COMMENT '学号',
? `name` varchar(20) NOT NULL DEFAULT '匿名' COMMENT '姓名',
? `pwd` varchar(20) DEFAULT '123456' COMMENT '密码',
? `address` varchar(50) DEFAULT NULL COMMENT '家庭住址',
? `email` varchar(30) DEFAULT NULL COMMENT '邮箱',
? `birthday` datetime DEFAULT NULL COMMENT '生日',
? PRIMARY KEY (`id`)
?) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
?-- 查看表的结构
?DESC student;
2.5数据库表的类型
?-- 查看数据库所支持的引擎
?SHOW ENGINES;
| MYISAM | INNODB |
---|
事务 | 不支持 | 支持 | 数据行锁定 | 不支持(支持表锁定) | 支持 | 外键约束 | 不支持 | 支持 | 全文索引 | 支持 | 不支持 | 表空间的大小 | 较小 | 较大,约它的两倍 |
常用的数据库引擎:
在物理空间存在的位置
所有的数据库文件都存在于data目录下
本质上还是文件的存储
MySql引擎在物理文件上的区别
设置数据库表的字符集编码
?charset=utf8
假设不设置的话,会是数据库中默认的编码,不支持中文
或者在my.ini文件中设置默认的编码,以后都不用设置(不推荐)
?
character-set-server=utf8MB4
2.6修改和删除表
修改
-- 修改表名
?alter table 旧表名 rename as 新表名
?-- 增加表的字段
?alter table 表名 add 字段名 字段的列属性
?-- 修改表的字段
?alter table 表名 modify 字段名 修该的属性 ? 修改约束
?-- 修改表的字段名
?alter table 表名 change 旧字段名 新字段名 ? 重命名
删除
?-- 删除表的字段
?alter table 表名 drop 字段名
所有的增加和删除尽量加上判断操作[if exists]
3.MYSQL的数据管理
3.1外键约束(了解)
方式一:在创建表里面添加一个外键,在执行引用
CREATE TABLE if not exists`grade`(
`gradeid` int(10) NOT NULL auto_increment COMMENT '年级id',
`gradename` VARCHAR(20) DEFAULT NULL COMMENT '年级名称',
PRIMARY KEY (`gradeid`)
);
-- 学生表的gradeid要去引用年级表的gradeid
-- 定义一个外键
-- 给这个外键添加约束 执行引用
CREATE TABLE if not exists `student` (
`id` INT NOT NULL AUTO_INCREMENT COMMENT '学号',
`gradeid` INT (10) NOT NULL,
`pwd` VARCHAR (20) DEFAULT NULL COMMENT '密码',
`address` VARCHAR (50) DEFAULT NULL COMMENT '家庭住址',
`email` VARCHAR (30) DEFAULT NULL COMMENT '邮箱',
`birthday` datetime DEFAULT NULL COMMENT '生日',
`name` VARCHAR (30) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_gradeid` (`gradeid`),
CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`)
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci
这种方式,在删除表的时候,应删除去引用外表的表(主表),之后再删除被引用外键的表,比较复杂
方式二:先创好表,再添加约束,执行引用
ALTER table `student` ADD
CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`);
格式
alter table 表名 add
constraint 外键约束 foreign key (引用的键) reference 被引用的表名(被引用的键)
以上的操作都是物理操作,就是数据库上面去才做(不推荐) 避免困扰
最佳实践
3.2DML(数据库管理语言)(全部记住)
数据库管理语言只用来存储数据和管理数据集
之后要使用外键,就使用java程序实现
数据库管理语言
3.3添加
-- 插入语句
-- INSERT INTO 表名(字段名)value (值1),(值2),(值3),...
-- 插入的值要没有设置默认的值,会报错
insert into `student`(`name`,`pwd`,`address`) values ('hanqing','qwqwqw','广东');
insert into `student`(`name`,`address`,`email`,`birthday`)VALUES
('王五','湖南','122323243@qq.com','2020-01-09'),
('李六','湖南','122323243@qq.com','2020-01-09'),
('admin','广东','1438617560@qq.com','1995-08-06');
注意:
-
插入的字段,要与后面的值一一对应 -
所有的符号要使用英文 -
可以插入多个值,后面要使用英文逗号隔开
3.3删除
-- 删除语句
-- DELETE FROM 表名 WHERE 条件
-- 如果不加条件,则会所有的数据都删除
DELETE FROM student WHERE id=1;
truncate 清空数据库,但不会删除表的结构和索引
delete 和 truncate区别
关于delete删除数据的问题 自增列的影响
-
innodb 断电自增列即为0 -
myisam 即使断电的情况下也不会改变自增列
3.4修改
-- 修改语句
-- update 表名 set 字段名=字段值,... where 条件
update `student` set `name`='god' where id=1;
-- 如果不加条件的情况下,所有的字段值都会被更改
修改某一个字段或者多个字段的值,用逗号隔开
where 条件 返回的是一个boolean值
操作符 | 含义 | 范围 | 结果 |
---|
= | 等于 | 1=2 | false | <>或!= | 不等于 | 1<>2 | true | >= | 大于等于 | 1>=2 | false | <= | 小于等于 | 1<=2 | true | < | 小于 | 1<2 | true | > | 大于 | 1>3 | false | between...and... | 闭合区间包含 | [2,3] | | and | 和 | 多个条件判断 | 一假则假 | or | 或 | 多个条件判断 | 一真则真 |
注意:
-
字段名尽量加上飘,避免与关键字冲突 -
条件相当于是筛选,如果没有指定,就会修改所有的值 -
字段值可以是字符串,也可以是变量 -
多个属性之间使用逗号隔开 -
trim标签可以将多余的逗号去掉
update `student` set `birthday`=CURRENT_TIME WHERE id>2;
4.DQL查询数据(重点)
4.1DQL
data query language:数据查询语言
-
查询所有数据 select -
无论是简单的还是复杂的数据都可以查询 -
是数据库最核心得到一个语句
SELECT语句的完整语法为:
SELECT[ALL|DISTINCT|DISTINCTROW|TOP]
{*|talbe.*|[table.]field1[AS alias1][,[table.]field2[AS alias2][,…]]}
FROM tableexpression[,…][IN externaldatabase]
[join...on...连接表]
[WHERE…]
[GROUP BY…]
[HAVING…]
[ORDER BY…]
[WITH OWNERACCESS OPTION]
4.2简单查询
-- 查询语句
-- SELECT 字段 FROM 表名
-- 查询所有字段
SELECT * from student;
-- 查询指定字段
SELECT `studentno`,`studentname` from student;
-- 给字段起别名 使用as
SELECT `studentno` as 学号,`studentname` as 学生姓名 FROM student;
-- 给字段起别名 使用as 也可以给表起别名
SELECT `studentno` as 学号,`studentname` as 学生姓名 FROM student as s;
-- 使用函数 CONCAT() 给结果拼接字符串
SELECT CONCAT('姓名:',`studentname`) from student;
有时候查询到的数据有大部分是相同的,这时候就可以考虑去重
distinct
SELECT DISTINCT `studentno` from student;
数据库的表达式
-- 查看数据库的版本
SELECT VERSION() 函数
-- 通过select可以进行简单的计算
SELECT 100*3-1 as 计算结果; 计算表达式
-- 查询自增的步长
SELECT @@auto_increment_increment; 变量
-- 给所有学生的成绩加1分
SELECT `studentno`,`studentresult`+1 FROM result;
4.3where条件子句
搜索的一个或多个条件为真,返回的结果为真,在mysql中表示检索到了
逻辑运算符
运算符 | 语法 | 描述 |
---|
逻辑与 | and && | 两个为真,结果为真 | 逻辑或 | or || | 一个为真,结果为真 | 逻辑非 | not ! | 取反操作 | 模糊查询 | between...and... | 两个为真,结果为真 |
-- 查询所有学生的成绩
SELECT `studentno`,`studentresult` from result;
-- 查询90 -100分的学生
SELECT `studentno`,`studentresult` from result
WHERE studentresult>=90 and studentresult<=100
SELECT `studentno`,`studentresult` from result
WHERE studentresult>=90 && studentresult<=100
SELECT `studentno`,`studentresult` from result
WHERE studentresult between 90 and 100
-- 查询学号除了1000的学生
SELECT `studentno`,`studentresult` from result
WHERE not studentno=1000;
SELECT `studentno`,`studentresult` from result
WHERE studentno !=1000;
-- 查询学号除了1000 而且成绩在90-100分的学生
SELECT `studentno`,`studentresult` from result
WHERE (not studentno=1000) and (studentresult BETWEEN 90 and 100)
-- 查询名字为赵强的学生
SELECT * from student
WHERE `studentname`='赵强'
-- 查询有多少个成绩在90~100分的学生
SELECT COUNT(*) from result
WHERE studentresult between 90 and 100
模糊查询
运算符 | 语法 | 描述 |
---|
是否为空 | a is null | 如果操作符为null,结果返回true | 是否不为空 | a is not null | 如果操作符不为null,结果返回true | 区间判断 | between a and b | 如果区间存在,结果返回true | 模糊查询 | a like b | 如果操作符a像b,结果返回为true | 具体查询 | in(a,b) | 如果包含了a,b,结果返回为true |
-- 非空判断
-- 判断地址为空的学生
SELECT `studentno`,`studentname` from student
WHERE address is NULL OR address=''
-- 判断地址不为空的学生
SELECT `studentno`,`studentname` from student
WHERE address is not NULL AND `address` != ''
-- 查询学号在1005~1015之间的学生
SELECT `studentno`,`studentname` from student
WHERE `studentno` BETWEEN 1005 AND 1015
-- 查询姓赵的学生 like '%' 表示查询任意字符 '_' 表示查询一个字符
SELECT `studentno`,`studentname` from student
WHERE `studentname` LIKE '赵%'
-- 查询姓名带有强的学生 like '%' 表示查询任意字符 '_' 表示查询一个字符
SELECT `studentno`,`studentname` from student
WHERE `studentname` LIKE '%强%'
-- 查询学号有1001,1006,1009的学生
SELECT `studentno`,`studentname` from student
WHERE `studentno` in(1001,1006,1009)
注意:在sql中,字符串没有值不一定是为null,也可以是空字符串
4.4 联表查询
-- 查询参加考试的同学,包含的信息包括学号,姓名,科目编号,分数
/*
分析思路
1.分析需求,查询的字段涉及到哪些表
2.确定要哪些连接查询
3.确定交叉点,涉及到的表有哪些相同的字段
判断条件:学生表的studentno = 成绩表的studentno
*/
join on 连接查询
join where 等价查询
-- inner join
SELECT s.studentno,studentname,subjectno,studentresult
FROM student as s
INNER JOIN result as r
WHERE s.studentno=r.studentno
--left join
SELECT s.studentno,studentname,subjectno,studentresult
FROM student as s
LEFT JOIN result as r
ON 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 `subjectno` is NULL
-- 查询没有参加考试的同学,包含的信息包括学号,姓名,科目,分数
SELECT s.studentno,studentname,subjectname,studentresult
FROM student as s
RIGHT JOIN result as r
ON s.studentno=r.studentno
inner JOIN `subject` sub
on r.subjectno=sub.subjectno
-- 查询学生所属的年级(学号,姓名,年级名称)
SELECT studentno,studentname,gradename
from student s
INNER JOIN grade g
on s.gradeid=g.gradeid
-- 查询科目对应的年级(科目名称,年级名称)
SELECT subjectname,gradename
from `subject`
INNER JOIN grade
on `subject`.gradeid=grade.gradeid
-- 查询参加高等数学-1考试的同学,包含的信息包括学号,姓名,科目名,分数
SELECT s.studentno,studentname,subjectname,studentresult
FROM student s
INNER JOIN result r
ON s.studentno=r.studentno
LEFT JOIN `subject` sub
on r.subjectno=sub.subjectno
WHERE sub.subjectname='高等数学-1'
思路分析
-
分析需求,查询的字段设计到哪些表 -
确定要哪种连接 -
判断交叉点,作为条件
联表查询有7中,但只需要知道一下三个就行
-
inner join 两表连接的相同数据 -
right join 以右表为基准查询 -
left join 以右表为基准查询
连接 | 原理 |
---|
inner join | 如果表中至少有一个匹配,就返回行 | right join | 会从右表返回所有的值,即使左表没有匹配 | left join | 会从左表返回所有的值,即使右表没有匹配 |
语法:
查询字段 from 表1 left join 表2 on 交叉条件
查询字段 from 表1 right join 表2 on 交叉条件
自连接:将一个表拆分成两张表,之后进行连接
父类
categoryid | categoryname |
---|
2 | 信息技术 | 3 | 软件开发 | 5 | 美术设计 |
子类
pid | categoryid | categoryname |
---|
3 | 4 | 数据库 | 3 | 6 | web开发 | 5 | 7 | ps技术 | 2 | 8 | 办公信息 |
查询父类对应的子类关系
父类 | 子类 |
---|
软件开发 | 数据库 | 软件开发 | web开发 | 美术设计 | ps技术 | 信息技术 | 办公信息 |
SELECT a.`categoryname`as 父类, b.`categoryname` as 子类
FROM category as a,category as b
WHERE a.categoryid=b.pid
4.5分页和排序
-- 查询参加考试的同学,包含的信息包括学号,姓名,科目名,分数(以分数升序展示)
SELECT s.studentno,studentname,subjectname,studentresult
FROM student s
INNER JOIN result r
ON s.studentno=r.studentno
inner JOIN `subject` sub
on r.subjectno=sub.subjectno
ORDER BY studentresult ASC
-- 查询参加考试的同学,包含的信息包括学号,姓名,科目名,分数(以分数降序展示)
SELECT s.studentno,studentname,subjectname,studentresult
FROM student s
INNER JOIN result r
ON s.studentno=r.studentno
inner JOIN `subject` sub
on r.subjectno=sub.subjectno
ORDER BY studentresult DESC
分页的操作必须放在最后
-- 查询参加考试的同学,包含的信息包括学号,姓名,科目名,分数(以分数降序展示)
-- LIMIT 起始下标,页面大小
-- 查询前面六位的同学
SELECT s.studentno,studentname,subjectname,studentresult
FROM student s
INNER JOIN result r
ON s.studentno=r.studentno
inner JOIN `subject` sub
on r.subjectno=sub.subjectno
ORDER BY studentresult DESC
LIMIT 0,6
-- 查询考试成绩前十名的同学(学号,姓名,成绩)
SELECT s.studentno,studentname,studentresult
from student s
INNER JOIN result r
on s.studentno=r.studentno
order by studentresult DESC
LIMIT 0,10
-- 查询考试成绩大于80分,并且排在前六名的同学(学号,姓名,成绩)
SELECT s.studentno,studentname,studentresult
from student s
INNER JOIN result r
on s.studentno=r.studentno
WHERE studentresult>80
ORDER BY studentresult DESC
limit 0,6
4.6子查询
where(这个值是计算出来的)
本质:where里面嵌套一个子查询语句
-- 查询参加高等数学考试的学生(学号,姓名。科目编号,成绩),降序排列
-- 方式一:连接查询
SELECT s.studentno,studentname,r.subjectno,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 s.studentno,studentname,r.subjectno,studentresult
FROM student s
INNER JOIN result r
on s.studentno=r.studentno
WHERE subjectno=(
SELECT subjectno from `subject` WHERE subjectname='高等数学-1'
)
order by studentresult DESC
-- 查询分数不小于80的学生的学号,姓名
SELECT s.studentno,studentname
from student s
INNER JOIN result r
on s.studentno=r.studentno
WHERE studentresult>=80
-- 查询参加高等数学-1分数不小于80的学生的学号,姓名
SELECT s.studentno,studentname
from student s
INNER JOIN result r
on s.studentno=r.studentno
WHERE studentresult>=80
and
subjectno=(
SELECT subjectno from `subject` WHERE subjectname='高等数学-1'
)
-- 查询参加高等数学-1分数不小于80的学生的学号,姓名
-- 嵌套查询(由里及外)
SELECT studentno,studentname
from student
WHERE studentno in(
SELECT studentno FROM result WHERE studentresult>=80
and
subjectno=(
SELECT subjectno from `subject` WHERE subjectname='高等数学-1'
))
-- 查询参加高等数学-1,前5名同学的信息(学号,姓名,成绩)
-- 联表查询
SELECT s.studentno,studentname,studentresult
from student s
INNER JOIN result r
on s.studentno=r.studentno
INNER JOIN `subject` sub
ON r.subjectno=sub.subjectno
WHERE subjectname='高等数学-1'
ORDER BY studentresult DESC
LIMIT 0,5
-- 查询参加高等数学-1,前5名同学的信息(学号,姓名,成绩)
-- 子查询
SELECT s.studentno,studentname,studentresult
from student s
INNER JOIN result r
on s.studentno=r.studentno
WHERE subjectno= (
SELECT subjectno from `subject` WHERE subjectname='高等数学-1'
)
ORDER BY studentresult DESC
LIMIT 0,5
4.7分组
-- 查询学员的各科的总分,平均分,最高分,最低分的信息(学号,姓名,科目名称)平均分大于80
SELECT subjectname,SUM(studentresult) 总分,MIN(studentresult) 最低分,AVG(studentresult) 平均分,MAX(studentresult) 最高分
from result r
INNER JOIN `subject` sub
on sub.subjectno=r.subjectno
GROUP BY r.subjectno
HAVING 平均分>80 -- 表示筛选条件,必须是在过滤分组之后再筛选
5.函数
5.1常用函数(不常用)
5.2聚合函数
函数名 | 描述 |
---|
SUM() | 求和 | AVG() | 求平均值 | COUNT() | 求总数 | MAX() | 求最大值 | MIN() | 求最小值 |
SELECT SUM(studentresult) as 总和 from result
SELECT AVG(studentresult) as 平均值 from result
SELECT MAX(studentresult) as 最大值 from result
SELECT MIN(studentresult) as 最小值 from result
-- 求指定字段的总条数。不会查出null的值
SELECT count(studentresult) as 最小值 from result
-- 求表中所有数据的总条数。会查出null的值
SELECT count(*) as 最小值 from result
-- 求表中所有数据的总条数。会查出null的值
SELECT count(1) as 最小值 from result
5.3数据库级别的MD5加密(扩展)
md5加密操作是强算法操作和不可逆的
可以在插入sql语句的时候给密码进行加密,这样更安全
解密:md5在线解密破解,md5解密加密 但较为复杂的密码或者是加了特殊符号的或许解密不了
6.事务
要么都成功,要么都失败
四个特性:
-
原子性:不可再分割的工作单位,一个事务中要么都完成,要么都不完成 -
一致性:从一致的状态转换到另一个一致的状态,事务前后的数据保持一致 -
隔离性:屏蔽其他的事务,专注自己的事务,互不干扰 -
持久性:存储在数据库的数据是永久的,事务一旦提交就不可逆,事务未提交就返回到原样
隔离产生的问题
-
脏读:指一个事务读取到了另一个事务未提交的数据 -
不可重复性 -
虚读
set autocommit=1;-- 设置自动提交事务开启,mysql的事务默认是开启的
set autocommit=0;-- 设置自动提交事务关闭
-- 手动关闭自动提交事务
set autocommit=0;
-- 开启事务
START transaction;
-- 如果数据没有问题就提交事务,提交成功,数据一旦提交就持久化
commit;
-- 如果数据出错就不提交事务,回滚到原来的数据
rollback;
-- 再次设置事务开启状态
set autocommit=1;
-- 可以设置保存点
SAVEPOINT ;
-- 可以设置删除保存点
release SAVEPOINT;
-- 查看自动提交开启状态
show variables like 'autocommit';
模拟账户转账
-- 关闭自动提交事务
SET autocommit=0;
-- 开启事务
start transaction;
-- 插入sql语句
UPDATE account SET money=money-500 WHERE `name`='A';
UPDATE account SET money=money+500 WHERE `name`='B';
-- 数据没问题就提交事务,数据一旦提交,就持久化到数据库
COMMIT;
-- 数据有问题就回滚
ROLLBACK;
-- 开启自动提交变成默认状态
set autocommit=1;
navicat的自动提交事务关闭了,导致了修改数据之后事务一直未提交
7.索引
8.权限管理和备份
9.规范数据库设计
10.JDBC
JDBC即Java DataBase Connectivity,java数据库连接 从根本来讲,JDBC是一种规范,它提供的接口,是一套完整的、可移植的访问底层数据库的程序。
JDBC 提供的API可以让JAVA通过API方式访问关系型数据库,执行SQL语句,获取数据
声卡驱动就是用来实现计算机与声卡之间的通信
显卡驱动就是用来实现计算机与显卡之间的通信
...
同样的,jdbc驱动就是用来实现java程序与数据库管理系统之间的通信
执行的顺序和打开可视化数据库一样:
-
注册或加载驱动------->(找到可视化数据库软件) -
获取连接池-------->(打开连接) -
获取用户信息(用户名和密码)和url(符合url协议获取到指定的库名)-------->(输入用户信息和url) -
连接数据库-------->(点击连接) -
获取执行sql语句对象----->(新建查询) -
执行sql语句------>(执行sql语句) -
关闭资源------>(关闭可视化数据库软件)
10.1数据库驱动
声卡,显卡都是通过驱动与操作系统打交道
而前面所说的,数据库是数据库管理系统,也应有一个驱动和java程序建立通信
我们的java程序会通过数据库驱动,和数据库打交道
10.2jdbc
jdbc:sun公司为了简化开发人员的(对数据库的统一)操作,提供了一个(对java操作数据库的)规范
需要导入数据库驱动包
10.3第一个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','zs@sina.com','1980-12-04'),
?(2,'lisi','123456','lisi@sina.com','1981-12-04'),
?(3,'wangwu','123456','wangwu@sina.com','1979-12-04');
编写测试代码
?
package com.kuangstudy.lesson01;
??
?import java.sql.*;
??
?public class JDBCFirstDemo {
? ? ?public static void main(String[] args) throws ClassNotFoundException, SQLException {
? ? ? ? ?//1.加载驱动
? ? ? ? ?Class.forName("com.mysql.cj.jdbc.Driver");
? ? ? ? ?//2.用户信息和url ? useSSL = true值防止对数据库的随意修改,应设置为false
? ? ? ? ?String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&&useSSL=false";
? ? ? ? ?String username = "root";
? ? ? ? ?String password = "123456";
? ? ? ? ?//3.获取连接对象 connection表示已经连接到数据库
? ? ? ? ?Connection connection = DriverManager.getConnection(url, username, password);
? ? ? ? ?//4.获取执行sql对象
? ? ? ? ?Statement statement = connection.createStatement();
??
? ? ? ? ?//5.执行sql语句 ,返回一个结果集
? ? ? ? ?String sql = "SELECT * from users";
? ? ? ? ?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("================");
? ? ? ? }
? ? ? ? ?//6.关闭资源
? ? ? ? ?statement.close();
? ? ? ? ?connection.close();
? ? ? ? ?resultSet.close();
? ? }
?}
操作jdbc的执行步骤:
-
加载驱动 -
获取连接对象,连接数据库 -
获取执行sql对象 -
返回结果 -
关闭资源
DriverManager
我们操作Driver和获取connection对象都是交给DriverManager统一管理,DriverManager可以注册和删除加载的驱动
static {
? ? ? ? ?try {
? ? ? ? ? ? ?DriverManager.registerDriver(new Driver());
? ? ? ? } catch (SQLException var1) {
? ? ? ? ? ? ?throw new RuntimeException("Can't register driver!");
? ? ? ? }
? ? }
?Connection connection = DriverManager.getConnection(url, username, password);
url
url路径是指定到哪个数据库,并设置SSL(安全套接层)为false
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&&useSSL=false";
connection
此时,获取到的connection是含有指定数据库
Connection connection = DriverManager.getConnection(url, username, password);
statement
现在就可以获取编译sql的对象
?Statement statement = connection.createStatement();
resuleMap
?statement.executeQuery(sql); ?//用于查询语句,返回的是一个结果集
?statement.execute(sql);//用于任何一个sql语句
?statement.executeUpdate(sql); ?//用于添加、删除、修改,返回的是一个被影响的行数
10.4 statement
若java程序想要对数据库数据进行增删改查,只需要执行statement,这样可以向数据库发送sql语句。使用executeUpdate方法适合增删改操作,返回的结果是一个被影响的行数,也就是增删改的操作对数据库表几行数据发生了变化
statement.executeQuery()用于查询数据库表的数据,返回的是一个结果集,数据被封装到里面
CRUD操作 delete
?package com.kuangstudy.lesson01;
??
?import java.sql.Connection;
?import java.sql.DriverManager;
?import java.sql.SQLException;
?import java.sql.Statement;
??
?public class TestDelete {
? ? ?public static void main(String[] args) throws ClassNotFoundException, SQLException {
? ? ? ? ?//1.加载驱动
? ? ? ? ?Class.forName("com.mysql.cj.jdbc.Driver");
? ? ? ? ?//2.连接数据库
? ? ? ? ?String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false";
? ? ? ? ?String username = "root";
? ? ? ? ?String password = "123456";
? ? ? ? ?Connection connection = DriverManager.getConnection(url, username, password);
? ? ? ? ?//3.获取执行sql对象
? ? ? ? ?Statement statement = connection.createStatement();
? ? ? ? ?String sql = "DELETE from users WHERE id=2 ";
? ? ? ? ?int i = statement.executeUpdate(sql);
? ? ? ? ?if (i > 0){
? ? ? ? ? ? ?System.out.println("删除成功");
? ? ? ? }
? ? ? ? ?//4.关闭资源
? ? ? ? ?statement.close();
? ? ? ? ?connection.close();
? ? }
?}
CRUD操作 insert
?
Statement statement = connection.createStatement();
? ? ? ? ?String sql = "INSERT INTO users(`name`,`password`,`email`,`birthday`) VALUES('zhangsan','232322','zhangsan@163.com','1995-08-06'),('lisi','232322','lisi@163.com','2002-08-06'),('wangwu','232322','wangwu@163.com','1989-08-06'),('hanqing','232322','1438617560@qq.com','1995-08-06') ";
? ? ? ? ?int i = statement.executeUpdate(sql);
? ? ? ? ?if (i > 0){
? ? ? ? ? ? ?System.out.println("添加成功");
? ? ? ? }
CRUD操作 update
Statement statement = connection.createStatement();
? ? ? ? ?String sql = "UPDATE users set `name`= '汉青' WHERE id=9 ";
? ? ? ? ?int i = statement.executeUpdate(sql);
? ? ? ? ?if (i > 0){
? ? ? ? ? ? ?System.out.println("修改成功");
? ? ? ? }
提取工具类
建立一个util包,里面用来存放相同操作的代码,包括:
另外,得新建一个properties文件,放在src文件夹下,用于存放用户信息以及加载驱动的实现类,包括
创建工具类
?package com.kuangstudy.lesson02.util;
??
?import java.io.IOException;
?import java.io.InputStream;
?import java.sql.*;
?import java.util.Properties;
??
?public class JdbcUtil {
? ? ?private static String driver=null;
? ? ?private static String username=null;
? ? ?private static String password=null;
? ? ?private static String url=null;
? ? ?static {
? ? ? ? ?try {
? ? ? ? ? ? ?InputStream rs = JdbcUtil.class.getClassLoader().getResourceAsStream("db.properties");
? ? ? ? ? ? ?Properties properties = new Properties();
? ? ? ? ? ? ?properties.load(rs);
? ? ? ? ? ? ?driver = properties.getProperty("driver");
? ? ? ? ? ? ?username = properties.getProperty("username");
? ? ? ? ? ? ?password = properties.getProperty("password");
? ? ? ? ? ? ?url = properties.getProperty("url");
? ? ? ? ? ? ?//驱动只用加载一次
? ? ? ? ? ? ?Class.forName(driver);
? ? ? ? } catch (IOException | 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 set) {
? ? ? ? ?if (set != null){
? ? ? ? ? ? ?try {
? ? ? ? ? ? ? ? ?set.close();
? ? ? ? ? ? } catch (SQLException e) {
? ? ? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? ?if (statement != null){
? ? ? ? ? ? ?try {
? ? ? ? ? ? ? ? ?statement.close();
? ? ? ? ? ? } catch (SQLException e) {
? ? ? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? ?if (connection != null){
? ? ? ? ? ? ?try {
? ? ? ? ? ? ? ? ?connection.close();
? ? ? ? ? ? } catch (SQLException e) {
? ? ? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
? ? }
?}
CRUD操作
?package com.kuangstudy.lesson02;
??
?import com.kuangstudy.lesson02.util.JdbcUtil;
??
?import java.sql.Connection;
?import java.sql.SQLException;
?import java.sql.Statement;
??
?public class TestDelete {
? ? ?public static void main(String[] args) {
? ? ? ? ?Connection connection = null;
? ? ? ? ?Statement statement = null;
? ? ? ? ?try {
? ? ? ? ? ? ?connection = JdbcUtil.getConnection();
? ? ? ? ? ? ?statement = connection.createStatement();
? ? ? ? ? ? ?String sql = "DELETE from users WHERE id=9";
? ? ? ? ? ? ?int i = statement.executeUpdate(sql);
? ? ? ? ? ? ?if (i > 0){
? ? ? ? ? ? ? ? ?System.out.println("删除成功!");
? ? ? ? ? ? }
? ? ? ? } catch (SQLException e) {
? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? }finally {
? ? ? ? ? ? ?JdbcUtil.release(connection,statement,null);
? ? ? ? }
? ? }
?}
?public class TestInsert {
? ? ?public static void main(String[] args) {
? ? ? ? ?Connection connection = null;
? ? ? ? ?Statement statement = null;
? ? ? ? ?try {
? ? ? ? ? ? ?connection = JdbcUtil.getConnection();
? ? ? ? ? ? ?statement = connection.createStatement();
? ? ? ? ? ? ?String sql = "INSERT INTO users(`name`,`password`,`email`,`birthday`) VALUES('zhangsan','232322','zhangsan@163.com','1995-08-06'),('lisi','232322','lisi@163.com','2002-08-06'),('wangwu','232322','wangwu@163.com','1989-08-06'),('hanqing','232322','1438617560@qq.com','1995-08-06') ";
? ? ? ? ? ? ?int i = statement.executeUpdate(sql);
? ? ? ? ? ? ?if (i > 0){
? ? ? ? ? ? ? ? ?System.out.println("添加成功!");
? ? ? ? ? ? }
? ? ? ? } catch (SQLException e) {
? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? }finally {
? ? ? ? ? ? ?JdbcUtil.release(connection,statement,null);
? ? ? ? }
? ? }
?}
?public class TestUpdate {
? ? ?public static void main(String[] args) {
? ? ? ? ?Connection connection = null;
? ? ? ? ?Statement statement = null;
? ? ? ? ?try {
? ? ? ? ? ? ?connection = JdbcUtil.getConnection();
? ? ? ? ? ? ?statement = connection.createStatement();
? ? ? ? ? ? ?String sql = "UPDATE users set `name`= '汉青' WHERE id=13";
? ? ? ? ? ? ?int i = statement.executeUpdate(sql);
? ? ? ? ? ? ?if (i > 0){
? ? ? ? ? ? ? ? ?System.out.println("修改成功!");
? ? ? ? ? ? }
? ? ? ? } catch (SQLException e) {
? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? }finally {
? ? ? ? ? ? ?JdbcUtil.release(connection,statement,null);
? ? ? ? }
? ? }
?}
?public class TestSelect {
? ? ?public static void main(String[] args) {
? ? ? ? ?Connection connection = null;
? ? ? ? ?Statement statement = null;
? ? ? ? ?ResultSet resultSet = null;
? ? ? ? ?try {
? ? ? ? ? ? ?connection = JdbcUtil.getConnection();
? ? ? ? ? ? ?statement = connection.createStatement();
? ? ? ? ? ? ?String sql = "select * from `users` where id=13";
? ? ? ? ? ? ?resultSet = statement.executeQuery(sql);
? ? ? ? ? ? ?if (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("=====================");
? ? ? ? ? ? }
??
? ? ? ? } catch (SQLException e) {
? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? }finally {
? ? ? ? ? ? ?JdbcUtil.release(connection,statement,resultSet);
? ? ? ? }
? ? }
?}
问题:而statement编译sql时会出现sql注入问题
模拟登陆操作
?
package com.kuangstudy.lesson02;
??
?import com.kuangstudy.lesson02.util.JdbcUtil;
??
?import java.sql.Connection;
?import java.sql.ResultSet;
?import java.sql.SQLException;
?import java.sql.Statement;
??
?public class SQL注入 {
? ? ?public static void main(String[] args) {
? ? ? ? ?//使用字符串拼接一个or,表示有一个为真则结果为真
? ? ? ? ?login("' or '1=1","232322");
? ? }
? ? ?//用户登录
? ? ?public static void login(String username,String password){
? ? ? ? ?Connection connection = null;
? ? ? ? ?Statement statement = null;
? ? ? ? ?ResultSet resultSet = null;
? ? ? ? ?try {
? ? ? ? ? ? ?connection = JdbcUtil.getConnection();
? ? ? ? ? ? ?statement = connection.createStatement();
? ? ? ? ? ? ?String sql = "select * from `users` where `name`='"+ username+"' and `password`='"+password+"' ";
? ? ? ? ? ? ?resultSet = statement.executeQuery(sql);
? ? ? ? ? ? ?if (resultSet.next()){
? ? ? ? ? ? ? ? ?System.out.println("登录成功");
? ? ? ? ? ? }
? ? ? ? } catch (SQLException e) {
? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? }finally {
? ? ? ? ? ? ?JdbcUtil.release(connection,statement,resultSet);
? ? ? ? }
??
??
? ? }
?}
解决办法,使用preparedstatement方法提前将预编译sql语句,并使用?站位符
10.5 PreparedStatement
PreparedStatement可以防止sql注入问题,效率更高
先进行预编译sql,将要设置的字段值使用占位符
本质:预编译会将传递进来的参数包裹成字符,而单引号会被转义字符转换为空内容,有效的防止sql注入的问题
-
查询
String sql = "select * from users where id=?";
? ? ? ? ? ? ?pst = connection.prepareStatement(sql);//预编译语句
? ? ? ? ? ? ?pst.setInt(1,14);
? ? ? ? ? ? ?ResultSet resultSet = pst.executeQuery();
? ? ? ? ? ? ?while (resultSet.next()){
? ? ? ? ? ? ? ? ?System.out.println(resultSet.getString("name"));
? ? ? ? ? ? ? ? ?System.out.println(resultSet.getString("password"));
? ? ? ? ? ? ? ? ?System.out.println(resultSet.getString("email"));
? ? ? ? ? ? ? ? ?System.out.println(resultSet.getString("birthday"));
? ? ? ? ? ? }
-
插入
String sql = "insert into users(`name`,`password`,`email`,`birthday`) value (?,?,?,?)";
? ? ? ? ? ? ?pst = connection.prepareStatement(sql);//预编译语句
? ? ? ? ? ? ?pst.setString(1,"王丽萍");//传递参数
? ? ? ? ? ? ?pst.setString(2,"wlp123");
? ? ? ? ? ? ?pst.setString(3,"wangliping@163.com");
? ? ? ? ? ? ?//获得时间戳 java.Date ? 再将时间戳转换为sql.Date下面的时间
? ? ? ? ? ? ?pst.setDate(4,new Date(new java.util.Date().getTime()));
? ? ? ? ? ? ?int i = pst.executeUpdate();
-
删除
String sql = "delete from users where id=?";
? ? ? ? ? ? ?pst = connection.prepareStatement(sql);//预编译语句
? ? ? ? ? ? ?pst.setInt(1, 8);//传递参数
? ? ? ? ? ? ?int i = pst.executeUpdate();
? ? ? ? ? ? ?if (i > 0) {
? ? ? ? ? ? ? ? ?System.out.println("删除成功");
-
修改
String sql = "update users set `name`=? ,`password` = ?where id=?";
? ? ? ? ? ? ?pst = connection.prepareStatement(sql);//预编译语句
? ? ? ? ? ? ?pst.setString(1,"李昂");
? ? ? ? ? ? ?pst.setString(2,"liang@163.com");
? ? ? ? ? ? ?pst.setInt(3,6);
? ? ? ? ? ? ?int i = pst.executeUpdate();
-
模拟登录测试
String sql = "select * from `users` where `name`=? and `password`=?";
? ? ? ? ? ? ?statement = connection.prepareStatement(sql);
? ? ? ? ? ? ?statement.setString(1,username);
? ? ? ? ? ? ?statement.setString(2,password);
? ? ? ? ? ? ?resultSet = statement.executeQuery();
10.6使用idea连接数据库
1.打开idea,进入界面
2.选择+号,进入选择MYSQL
3.填写用户信息(和mysql可视化软件一样)?
4.连接成功!
如果出现连接不成功的,看看是否时区问题,若是时区问题,因为数据库默认的时区比当前的时区晚8小时,所以需要在my.ini文件修改设置时区:default-time_zone='+08:00',之后重启mysql,之后再次连接
重启mysql,在命令行输入services.msc打开服务,选中MySQL重启服务
连接成功之后
编写sql的地方
查看数据库的表
??
10.7 Jdbc事务
事务:要么成功,要么失败
ACID原则
-
原子性:要么全部完成,要么都不完成 -
一致性:最终的结果总数据不发生改变 -
隔离性:多个进程互不干扰 -
持久性:数据一旦提交不可逆,持久化到数据库
模拟转账事务
?
//模拟转账
?public class TransferDemo {
? ? ?public static void main(String[] args) throws SQLException {
? ? ? ? ?//connection代表数据库
? ? ? ? ?Connection connection = null;
? ? ? ? ?PreparedStatement st = null;
? ? ? ? ?try {
? ? ? ? ? ? ?connection = JdbcUtil.getConnection();
? ? ? ? ? ? ?//1.手动设置关闭自动提交事务,此时,事务自动开启
? ? ? ? ? ? ?connection.setAutoCommit(false);
? ? ? ? ? ? ?String sql1 = "update account set money = money - 100 where `name` = 'A';";
? ? ? ? ? ? ?st = connection.prepareStatement(sql1);
? ? ? ? ? ? ?st.executeUpdate();
? ? ? ? ? ? ?int num = 1/0;//强制制造问题
? ? ? ? ? ? ?String sql2 = "update account set money = money + 100 where `name` = 'B';";
? ? ? ? ? ? ?st = connection.prepareStatement(sql2);
? ? ? ? ? ? ?st.executeUpdate();
? ? ? ? ? ? ?//2.提交数据
? ? ? ? ? ? ?connection.commit();
? ? ? ? ? ? ?System.out.println("转账成功");
? ? ? ? } catch (SQLException e) {
? ? ? ? ? ? ?//数据出现问题,事务会自动回滚到原来的样子,这里可以不用设回滚
? ? ? ? ? ? ?try {
? ? ? ? ? ? ? ? ?connection.rollback();
? ? ? ? ? ? } catch (SQLException ex) {
? ? ? ? ? ? ? ? ?ex.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }finally {
? ? ? ? ? ? ?JdbcUtil.release(connection,st,null);
??
? ? ? ? }
??
? ? }
问题:每次连接数据库都需要消耗大量的资源,效率低
解决:创建一个连接池。每次应用程序需要从数据库获取数据时,直接从连接池中申请一个连接使用,用完之后连接池回收连接,从而达到连接复用的效果,并减少资源消耗的目的
本质:准备一些预先的资源,过来就直接连接预先准备好的(池化技术)
开源的连接池项目:
使用了这些数据库连接池,我们就不需要编写连接数据库的代码
DBCP
InputStream rs = DBCPUtil.class.getClassLoader().getResourceAsStream("dbcp.properties");
? ? ? ? ? ? ?Properties properties = new Properties();
? ? ? ? ? ? ?properties.load(rs);
? ? ? ? ? ? ?//获取数据源 BasicDataSourceFactory 工厂:用来创建对象的
? ? ? ? ? ? ? ? ?dataSource = BasicDataSourceFactory.createDataSource(properties);
总结:无论用什么连接源,本质还是一样的,DataSource接口不会变,获取connection方法就不会变
|