Mysql学习
1、初识Mysql
前端(页面,展示,数据)
后台 (连接点 链接数据库,连接前端(控制,控制视图跳转和,给前端传递数据))
数据库 (存数据 txt、word、Excel)
1.1、为什么学Mysql
- 岗位需求
- 现在的大世界,大数据时代,打的就是信息战,数据站
- 被迫需求:存数据
- 数据库是所有软件体系中,最核心的存在
1.2、什么是数据库
数据库 (DB DateBase)
概念:数据仓库,是一个软件,安装在操作系统(Windows、Linux、mac....)之上的,核心是操作sql,存储的数据在500万以下,以上需要做索引优化
作用:存储数据,管理数据
1.3、数据库分类
关系型数据库: (SQL)
- Mysql、oracle、sql server、DB2、sqlLite
- 通过表和表、行和列之间的关系进行数据的存储
非关系型数据库: (No Sql) Not Only Sql
- Redis、MongDB
- 非关系型数据库,就是存储对象,通过自身的属性来决定
DBMS(数据库管理系统)
- 数据库的管理软件,科学有效的管理我们的数据,维护和获取数据
1.4、MySQL简介
MySQL是一个关系型数据库管理系统
前世:瑞典MySQL AB 公司开发
今生:属于 Oracle 旗下产品
MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。
开源的数据库软件
体积小、速度快、总体拥有成本低,招人的成本也低,所有人都得会
中小型网站、或者大型网站
官网:https://www.mysql.com/
官网下载:https://dev.mysql.com/downloads/mysql/
安装建议:
- 不要安装exe,会注册到注册表,不好卸载
- 尽可能使用压缩安装包
1.5、使用IDEA创建数据库
- 连接数据源 MySQL

- 创建数据源格式

- 新建Schemas


- 新建一张表 student

- 字段 id,name,age

- 查看表
- 添加数据

1.6、连接数据库
命令行连接:
mysql -u root -proot --连接数据库
flush privileges --刷新权限
--所有的语句都必须使用 ; 结尾
show databases; --查看所有的数据库
mysql> use school; --切换数据库
Database changed
show tables; --查看数据库中所有的表
describe student; --显示表中的所有数据
create database westos; --创建数据库
exit; --退出连接
-- 单行注释 (SQL的本来注释)
/*
多行注释
*/
数据库 xxx 语言
DDL 定义
DML 操作
DQL 查询
DCL 控制
2、操作数据库
操作数据库 > 操作表 > 操作字段
MySQL数据库不区分大小写
2.1、
- 创建数据库
create database [if not exists] westos --if not exists判断是否存在,不存在则创建,否则报一个异常,但不报错
- 使用数据库
--tab键的上面,如果表名或者字段名是特殊字符,就使用``括起来
use `school`;
- 删除数据库
drop database [if exists] hello; --if exists判断是否存在,存在则删除,否则报一个异常,但不报错
- 查看所有的数据库
show databases ;
2.2、数据库的列类型
- 数值
- tinyint 十分小的数据 1个字节
- smallint 较小的数据 2个字节
- mediumint 中等大小的字节 3个字节
- int 标准的整数 4个字节 常用
- bigint 较大的数据 8个字节
- float 单精度浮点型 4个字节
- double 双精度浮点型 8个字节
- decimal 字符串形式的浮点数,防止精度缺失,一般用于金融运算
- 字符串
- char 字符串固定大小,0~255
- varchar 可变字符串,0~65535 常用
- tinytext 微型文本, 2^8-1
- text 文本串,2^16-1 保存大文本
- 时间日期
- date YYYY-MM-DD 日期格式
- time HH:mm:ss 时间格式
- datetime YYYY-MM-DD HH:mm:ss 最常用的时间格式
- timestamp 时间戳, 1970/1/1 到现在的毫秒数
- year 年份表示
- null
- 未知,没有值
- 不要用NULL进行运算,结果也是NULL,没有意义
2.3、数据库的字段属性(重点)
Unsigned
- 无符号的整数
- 声明了该列不能为负数
zerofill
- 0填充
- 不足的位数,使用0来填充, int(3) 5 --> 005
自增
- 自动在上一条记录的基础上 + 1(默认)
- 通常用来设置主键,必须是整数
- 可以自定义设置自增的起始值和步长
非空 null, not null
- 假设设置为not null,插入数据的时候必须要填写该字段,否则报错
- null,如果不填写值,默认为null
默认值
- 设置默认的值
- 如果不指定该列的值,就会有默认的值
IDEA中对应的位置:

2.4、创建数据库表
-- 注意点,使用英文(),表的名称和字段尽量使用``括起来
-- if not exists 如果不存在则创建
-- auto_increment 自增
-- 字符串使用''括起来
-- comment 字段的含义
-- 所有的语句最后加 英文逗号,最后一句不用加
-- primary key一般写在最后面,为表级约束,也可以放在行内,为行级约束
-- engine 引擎
-- default charset默认编码
create table if not exists `student`(
`id` int(4) not null auto_increment comment '学号',
`name` varchar(20) 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(50) default '西安' comment '家庭住址',
`email` varchar(15) default null comment '邮箱',
primary key (id)
)engine=innoDB default charset=utf8;
格式:
create table [if not exists] `表名`(
`字段` 列类型 [属性] [索引] [注释],
`字段` 列类型 [属性] [索引] [注释],
.......
`字段` 列类型 [属性] [索引] [注释]
)[表类型] [字符集设置] [注释];
常用命令:
show create database school; -- 查看创建数据库的语句
show create table student; -- 查看创建student表的定义语句
desc student; -- 显示表的结构
2.5、数据库表的类型
-- 关于数据库引擎
/*
INNODB 默认使用的
MYISAM 早些年使用的
*/
| INNODB | MYISAM | |
|---|---|---|
| 事务支持 | 支持 | 不支持 |
| 数据行锁定 | 支持行锁 | 不支持行锁,但支持表锁 |
| 外键约束 | 支持 | 不支持 |
| 全文索引 | 不支持 | 支持 |
| 表空间的大小 | 较大,约为MYISAM的2倍 | 较小 |
常规使用操作:
- MYISAM 节约空间,速度较快
- INNODB 安全性高,支持事务的处理,支持多表多用户操作
物理空间存放的位置:
- 所有的数据库都存放在 data目录下
- 本质还是文件的存储
MySQL引擎在物理文件上的区别:
- InnoDB在数据库表中只有一个*.frm文件,以及上级目录下的ibdata1文件
- MYISAM 对应文件
- *.frm 存储表结构的定义文件
- *.MYD 数据文件
- *.MYI 索引文件
设置数据库表的字符集编码:
charset=utf8
不设置会使用mysql的默认字符集编码,不支持中文
2.6、删除以及修改表
修改表:
-- 修改表名 alter table 旧表名 rename as 新表名;
alter table teacher rename as teacher1;
-- 添加字段 alter table 表名 add 字段名 字段类型;
alter table teacher1 add age int(3);
-- 修改表的字段 (重命名,修改约束)
-- alter table 表名 modify 字段名 字段类型;
alter table teacher1 modify age varchar(20); -- 修改约束
-- alter table 表名 change 旧列名 新列名 字段类型;
alter table teacher1 change age age1 int(3); -- 重命名
-- 删除表的字段 alter table 表名 drop 列名;
alter table teacher1 drop age1;
删除表:
-- 删除表 (判断一下,如果表存在则删除)
drop table if exists teacher1;
所有的删除及创建都建议判断是否存在,防止报错
注意点:
- 表名、字段名尽量使用 `` 括起来
- 注释 -- /**/
- sql关键字大小写不敏感,建议小写
- 所有的符号全部用英文
3、MySQL数据管理
3.1、外键(了解)
create table if not exists `student`(
`id` int(4) not null auto_increment comment '学号',
`name` varchar(20) not null default '匿名' comment '姓名',
`pwd` varchar(20) not null default '123456' comment '密码',
`sex` varchar(2) not null default '男' comment '性别',
`gradeId` int(10) not null comment '年级id' ,
`address` varchar(50) default '西安' comment '家庭住址',
`email` varchar(15) default null comment '邮箱',
primary key (id)
)engine=innoDB default charset=utf8;
drop table `grade`;
create table `grade`(
`gradeId` int(10) not null auto_increment comment '年级id',
`gradeName` varchar(30)not null comment '年纪名称',
primary key(`gradeId`)
)ENGINE=INNODB default charset=utf8;
-- 创建表的时候灭有添加外键
-- constraint 约束名
-- foreign key外键
-- references 引用的哪张表的主键
ALTER table student
add constraint `FK_gradeId` foreign key (`gradeId`) references `grade`(`gradeId`);
-- ALTER table 表名 add constraint 约束名 foreign key (作为外键的列) references 哪张表(哪个列)
以上外键为物理级别的外键,就是数据库级别的外键,不建议使用,避免数据库过多,删除起来麻烦
最佳用法
- 数据表就是单纯的表,只有行和列
- 使用多张表的话,使用外键可由程序去实现
3.2、DML语言(全部记住)
数据库意义:数据存储,数据管理
DML语言:数据操作语言
- Insert
- Updata
- Delete
3.3、添加
insert
-- 插入语句(添加)
-- insert into 表名([字段1,字段2,字段3...]) values ('值1','值2','值3'....);
insert into `grade`(`gradeName`) values ('大四');
-- 由于主键自增,可以省略不写,但是如果不写表的字段,就会一一匹配
insert into `grade` values ('大三'); -- Column count doesn't match value count at row 1
-- 一般写插入语句,必须使字段和数据一一对应
-- 一次性插入多个字段
insert into `grade`(`gradeName`)
values ('大二'),
('大一');
insert into `student`(`name`,`pwd`,`sex`) values ('张三','aaa','男');
insert into `student`(`name`,`pwd`,`sex`)
values ('张三','aaa','男'),('李四','bbb','男'),('王五','ccc','女');
-- 重点是 一 一对应
语法:nsert into 表名([字段1,字段2,字段3...]) values ('值1','值2','值3'....);
注意事项:
- 字段和字段之间使用 英文逗号 隔开
- 字段可以省略,但是后面的值必须要一一对应,不能少
- 可以同时插入多个值,values后面的值用逗号隔开
values(),(),.....
3.4、修改
updata
-- 修改学员姓名,带了条件
update student set name = '爱因斯坦' where id=1;
-- 不指定条件的情况下,会改变所有的表
update `student` set name = '特斯拉';
-- 操作多个字段,使用逗号隔开
update `student` set name = '小行星',email = '15268595@qq.com' where id = 1;
-- 通过多个条件定位数据
update student
set name = '小破孩'
where name = '特斯拉' and sex = '女';
语法:updata 表名 set 字段名 = 新值[,字段名 = 新值,....] where 条件
注意事项:
- 字段名使数据库的列,尽量带上``
- 条件,是为了筛选、定位某些需要修改的记录,如果没有,默认修改所有的列
- 新值,可以是一个值,也可以是变量
3.5、删除
语法: delete from 表名 where 条件
-- 删除数据,避免这样写,会删除这个表内的所有数据
delete from student;
-- 删除数据,删除指定的数据
delete from student where id = 1;
truncate 命令
作用:完全清空一张表,表的结构和索引不会改变
-- 清空student表
truncate `student`
delete 和 truncate 的区别
- 相同点:都会删除表里面的所有数据,不会改变表的结构
- 相同点
- truncate 会重新设置自增列,计数器会归零
- truncate不会影响事务
-- 测试 delete 和 truncate 的区别
create table `test`(
`id` int not null auto_increment,
`coll` varchar(20) not null ,
primary key (`id`)
);
insert into `test`(`coll`) values ('1'),('2'),('3');
delete from `test`; -- 不会影响自增
truncate `test`; -- 自增会归零
delete删除问题
- 如果数据库引擎使用 InnoDB,数据库重启后会从1开始(存在于内存当中,断电即失)
- 如果数据库引擎使用 MyISAM,数据库重启后,从上一个自增量开始(存在于文件当中)
4、DQL查询数据(最重点)
4.1、DQL
DQL:数据查询语言
- 所有的查询操作都用它select
- 简单的、复杂的查询都可以使用
- 数据库中最核心的语言
- 使用最频繁的语句
sql语句,创建四张表:
create database if not exists `school`;
-- 创建一个school数据库
use `school`;-- 创建学生表
drop table if exists `student`;
create table `student`(
`studentno` int(4) not null comment '学号',
`loginpwd` varchar(20) default null,
`studentname` varchar(20) default null comment '学生姓名',
`sex` tinyint(1) default null comment '性别,0或1',
`gradeid` int(11) default null comment '年级编号',
`phone` varchar(50) not null comment '联系电话,允许为空',
`address` varchar(255) not null comment '地址,允许为空',
`borndate` datetime default null comment '出生时间',
`email` varchar (50) not null comment '邮箱账号允许为空',
`identitycard` varchar(18) default null comment '身份证号',
primary key (`studentno`),
unique key `identitycard`(`identitycard`),
key `email` (`email`)
)engine=myisam default charset=utf8;
-- 创建年级表
drop table if exists `grade`;
create table `grade`(
`gradeid` int(11) not null auto_increment comment '年级编号',
`gradename` varchar(50) not null comment '年级名称',
primary key (`gradeid`)
) engine=innodb auto_increment = 6 default charset = utf8;
-- 创建科目表
drop table if exists `subject`;
create table `subject`(
`subjectno`int(11) not null auto_increment comment '课程编号',
`subjectname` varchar(50) default null comment '课程名称',
`classhour` int(4) default null comment '学时',
`gradeid` int(4) default null comment '年级编号',
primary key (`subjectno`)
)engine = innodb auto_increment = 19 default charset = utf8;
-- 创建成绩表
drop table if exists `result`;
create table `result`(
`studentno` int(4) not null comment '学号',
`subjectno` int(4) not null comment '课程编号',
`examdate` datetime not null comment '考试日期',
`studentresult` int (4) not null comment '考试成绩',
key `subjectno` (`subjectno`)
)engine = innodb default charset = utf8;
-- 插入学生数据 其余自行添加 这里只添加了2行
insert into `student` (`studentno`,`loginpwd`,`studentname`,`sex`,`gradeid`,`phone`,`address`,`borndate`,`email`,`identitycard`)
values
(1000,'123456','张伟',0,2,'13800001234','北京朝阳','1980-1-1','text123@qq.com','123456198001011234'),
(1001,'123456','赵强',1,3,'13800002222','广东深圳','1990-1-1','text111@qq.com','123456199001011233');
-- 插入成绩数据 这里仅插入了一组,其余自行添加
insert into `result`(`studentno`,`subjectno`,`examdate`,`studentresult`)
values
(1000,1,'2013-11-11 16:00:00',85),
(1000,2,'2013-11-12 16:00:00',70),
(1000,3,'2013-11-11 09:00:00',68),
(1000,4,'2013-11-13 16:00:00',98),
(1000,5,'2013-11-14 16:00:00',58);
-- 插入年级数据
insert into `grade` (`gradeid`,`gradename`) values(1,'大一'),(2,'大二'),(3,'大三'),(4,'大四'),(5,'预科班');
-- 插入科目数据
insert into `subject`(`subjectno`,`subjectname`,`classhour`,`gradeid`)values
(1,'高等数学-1',110,1),
(2,'高等数学-2',110,2),
(3,'高等数学-3',100,3),
(4,'高等数学-4',130,4),
(5,'C语言-1',110,1),
(6,'C语言-2',110,2),
(7,'C语言-3',100,3),
(8,'C语言-4',130,4),
(9,'Java程序设计-1',110,1),
(10,'Java程序设计-2',110,2),
(11,'Java程序设计-3',100,3),
(12,'Java程序设计-4',130,4),
(13,'数据库结构-1',110,1),
(14,'数据库结构-2',110,2),
(15,'数据库结构-3',100,3),
(16,'数据库结构-4',130,4),
(17,'C#基础',130,1);
4.2、指定查询数据
-- 查询所有学生信息 select 字段 from 表名;
select * from student;
-- 查询指定字段
select studentno,studentname from student;
-- 别名,给查询到的结果起名字,使用 as 关键字
select studentno as 学号,studentname as 学生姓名 from student;
-- concat 拼接字符串,属于函数 concat(a,b) --> ab
select concat('姓名:',studentname) as sname from student;
语法: select 字段,... from 表名;
去重:distinct
作用:去除select查询出来的重复的数据,重复的数据只显示一条
-- 查询有哪些学生参加了考试,就是有成绩的学生
select * from result; -- 查询全部的考试成绩
select studentno from result; -- 查询参加了考试的学生学号
select distinct studentno from result; -- 由于重复项太多了,使用distinct关键词去重
数据库的表达式(列)
select version(); -- 查询版本(函数)
select 100*3-1 as 计算结果; -- 计算结果(表达式)
select @@auto_increment_increment; -- 查询自增的步长 (变量)
-- 给查询出来的成绩 + 1 分
select studentno,studentresult + 1 as studentresult from result;
数据库中的表达式:文本值,列,null,函数,计算表达式,系统变量
select 表达式 from 表名;
4.3、where条件语句
作用:检索数据中符合条件的值
搜索的条件由一个或者多个表达式组成,返回结果为布尔值
模糊查询:like in null not null 关键字
-- =========================== 模糊查询 ================================
-- 查询姓赵的同学
-- like 结合 %(代表0~任意个字符) _(代表1个字符)
select studentname,studentno from student where studentname like '赵%';
-- 查询姓赵的同学,后面只有一个字的
select studentname,studentno from student where studentname like '赵_';
-- 查询名字里面含有 伟 字的同学 %伟% 伟字不知道在何处出现,所以使用%
select studentname,studentno from student where studentname like '%伟%';
-- 使用in关键字,in(具体的一个或者多个值)
-- 查询学号为 1001,1002,1003号学生
select studentname,studentno from student where studentno in(1001,1002,1003);
-- 使用 null 或 not null 关键字
-- 查询地址为空的学生,可能地址字段为null 或者 ''空字符串
select studentname,studentno from student where address = '' or address is null;
-- 查询有出生日期的学生 就是出生生日不为空
select studentname,studentno from student where borndate is not null;
4.4、联表查询
-- ================== 联表查询 join ===============================
-- 查询参加了考试的学生 (学号,姓名,学科编号,成绩)
select * from student;
select * from result;
/* 思路:
1.分析需求,确定需要查询哪些数据,在哪几张表里面
2.确定使用哪种连接查询
3. 确定交叉点,找这些表里面的公共字段
4.判断条件:学生表 studentno = 成绩表 studentno
*/
select s.studentno,studentname,subjectno,studentresult
from student s inner join result r
where s.studentno = r.studentno;
-- right join
select s.studentno,studentname,subjectno,studentresult
from student s right join result r
on s.studentno = r.studentno;
-- left join
select s.studentno,studentname,subjectno,studentresult
from student s left join result r
on s.studentno = r.studentno;
| 操作 | 描述 |
|---|---|
| inner join | 两个表中有一个字段名相同,类型也必须相同,则返回结果 |
| right join | 会将右表的所有记录返回,即使左表没有匹配的记录,未匹配的记录赋值为null |
| left join | 会将左表的所有记录返回,即使右表没有匹配的记录,未匹配的记录赋值为null |
-- ================== 联表查询 join ===============================
-- 查询参加了考试的学生 (学号,姓名,学科编号,成绩)
select * from student;
select * from result;
/* 思路:
1.分析需求,确定需要查询哪些数据,在哪几张表里面
2.确定使用哪种连接查询
3. 确定交叉点,找这些表里面的公共字段
4.判断条件:学生表 studentno = 成绩表 studentno
*/
-- join(连接的表) on(判断的条件) 连接查询
-- where 等值查询
select s.studentno,studentname,subjectno,studentresult
from student s inner join result r
where s.studentno = r.studentno;
-- right join
select s.studentno,studentname,subjectno,studentresult
from student s right join result r
on s.studentno = r.studentno;
-- left join
select s.studentno,studentname,subjectno,studentresult
from student s left join result r
on s.studentno = r.studentno;
-- 查询缺考的同学 左连接查询出来的结果为 所有学生匹配的成绩,如果哪个成绩为空,就是未参加考试的
select s.studentno,studentname,subjectno,studentresult
from student s left join result r
on s.studentno = r.studentno
where studentresult is null;
-- 查询参加考试的同学信息(学号,学生姓名,科目名,成绩)
/* 思路:
1.分析需求,确定需要查询哪些数据,在哪几张表里面 student、result、subject
2.确定使用哪种连接查询 左连接
3. 确定交叉点,找这些表里面的公共字段
4.判断条件:右联表查出来的结果里面有subjectNo,使用查询出来subjectNo和subject表再联表查询,就会得到课程名
*/
select s.studentno,studentname,subjectname,studentresult
from student s right join result r
on s.studentno = r.studentno
-- 右联表查出来的结果里面有subjectNo,使用查询出来subjectNo和subject表再联表查询,就会得到课程名
inner join subject sub
on r.subjectno = sub.subjectno
-- 格式:
-- 查询哪些数据 : select ...
-- 从哪些表中去查 : from 表名 inner/right/left join 连接的表 on 交叉条件
-- 假设是多张表查询,先查询两张表,再慢慢增加
-- 查询学生所属的年级(学号,姓名,年纪名称)
select studentname,studentno,gradename
from student s inner join grade g
on s.gradeid = g.gradeid;
-- 查询科目所属的年级
select subjectname,gradename
from subject s inner join grade g
on s.gradeid = g.gradeid;
-- 查询参加高等数学-1考试的同学信息(学号,学生姓名,科目名,成绩)
select s.studentno,studentname,subjectname,studentresult
from student s right join result r
on s.studentno = r.studentno
inner join subject sub
on r.subjectno = sub.subjectno
where sub.subjectname = '高等数学-1'
自链接:就是自己的表和自己的表连接,核心:一张表拆成两张一样的表
一张表里面含有包含关系的两个字段,pid包含categoryid ,才能使用自链接
-- 查询父子信息:将一张表拆分为两张表
select a.categoryname '父栏目',b.categoryname '子栏目'
from category a,category b
where a.categoryid = b.pid;
4.5、分页和排序
排序:
-- 排序 :升序 asc 降序 desc
-- order by 哪个字段排序 怎么排
-- 根据查询出来的结果,按成绩降序进行排列
select s.studentno,studentname,subjectname,studentresult
from student s right join result r
on s.studentno = r.studentno
inner join subject sub
on r.subjectno = sub.subjectno
order by studentresult desc;
分页:
为什么要有分页?
假设有100万条数据,分页可以缓解数据库压力,给人的体验更好
-- 分页,每页只显示五条数据
-- 语法: limit startIndex,pageSize
select s.studentno,studentname,subjectname,studentresult
from student s right join result r
on s.studentno = r.studentno
inner join subject sub
on r.subjectno = sub.subjectno
order by studentresult desc
limit 0,5;
-- 第一页 limit 0~5 5*(1-1)
-- 第二页 limit 5~5 5*(2-1)
-- 第三页 limit 10~5 5*(3-1)
-- .....
-- 第N页 limit 10~5 pageSize*(N-1)
-- pageSize:页面大小
-- pageSize*(N-1) :起始下标
-- N :当前页
-- 总页数 = 数据总数/当前页
分页和排序应用
-- 查询 高等数学-1 课程成绩排名前十的,并且分数大于80的学生信息 (姓名,学号,课程名,成绩)
select studentname,s.studentno,subjectname,studentresult
from student s inner join result r
on s.studentno = r.studentno
inner join subject sub
on r.subjectno = sub.subjectno
where studentresult > 80 and subjectname = '高等数学-1'
order by studentresult desc
limit 0,10;
4.6、子查询
where(这个值是经过计算得出来的)
本质:相当于在where 下面在嵌套一个查询
-- =============== where ===================
-- 1.查询高等数学-4的所有考试结果(学号,考试编号,成绩),成绩降序排列
-- 方式一:联表查询
select studentno,s.subjectno,studentresult
from result r inner join subject s
on r.subjectno = s.subjectno
where subjectname = '高等数学-4'
order by studentresult desc;
-- 方式二:子查询(由里及外)
select studentno,subjectno,studentresult
from result r
where r.subjectno in
(select subjectno from subject where subjectname = '高等数学-4')-- 子查询语句就是为了确定 高等数学-4 的课程号
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 ,但是student表和result表没有 科目名称,只有科目编号,所以还得知道 高等数学-1 的编号
select s.studentno,studentname,studentresult
from student s inner join result r
on s.studentno = r.studentno
where studentresult >= 80 and subjectno = (select subjectno from subject where subjectname = '高等数学-1');
-- 如果使用联表查询
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' and studentresult >= 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'
)
);
4.7、分组和过滤
-- 查询不同课程的平均分,最高分,最低分
-- 核心:不同的课程
select subjectname,avg(studentresult) as 平均分,max(studentresult) as 最高分,
min(studentresult) as 最低分 from result inner join
subject s on result.subjectno = s.subjectno
group by result.subjectno -- 通过什么来分组,在这一组里面再去计算平均值,最高分,最低分
having avg(studentresult) >= 80; -- 分组完成后,每个组还需要满足的次要条件 avg(studentresult) 也可以使用别名
4.8、select语法小结
select 去重 要查询的字段 from 表名 (字段和表名都可以取别名)
xxx join 要连接的表 on 等值判断
where(具体的值或子查询语句)
Group by (通过哪个字段分组)
having (过滤分组后的信息,条件和where一样,只是位置不同)
order by 通过那个字段排序[升序/降序]
limit startIndex,PageSize
5、MySQL函数
5.1、常用函数
-- =========== 常用函数 ==============
-- 数学运算
select abs(-5); -- 绝对值 5
select ceiling(9.2); -- 向上取整 10
select floor(9.9); -- 向下取整 9
select rand(); -- 返回0~1之间的随机数
select sign(); -- 返回参数的符号 0返回0,负数返回-1 正数返回1
-- 字符串函数
select char_length('这个年纪你能睡得着?'); -- 返回字符串的长度 10
select concat('你','管','我'); -- 拼接字符串
-- 从某个位置开始替换多少长度,
-- insert(原字符串,插入的起始下标(从一开始),插入字符串的长度,需要插入的字符串)。
select insert('hello,world',1,2,'ss'); -- ssllo,world
select lower('Computer'); -- 大写转小写
select upper('Computer'); -- 小写转大写
select instr('hello','h'); -- 返回子串第一次出现的索引,如果没有找到返回 0 结果为 1
select replace('坚持就是胜利','坚持','努力'); -- 替换出现的指定字符串 努力就是胜利
select substr('坚持就是胜利',3,2); -- 返回指定的字符串(原字符串,开始截取位置,截取长度) 结果为 就是
select reverse('123456'); -- 反转字符串 654321
-- 查询姓 张的同学,将张替换成 葬
select replace(studentname,'张','葬') from student where studentname like '张%';
-- 时间和日期函数(重要)
select current_date(); -- 获取当前日期 2021-12-09
select curdate(); -- 获取当前日期 2021-12-09
select now(); -- 获取当前的时间 2021-12-09 13:14:02
select localtime; -- 获取本地时间 2021-12-09 13:14:58
select SYSDATE(); -- 获取系统时间 2021-12-09 13:16:11
select year(now()); -- 年
select month(now()); -- 月
select day(now()); -- 日
select hour(now()); -- 时
select minute(now()); -- 分
select second(now()); -- 秒
-- 系统
select system_user(); -- 当前系统用户 root@localhost
select user(); -- 当前系统用户 root@localhost
select version(); -- 当前版本号 8.0.25
5.2、聚集函数(常用)
| 函数 | 描述 |
|---|---|
| count() | 计数 |
| sum() | 求和 |
| avg() | 平均值 |
| max() | 最大值 |
| min() | 最小值 |
| ..... | .... |
-- =============== 聚合函数 ==================
-- 这三个count()都能够统计个数,查询一张表中有多少个记录
select count(studentname) from student; -- count(字段) 会忽略所有的null值
select count(*) from student; -- count(*) 不会忽略 null 值 本质:计算行数
select count(1) from student; -- count(1)不会忽略 null 值 本质:计算行数
-- 没有主键的话,count(1)查询效率最快,有主键,字段查询效率更快
select sum(studentresult) as 总和 from result;
select avg(studentresult) as 平均分 from result;
select max(studentresult) as 最高分 from result;
select min(studentresult) as 最低分 from result;
5.3、数据库级别的MD5加密(扩展)
什么是MD5?
主要增强算法复杂度和不可逆
具体的值的 MD5 是一样的
ND5网站破解原理,背后有一个字典,输入加密后的值获得加密前的值,如果过于复杂则查找不到
-- ============== 测试MD5加密=================
create table testMd5(
id int not null auto_increment comment '用户id',
name varchar(30) not null comment '用户名',
pwd varchar(50) not null comment '密码',
primary key (id)
)engine = innoDB default charset = utf8;
alter table testmd5 modify pwd varchar(100);
-- 明文密码
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('123456');
6、事务
要么都成功,要么都失败
将一组 SQL 放在一个批次中去执行
事务原则:ACID原则,原子性,一致性,隔离性,持久性
原子性:事务要么都完成,要么都失败
一致性:事务完成后和完成前的状态必须一致
隔离性:多个用户同时操作,主要排除其他事务对本次事物的影响
持久性:在事务提交前出现问题,必须将数据恢复原状,在事务提交后出现问题,数据必须已经持久化到了数据库,也就是说事务一旦提交就不可逆了
隔离性存在的一些问题:
- 脏读:一个事务读取了另一个事务未提交的问题
- 不可重复读:事务在多次读取数据的时候出现了数据前后不一致的问题
- 幻读:一个事务内读取到了其他事务插入的数据
处理事务的流程
-- ============事务===============
-- mysql中默认开启自动提交提交
set autocommit = 0; -- 关闭自动提交事务
set autocommit = 1; -- 开启自动提交事务(是默认的)
-- 手动处理事务
-- 1.关闭自动提交
set autocommit = 0;
-- 2.开启事务
start transaction; -- 标记事务的开始,从这里开始的sql语句,都在同一个事务内
-- 3.1提交: 持久化
commit;
-- 3.2回滚: 回到原来的样子
rollback;
-- 事务结束
-- 4.开启自动提交
set autocommit = 1;
模拟转账场景
-- 模拟转账
create database shop character set utf8 collate utf8_general_ci;
use shop;
create table account(
id int not null auto_increment,
name varchar(50) not null ,
money decimal(8,2) not null ,
primary key (id)
)engine = innoDB default charset utf8;
insert 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'; -- A减去500
update account set money = money + 500 where name = 'B'; -- B加上500
commit; -- 提交事务,数据被持久化
rollback; -- 回滚事务
set autocommit = 1; -- 恢复自动提交
7、索引
索引(Index)是帮助MySQL高效获取数据的数据结构
7.1、索引的分类
- 主键索引 (primary key)
- 唯一的标识,主键不可重复
- 唯一索引 (unique key)
- 避免该列含有重复的数据出现,而多个列都可以标识为 唯一索引
- 常规索引 (key/index)
- 可以用index或者key关键字来设置
- 全文索引 (FullText)
- 快速的定位数据
-- 索引的使用
-- 1.在创建表的时候给字段添加索引
-- 2.使用alter关键字给表添加索引
-- 显示所有的索引信息
show index from student;
-- 给student表添加全文索引 alter table 表名 add 索引类型 索引名(需要添加索引的列名);
alter table student add fulltext index studentName(studentname);
-- explain 分析sql执行的状况
explain select * from student; -- 非全文索引
explain select * from student where match(studentname) against('伟');
7.2、测试索引创建一张表,含有100万条数据
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 NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT = 'app用户表';
-- 插入100万条数据
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),'1515151@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'; -- 1 s 827 ms
explain select * from app_user where name = '用户9999'; -- rows=992778
添加索引之后,再执行上面的操作
-- 索引命名规范 id_表名_字段名
-- create index 索引名 on 表名(字段名);
create index id_app_user_name on app_user(name);
select * from app_user where name = '用户9999'; -- 16 ms
explain select * from app_user where name = '用户9999'; -- rows=1
结论:
创建完索引之后,每一个用户名都有一个唯一定位的index,查询用户名,相当于查询的就是index,能够唯一定位索引只有在数据量非常大的时候才能体现出来,区别十分明显
7.3、索引原则
- 索引不是越多越好
- 不要对经常变动的数据加索引
- 小数据量的表不需要加索引
- 索引一般加在常用来查询的字段上
InnoDB默认的数据结构是 BTree(B树)
mysql索引背后的数据结构及算法原理 参考博客:https://www.cnblogs.com/pandang/p/7359532.html
8、权限管理和备份
8.1、用户管理
SQL命令:用户操作在MySQL.user表中
本质就是对这张表进行增删改查
-- 创建一个用户 create user 用户名 identified by '密码';
create user person identified by '123456';
-- 修改密码(修改当前用户的密码)
set password = password ('111111');
-- 修改密码(修改指定用户的密码) set password for 用户名 = password ('密码');
set password for person = password ('111111');
-- 重命名 rename user 旧用户名 to 新用户名;
rename table testmd5 to test2; -- 给表重命名
rename user person to person2; -- 给用户重命名
-- 用户授权 all privileges所有的权限 *.* 所有的库.所有的表
-- 除了给别人授权的权限
grant all privileges on *.* to person;
-- 查看用户权限
show grants for person2; -- 查看指定用户的权限
show grants for root@localhost; -- 查看root用户的权限
-- ROOT用户的权限:GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
-- 撤销权限 revoke 撤销的权限,在哪个库.哪张表撤销,撤销的哪个用户的
revoke all privileges on *.* from person2;
-- 删除用户
drop user person2;
8.2、数据库备份
为什么备份?
- 保证重要的数据不丢失
- 方便数据转移
MySQL数据库备份的方式
- 拷贝物理文件,MySQL数据库都在data文件下面
- 使用可视化工具手动导出
- 使用命令行导出 命令是 mysqldump
#mysqldump -h 主机名 -u 用户名 -p 密码 数据库 表名 >物理磁盘位置/文件名.sql
C:\Users\lzc>mysqldump -hlocalhost -uroot -proot school student >d:/a.sql
#导出多张表 mysqldump -h 主机名 -u 用户名 -p 密码 数据库 表名1 表名2 ... >物理磁盘位置/文件名.sql
C:\Users\lzc>mysqldump -hlocalhost -uroot -proot school student result >d:/b.sql
#导出数据库 mysqldump -h 主机名 -u 用户名 -p 密码 数据库 >物理磁盘位置/文件名.sql
C:\Users\lzc>mysqldump -hlocalhost -uroot -proot school >d:/c.sql
- 使用命令行导入数据库
# 登陆的情况下
# 切换到需要导入的那个数据库,如果导入的是数据库就不用切换
# source 备份文件
source d:/a.sql
# 未登录的情况下
# mysql -u用户名 -p密码 库名< 备份文件
9、规范数据库设计
9.1、为什么需要设计数据库
当数据库过于复杂的时候,就需要设计了
糟糕的数据库设计:
- 数据冗余
- 数据库插入和删除都是比较复杂的
- 程序运行的性能差
良好的数据库设计:
- 节省内存空间
- 保证数据库完整性
- 方便开发系统
软件开发中关于数据库的设计:
- 需求分析:分析业务和需要处理数据的需求
- 概要设计:设计关系图,一般是 E-R图
设计数据库的步骤(个人博客)
- 收集信息,分析需求
- 用户表(用户的登录注销,用户的个人信息,写博客,博客分类)
- 分类表(文章分类,谁创建的)
- 评论表
- 文章表(文章的信息)
- 友链表(友联信息)
- 自定义表(系统信息,某个关键的字,一些主题)
- 标识实体(把需求落地到每个字段)
- 标识实体 之间的关系
- 写博客 user --> blog
- 创建分类 user--> category
- 关注 user --> user
- 友链 links
- 评论 user --> user --> blog
9.2、数据库的三大范式
为什么需要数据规范化?
- 避免数据重复
- 避免插入异常
- 避免更新异常
- 无法正常显示信息
- 避免删除异常
- 丢失有效信息
三大范式:
- 第一范式(1NF)
- 原子性:每一列的属性不可再分
- 第二范式(2NF)
- 前提是满足第一范式
- 主键能够唯一的确定一条记录,这条记录必须依赖于所有的主键信息,主键里面的一部分无法确定该条信息
- 第三范式(3NF)
- 前提是满足第一、第二范式
- 数据表中的每一条数据都必须与主键相关,而不能与其他的非主键相关,也就是其他非主键不能够去确定任意一个字段
规范性和性能的问题
两者不可兼得
关联查询的表不得超过三张表
- 考虑商业化的需求和目标,基于成本以及用户体验,数据库的性能更加重要
- 在考虑性能的问题时,适当的考虑一下规范性
- 有时候会故意增加一些冗余的字段,将其从多表查询变为单表查询
- 故意增加一些计算列
10、JDBC
10.1、数据库驱动
为了能够使得应用程序能够连接到数据库,需要数据库驱动作为中间件进行连接,应用程序通过数据库驱动和数据库打交道
10.2、JDBC
SUN公司为了简化开发人员对数据库的统一操作,提供了一套Java操作数据库的规范:称为JDBC
这些规范由具体的厂商去做,我们只需要掌握JDBC的接口操作即可
需要导入的数据库驱动依赖:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
10.3、第一个JDBC程序
- 创建一个maven项目
- 导入数据库依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
- 连接创建数据库
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,'zhangsan','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.lzc.lesson01;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
//第一个JDBC程序
public class JdbcFirstDemo {
public static void main(String[] args) throws Exception {
//1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.用户信息,和url
//useUnicode=true 支持中文编码
// useSSL=true 保证安全连接
// characterEncoding=utf8 字符编码格式为utf8
String url = "jdbc:mysql://localhost:3306/jdbcstudy?
useUnicode=true&useSSL=true&characterEncoding=utf8";
String username = "root";
String password = "root";
//3.开始连接,连接成功,返回数据库对象 connection 就代表数据库
Connection connection = DriverManager.getConnection(url, username, password);
//4.获取 执行sql的对象 statement
Statement statement = connection.createStatement();
//5.用得到的对象去 执行sql 可能存在结果,查看返回结果
String sql = "select * from jdbcstudy.users";
//5.1 resultSet 返回的结果集,结果集里卖弄封装了所有查出来的对象
ResultSet resultSet = statement.executeQuery(sql);
//5.2 遍历获得到的结果集,resultSet.getObject(1)查询第几列的数据
while (resultSet.next()){
System.out.println("id:" + resultSet.getObject(1));
System.out.println("name:" + resultSet.getObject(2));
System.out.println("password:" + resultSet.getObject(3));
System.out.println("email:" + resultSet.getObject(4));
System.out.println("birthday:" + resultSet.getObject(5));
}
//6.释放连接
resultSet.close();
statement.close();
connection.close();
}
}
步骤总结:
- 加载驱动
- 连接数据库 DriverManager
- 获取执行sql的对象
- 获得返回的结果集
- 关闭连接
10.4、statement对象
jdbc中的statement对象用于向数据库发送sql,想完成数据库的crud,只需要通过这个对象对数据库发送crud语句即可
statement.executeUpdate(sql)方法,用于向数据库发送增删改语句,返回一个整数,即增删改语句导致的数据库几行数据发生了改变
statement.executeQuery(sql)方法,用于向数据库发送查询语句,返回一个ResultSet对象,结果集
代码实现:
- 提取工具类
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 {
//获取db.properties资源流
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
//加载 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){
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 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();
}
}
}
}
- 编写增删改的方法,都是使用
executeUpdate(sql)方法
增加数据:
public class TestInsert {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
int rows = 0;
try {
connection = JdbcUtils.getConnection(); //获得数据库连接
statement = connection.createStatement(); // 获得执行sql的对象
String sql = "insert into jdbcstudy.users values(4,'zhaoliu','123212','add@qq.com','1995-02-26')";
rows = statement.executeUpdate(sql);
if (rows>0){
System.out.println("插入成功");
}else {
System.out.println("失败");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(connection,statement,null);
}
}
}
删除数据:
public class TestDelete {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
try {
connection = JdbcUtils.getConnection();
statement = connection.createStatement();
String sql = "delete from jdbcstudy.users where id = 4";
int i = statement.executeUpdate(sql);
if (i>0){
System.out.println("删除成功");
}else {
System.out.println("失败");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(connection,statement,null);
}
}
}
更新数据
public class TestUpdate {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
try {
connection = JdbcUtils.getConnection();
statement = connection.createStatement();
String sql = "update jdbcstudy.users set name = '赵六' where id = 4";
int i = statement.executeUpdate(sql);
if (i > 0){
System.out.println("更新成功");
}else {
System.out.println("失败");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(connection,statement,null);
}
}
}
核心就是sql语句的不同,其他内容一摸一样
3. 查询数据,使用executeQuery(sql)方法
public class TestQuery {
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 jdbcstudy.users";
//获取结果集
resultSet = statement.executeQuery(sql);
//遍历打印结果集
//resultSet.next() 如果还有下一个数据则为真
while (resultSet.next()){
System.out.println("id:" + resultSet.getObject(1));;
System.out.println("name:" + resultSet.getObject(2));;
System.out.println("password:" + resultSet.getObject(3));;
System.out.println("email:" + resultSet.getObject(4));;
System.out.println("birthday:" + resultSet.getObject(5));;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(connection,statement,resultSet);
}
}
}
sql注入的问题:就是sql存在漏洞,会被攻击,导致数据泄露
本质就是在原来的sql基础上再拼接一句sql语句(or 语句),导致查询出来的结果与预期结果不符合
//注意:此处命名不规范
public class SQL注入 {
public static void main(String[] args) {
//正常登录
// login("zhangsan","123456");
//拼接sql语句登录,不需要正确的用户名以及密码,就能查询出来所有的数据
login(" ' or '1=1","' or '1=1");
}
public static void login(String username,String password){
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils.getConnection();
statement = connection.createStatement();
String sql = "select * from jdbcstudy.users where NAME = '"+username+"' and PASSWORD = '" + password +"'";
//获取结果集
resultSet = statement.executeQuery(sql);
//遍历打印结果集
//resultSet.next() 如果还有下一个数据则为真
while (resultSet.next()){
System.out.println("name:" + resultSet.getObject("name"));;
System.out.println("password:" + resultSet.getObject("password"));;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(connection,statement,resultSet);
}
}
}
10.5、PreparedStatement对象
PreparedStatement可以防止sql注入,效率更好
- 增加
public class TestInsert {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement pstm = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils.getConnection();
//编写sql语句,使用 ? 占位符,一个?代表一位参数
String sql = "insert into jdbcstudy.users values(?,?,?,?,?)";
//预编译sql,但是不执行
pstm = connection.prepareStatement(sql);
//现在给 pstm对象里面的 ?赋值
pstm.setInt(1,5); //给第一个 ? 赋值为5
pstm.setString(2,"天启");//给第二个 ? 赋值为天启
pstm.setString(3,"155115");
pstm.setString(4,"151115@qq.com");
// java.sql.Date 是数据库层面的时间
// java.util.Date() 是java的时间
//需要将java得到的时间戳转换为数据库的时间
pstm.setDate(5,new java.sql.Date(new java.util.Date().getTime()));
//执行sql ,这里不需要参数
int i = pstm.executeUpdate();
if (i > 0){
System.out.println("插入成功");
}else {
System.out.println("失败");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(connection,pstm,resultSet);
}
}
}
- 删除
public class TestDelete {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement pstm = null;
try {
connection = JdbcUtils.getConnection();
String sql = "delete from jdbcstudy.users where id = ?";
//预编译sql
pstm = connection.prepareStatement(sql);
//设置参数
pstm.setInt(1,5);
//执行
int i = pstm.executeUpdate();
if (i>0){
System.out.println("删除成功");
}else {
System.out.println("失败");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(connection,pstm,null);
}
}
}
- 修改
public class TestUpdate {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement pstm = null;
try {
connection = JdbcUtils.getConnection();
String sql = "update jdbcstudy.users set NAME = ? where id = ?";
//预编译sql
pstm = connection.prepareStatement(sql);
//设置参数
pstm.setString(1,"爱因斯坦");
pstm.setInt(2,4);
//执行
int i = pstm.executeUpdate();
if (i>0){
System.out.println("更新成功");
}else {
System.out.println("失败");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(connection,pstm,null);
}
}
}
- 查询
public class TestSelcte {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement pstm = null;
ResultSet rs = null;
try {
connection = JdbcUtils.getConnection();
//预编译sql
String sql = "select * from jdbcstudy.users where id = ?";
pstm = connection.prepareStatement(sql);
//设置参数
pstm.setInt(1,4);
//执行
rs = pstm.executeQuery();
//处理结果
if (rs.next()){
System.out.println(rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(connection,pstm,rs);
}
}
}
- 防止sql注入
//注意:此处命名不规范
public class SQL注入 {
public static void main(String[] args) {
//正常登录
//login("zhangsan","123456");
//拼接sql语句登录,不需要正确的用户名以及密码
login("\'\' or \'1=1\'","123456");
}
public static void login(String username,String password){
Connection connection = null;
PreparedStatement pstm = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils.getConnection();
//prepareStatement能够防止sql注入的本质就是,会将传递进来的参数当成字符串,不会当作关键字,所以 or 无法使用
//假设其中存在转义字符,比如 ' 会被直接转义
String sql = "select * from jdbcstudy.users where NAME = ? and PASSWORD = ?";
pstm = connection.prepareStatement(sql);
//设置参数
pstm.setString(1,username);
pstm.setString(2,password);
//获取结果集
resultSet = pstm.executeQuery();
//遍历打印结果集
//resultSet.next() 如果还有下一个数据则为真
while (resultSet.next()){
System.out.println("name:" + resultSet.getObject("name"));;
System.out.println("password:" + resultSet.getObject("password"));;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(connection,pstm,resultSet);
}
}
}
10.6、事务
事物的四大特性:ACID原则
- 原子性:要么都成功,要么都失败
- 一致性:事务提交前和提交后的状态不能发生变化
- 隔离性:一个事务在运行的时候必须隔离其他事务,防止数据不一致
- 持久性:事务一旦提交成功,就会持久化到数据库
隔离性的问题:
- 脏读:事务读取到了其他事务未提交的数据
- 不可重复读:事务在重复多次读取数据的时候,前一次读取到的数据和这一次的数据不一致
- 幻读:在一个事务内,读取到了别人插入的数据,导致前后结果不一致
不可重复读和幻读很相似,不过不可重复读读取的是更新的数据,幻读读取的是插入的数据
代码实现:
- 开启事务
connection.setAutoCommit(false); - 一组业务执行完毕,提交事务
- 可以在cache语句中显示的定义回滚,如果不定义,默认就会回滚
public class TestTransaction {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement pstm = null;
ResultSet rs = null;
try {
connection = JdbcUtils.getConnection();
//关闭自动提交,关闭之后会自动开启事务
connection.setAutoCommit(false);
//执行事务
String sql1 = "update jdbcstudy.account set money = money -100 where name = 'A'";
pstm = connection.prepareStatement(sql1);
pstm.executeUpdate();
//手动制造错误,事务会回滚,上一条sql语句不会被持久化到数据库
int i = 1/0;
String sql2 = "update jdbcstudy.account set money = money + 100 where name = 'B'";
pstm = connection.prepareStatement(sql2);
pstm.executeUpdate();
//提交数据
connection.commit();
System.out.println("成功");
} catch (SQLException e) {
try {
connection.rollback();// 如果提交失败,则回滚事务
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}finally {
JdbcUtils.release(connection,pstm,rs);
}
}
}
10.9、数据库连接池
从数据库连接 --> 执行完毕 --> 释放资源 频繁的这种过程中是十分浪费资源的,所以有了池化技术
池化技术:准备一些预先的资源,连接的时候就连接这些预先准备好的
编写连接池,只需要实现DataSource接口
常用的开源数据源实现:DBCP,C3P0,Druid(阿里巴巴的)
使用了 这些数据库连接池之后,在代码中就不需要编写连接数据库的代码了
DBCP:
- 需要导入的依赖
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
- DBCP工具类
public class JdbcUtils_dbcp {
public static DataSource dataSource = null;
static {
try {
//获取db.properties资源流
InputStream in = JdbcUtils_dbcp.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
//加载 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 connection, Statement statement, ResultSet resultSet){
if (resultSet != null){
try {
resultSet.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();
}
}
}
}
- dbcp的使用
public class TestDBCPInsert {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
int rows = 0;
try {
connection = JdbcUtils_dbcp.getConnection(); //获得数据库连接
statement = connection.createStatement(); // 获得执行sql的对象
String sql = "insert into jdbcstudy.users values(4,'zhaoliu','123212','add@qq.com','1995-02-26')";
rows = statement.executeUpdate(sql);
if (rows>0){
System.out.println("插入成功");
}else {
System.out.println("失败");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils_dbcp.release(connection,statement,null);
}
}
}
c3p0:
- 需要导入的依赖
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/mchange-commons-java -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>mchange-commons-java</artifactId>
<version>0.2.19</version>
</dependency>
- c3p0工具类
public class JdbcUtils_c3p0 {
public static ComboPooledDataSource dataSource = null;
static {
try {
//代码写法,往数据源里面手动设置数据,不推荐
/*dataSource = new ComboPooledDataSource();
dataSource.setDriverClass();
dataSource.setUser();
dataSource.setPassword();
dataSource.setJdbcUrl();
dataSource.setMaxPoolSize();
dataSource.setMinPoolSize();
dataSource.setMaxIdleTime();*/
//配置文件写法,直接去读取xml文件即可
dataSource = new ComboPooledDataSource();
}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 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();
}
}
}
}
- c3p0的使用
public class TestInsert {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
int rows = 0;
try {
connection = JdbcUtils_c3p0.getConnection(); //获得数据库连接
statement = connection.createStatement(); // 获得执行sql的对象
String sql = "insert into jdbcstudy.users values(5,'zhaoliu','123212','add@qq.com','1995-02-26')";
rows = statement.executeUpdate(sql);
if (rows>0){
System.out.println("插入成功");
}else {
System.out.println("失败");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils_c3p0.release(connection,statement,null);
}
}
}

浙公网安备 33010602011771号