连接数据库
命令行连接
mysql -uroot -pmyPass@123456
创建数据库
CREATE TABLE [IF NOT EXISTS] `表名`(
`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
......
`字段名` 列类型 [属性] [索引] [注释]
)[表类型][字符集][注释]
常用命令
SHOW CREATE DATABASE school; -- 查看创建数据库的语句
SHOW CREATE TABLE student; -- 查看student数据表的定义语句
DESC student; -- 显示student表的结构
引擎
-- 关于数据库引擎
/*
INNODB 默认使用
MYISAM 早些年使用
*/
MYISAM | INNODB | |
---|---|---|
事务支持 | 不支持 | 支持 |
数据行锁定 | 不支持 | 支持 |
外键约束 | 不支持 | 支持 |
全文索引 | 支持 | 不支持 |
表空间的大小 | 较小 | 较大(约为两倍) |
常规使用操作:
- MYISAM 节约空间 速度较快
- INNODB 安全性高,支持事务处理,多表多用户操作
在物理空间存在的位置
所有的数据库文件都在data目录下,一个文件夹对应一个数据库
本质还是文件的存储!
MySql引擎在物理文件上的区别
- INNODB 在数据库表中只有一个 *.frm文件,以及上级目录下的idtada1文件
- MYISAM 对应的文件
- *.frm 表结构的定义文件
- *.MYD 数据文件(data)
- *.MYI 索引文件(index)
设置数据表的字符集编码
CHARSET=UTF8
不设置的话会是mysql默认的字符集编码 (不支持中文)
修改和删除表
-- 修改表名ALTER TABLE 旧表名 RENAME AS 新表名;
ALTER TABLE student RENAME AS teacher;
-- 增加字段 ALTER TABLE 表名 ADD 字段名 列属性[];
ALTER TABLE teacher ADD age INT(11);
-- 修改表的字段(重命名/修改约束)
-- ALTER TABLE 表名 MODIFY 字段名 列属性[];
ALTER TABLE teacher MODIFY age VARCHAR(11); -- 修改字段约束,不修改名字
-- ALTER TABLE 表名 CHANGE 旧字段名 新字段名 列属性[];
ALTER TABLE teacher CHANGE age age1 INT(11); -- 修改字段名字,不修改约束
-- 删除表的字段
-- ALTER TABLE 表名 DROP 字段名;
ALTER TABLE teacher DROP age1;
-- 删除表(如果表存在再删除)
DROP TABLE IF EXISTS teacher;
所有的创建和删除操作尽量加上判断,以免报错
Mysql数据管理
-
外键(了解即可)
方式一,在创建表的时候,增加约束(麻烦,比较复杂)
方式二,建表后再添加外键约束
-- 建表的时候没有外键关系 alter table `student` add constraint `FK_gradeid` foreign key(`gradeid`) references `grade`(`gradeid`); -- alter table 表 -- add constraint 约束名 foreign key(作为外键的列) -- references 哪个表(哪个字段)
以上的操作都是物理级别的外键,数据库级别的外键,不建议使用,避免数据库过多造成困扰
DML语言(全部记住背下来)
数据操作语言
- insert
- update
- delete
-
添加insert
insert into 表名(字段名1,字段名2,......]) values('值1','值2'),('值1','值2'),(......)
-- 插入语句(添加) -- insert into 表名(字段名1,字段名2) values('值1'),('值2'),(...) insert into `grade`(`gradename`) values('大四'); -- 由于主键自增我们可以省略(如果不写表的字段,他就会一一匹配) -- 一半血插入语句,我们一定要数据和字段一一对应 -- 插入多个字段 insert into `student`(`name`,`sex`) values('张三','男'),('李四','男');
-
修改update
update 修改谁(条件) set 原来的值=新值
-- 修改学生的名字 带条件 update `student` set `name`='大帅' where id=1; -- 不指定条件情况下 会改动所有 update `student` set `name`='大帅'; -- 修改多个属性 属性之间逗号隔开 update `student` set `name`='大江',`sex`='女' where id=1; -- 语法 -- update 表名 set 字段名1=值1,字段名2=值2,...... where [条件] -- 通过多个条件定位数据 无上限 update `student` set `name`='邱大江',`sex`='男' where `name`='大帅' and `sex`='男';
操作符 | 含义 | 范围 | 结果 |
---|---|---|---|
= | 等于 | ||
<>或!= | 不等于 | ||
> | |||
< | |||
>= | |||
<= | |||
between 2 an 5 | 在某个范围内,闭合区间 | [2,5] | |
and | && | 5>1 and 1>2 | false |
or | || | 5>1 and 1>2 | true |
-
删除
-
delete命令
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(4) not null auto_increment, `name` varchar(20) not null, primary key(`id`) )engine=innodb default charset=utf8 insert into `test`(`name`) values('qiu'),('da'); delete from `test`; -- 不会影响自增 truncate table `test`; -- 自增会归零
delete删除的问题:重启数据库,现象:
- InnoDB : 自增列会从1开始(存在内存中的,断电即失)
- MyISAM :继续从上一个自增量开始(存在文件中的,不会丢失)
-
DQL查询数据(最重点)
DQL:数据查询语言
- 所有的查询操作都用它 Select
- 简单的和复杂的它都能做
- 数据库最核心的语言,最重要的语句
- 使用频率最高
指定查询字段
-- 查询全部学生 select 字段 from 表
select * from student;
-- 查询指定字段
select `studentno`,`studentname` from `student`;
-- 使用别名,给结果起一个名字 as 可以给字段起别名,也可以给表起别名
select `studentno` as 学号,`studentname` as 学生姓名 from `student` as 学生;
-- 函数 Concat(a,b)
select CONCAT('姓名:',`studentname`) as 新名字 from `student`;
去重:distinct
取除重复的数据,只显示一条
-- 查询一下哪些同学参加了考试
select * from `result`; -- 查询全部的考试成绩
-- 查询有哪些同学参加了考试
select `studentno` from `result`;
-- 发现重复数据 去重
select distinct `studentno` from `result`;
数据库的列
select VERSION();-- 查看系统版本(函数)
select 100*3-1 as 计算结果; -- 用来计算(计算表达式)
select @@auto_increment_increment; -- 查询自增的步长(变量)
-- 学员考试成绩+1分
select `studentno`,`studentresult`+1 as '提分后' from `result`;
where 条件子句
搜索条件由一个或者多个表达式组成,结果为布尔值
作用:检索数据中符合条件的值
逻辑运算符
运算符 | 语法 | 描述 |
---|---|---|
and && | a and b a && b | 逻辑与 |
or || | a or b a || b | 逻辑或 |
not ! | not a !a | 逻辑非 |
尽量使用英文字母
-- ========================= where =======================
select studentno,studentresult from result;
-- 查询考试成绩再95-100之间的
select studentno,studentresult from result
where studentresult >= 95 and studentresult <= 100;
-- 模糊查询
select studentno,studentresult from result
where studentresult between 70 and 100;
-- 除了1000号学生之外的同学的成绩
select studentno,studentresult from result
where studentno !=1000;
模糊查询 :比较运算符
运算符 | 语法 | 描述 |
---|---|---|
is null | ||
is not null | ||
between | 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......其中的某一个值中,结果为真 |
-- ========================= 模糊查询 =======================
-- 查询姓张的同学
-- =========like结合 %(代表0到任意个字符) _(代表一个字符)================
select `studentno`,`studentname` from `student`
where studentname like '张%';
-- 名字后面只有一个字的
select `studentno`,`studentname` from `student`
where studentname like '张_';
-- 名字后面只有两个字的
select `studentno`,`studentname` from `student`
where studentname like '张__';
-- 查询名字中间有强字的同学
select `studentno`,`studentname` from `student`
where studentname like '%强%';
-- ========= in (具体的一个或者多个值)==========
-- 查询1000,1001号学员
select `studentno`,`studentname` from `student`
where studentno in (1000,1001);
-- 查询在北京的学生
select studentno ,studentname from student
where address in ('北京朝阳','广东深圳');
-- ======== null , not null==========
-- 查询地址为空的学生 null或者''
select studentno,studentname from student
where address='' or address is null;
-- 查询有出生日期的同学
select studentno,studentname from student
where borndate is not null;
联表查询
-- =============== 联表查询 join ===============
-- 查询参加了考试的同学(学号 姓名 科目编号 分数)
select * from `student`;
select * from `result`;
/*
1.分析需求,分析查询的字段来自哪些表(连接查询)
2.确定使用哪种连接查询?
3.确定交叉点(这两个表中哪个数据是相同的)
判断条件:学生表 studentno = 成绩表 studentno
*/
-- ================ inner join =============
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;
-- =========== left join ===============
select s.studentno,studentname,subjectno,studentresult
from student as s
left join result as r
on s.studentno=r.studentno;
操作 | 描述 |
---|---|
inner join | 如果表中至少有一个匹配就返回行 |
left join | 即使右表没有匹配也会从左表中返回所有的值 |
right join | 即使左表没有匹配也会从右表中返回所有的值 |
-- =============== 联表查询 join ===============
-- 查询参加了考试的同学(学号 姓名 科目编号 分数)
select * from `student`;
select * from `result`;
/*
1.分析需求,分析查询的字段来自哪些表(连接查询)
2.确定使用哪种连接查询?
3.确定交叉点(这两个表中哪个数据是相同的)
判断条件:学生表 studentno = 成绩表 studentno
*/
-- join(连接的表) on(判断的条件) 连接查询
-- where 等值查询
-- ================ inner join =============
select s.studentno,studentname,subjectno,studentresult
from student as s
inner 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;
-- =========== left join ===============
select s.studentno,studentname,subjectno,studentresult
from student as s
left join result as r
on s.studentno=r.studentno;
-- 查询缺考的同学
select s.studentno,studentname,subjectno,studentresult
from student as s
left join result as r
on s.studentno=r.studentno
where studentresult is null;
-- 思考题(查询了参加考试的同学的信息:学号,学生姓名,科目名称,分数)
/*
1.分析需求,分析查询的字段来自哪些表 student、result、subject(连接查询)
2.确定使用哪种连接查询?
3.确定交叉点(这两个表中哪个数据是相同的)
判断条件:学生表 studentno = 成绩表 studentno
*/
select s.studentno as 学号,studentname as 姓名,subjectname as 学科,studentresult as 成绩
from student s
right join result r
on r.studentno=s.studentno
inner join subject sub
on sub.subjectno=r.subjectno;
自连接
自己的表和自己的表连接,核心:一张表拆为两张一样的表
父类:
categoryid | categoryname |
---|---|
2 | 信息技术 |
3 | 软件开发 |
5 | 美术设计 |
子类:
pid | categoryid | categoryname |
---|---|---|
3 | 4 | 数据库 |
2 | 8 | 办公信息 |
3 | 6 | web开发 |
5 | 7 | PS技术 |
操作:查询父类对应的子类关系
父类 | 子类 |
---|---|
信息技术 | 办公信息 |
软件开发 | 数据库 |
软件开发 | web开发 |
美术设计 | PS技术 |
-- 查询父子信息: 把一张表看成两张一摸一样的表
select a.`categoryname` as 父栏目,b.`categoryname` as 子栏目
from `category` as a,`category` as b
where a.`categoryid`=b.`pid`;
分页和排序
排序:
-- ================= 分页 limit 和排序 order by ===================
-- 排序: 升序asc 降序desc
-- order by 需要排序的字段 怎么排
-- 查询的结果根据成绩 降序 排序
select s.studentno as 学号,studentname as 姓名,subjectname as 学科,studentresult as 成绩
from student s
right join result r
on r.studentno=s.studentno
inner join subject sub
on sub.subjectno=r.subjectno
order by studentresult desc;
分页
-- 分页,每页显示三条数据
-- limit 起始值,页面的大小 limit 0,3 1~3
-- 第一页 limit 0,3
-- 第二页 limit 3,3
-- 第三页 limit 6,3
-- 第n页 limit (n-1)*3,3 (n-1)*pageSzie,pageSzie
-- pageSzie:页面大小
-- n:当前页
-- 总页数:数据总数/页面大小
select s.studentno as 学号,studentname as 姓名,subjectname as 学科,studentresult as 成绩
from student s
right join result r
on r.studentno=s.studentno
inner join subject sub
on sub.subjectno=r.subjectno
order by studentresult desc
limit 0,3;
练习:
-- 练习
-- 查询高等数学-1 课程成绩排名前十的学生,并且分数要大于80的学生信息
-- 学号,姓名,课程名称,分数
select s.studnetno,studentname,subjectname,studentresult
from student s
right join result r
on s.studntno=r.`studentno`
inner join subject sub
on sub.`subjectno`=r.`subjectno`
where studentresult>80 and subjectname='高等数学-1'
order by studentresult desc
limit 0,10;
子查询
where(这个值是计算出来的)
本质:在where语句中嵌套一个子查询语句
-- ============= where 子查询===============
-- 查询高等数学-1 的所有考试结果(学号,科目编号,成绩)
-- 子查询 (由里及外)
select studentno,r.subjectno,studentresult
from result r
where subjectno=(
select subjectno from subject
where subjectname='高等数学-1'
)
order by studentresult desc;
-- 查询 高等数学-1 分数不小于80分的学生的学号和姓名
select distinct s.studentno,studentname
from student s
inner join result r
on r.`studentno`=s.`studentno`
inner join subject sub
on sub.`subjectno`=r.`subjectno`
where studentresult>=80 and subjectname='高等数学-1'
-- 在上面的基础上增加一个科目,高等数学-1 查询高等数学-1的编号
select distinct s.studentno,studentname
from student s
inner join result r
on r.`studentno`=s.`studentno`
where studentresult>=80 and subjectno=(
select subjectno from `subject`
where `subjectname`='高等数学-1'
)
-- 再改造 多重嵌套 (由里及外)
select studentno,studentname from student where studentno in(
select studentno from result where studentresult >80 and subjectno=(
select subjectno from `subject` where subjectname='高等数学-1'
)
)
分组和过滤
-- 查询不同课程的平均分 最高分 最低分 平均分大于80分
-- 核心:根据不同的课程分组
select subjectname,AVG(studentresult),MAX(studentresult),MIN(studentresult)
from result r
inner join `subject` sub
on r.`subjectno`=sub.`subjectno`
group by r.`subjectno` -- 通过什么字段来分组
having AVG(studentresult)>80 -- 分组后过滤
select小结
完整语法
-- 顺序不能变!!!
-- select 去重 要查询的字段 from 表
-- xxxx join 要连接的表 on 等值判断
-- where(具体的值/子查询语句)
-- group by 字段
-- having 过滤条件
-- order by 字段 排序规则
-- limit 开始位置,每页大小
MySql函数
常用函数
-- =========== 常用函数 ============
-- 数学运算
select ABS(-1) -- 绝对值
select CEILING(1.23) -- 向上取整
select FLOOR(1.23) -- 向下取整
select RAND() -- 返回一个0-1之间的随机数
select SIGN(-123) -- 判断一个数的符号 1:正数 -1:负数
-- 字符串函数
select CHAR_LENGTH('qiudajiang') -- 返回字符串长度
select CONCAT('wo','ai','ni') -- 拼接字符串
-- 插入,替换 从第1某个位置开始替换2长度的字符串
select INSERT('woaibiancheng',1,2,'chaojireai')
-- 大小写转换
select LOWER('Qiu')
select UPPER('dahusia')
-- 查询字符在指定字符串第一次出现的位置(索引)
select INSTR('qiu','i')
-- 替换指定字串的字符
select REPLACE('qiudajiang','jiang','shuai')
-- 截取指定字符串中的一段字符 从第4个开始,截取6个
select SUBSTRING('qiudashai',4,6)
-- 反转字符串
select REVERSE('qiudajiang')
-- 查询姓周的同学,替换姓为李
select REPLACE(studentname,'张','李') from student
where studentname like '张%'
-- 时间和日期函数(记住)
select CURRENT_DATE() -- 获取当前日期
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 USER()
select VERSION()
聚合函数(常用)
函数名称 | 描述 |
---|---|
count() | 计数 |
sum() | 求和 |
avg() | 平均值 |
max() | 最大值 |
min() | 最小值 |
...... | ...... |
-- ============ 聚合函数 ==================
-- 都能够统计表中的数据
select COUNT(studentname) from student -- count(字段) 会忽略所有的null值
select COUNT(*) from student -- count(*) 不会忽略null值 本质 计算行数
select COUNT(1) from result -- count(1) 不会忽略null值 本质 计算行数
select SUM(studentresult) as 总和 from result
select AVG(studentresult) as 平均分 from result
select MAX(studentresult) as 最高分 from result
select MIN(studentresult) as 最低分 from result
数据库级别的加密(扩展)
MD5不可逆
-- =================== 测试MD5 加密 =================
create table `md5test`(
`id` int(4) not null,
`name` varchar(100) not null,
`pwd` varchar(100) not null,
primary key(`id`)
)engine=innodb default charset=utf8
-- 明文密码
insert into `md5test` values(1,'qiu','123456'),(2,'zhang','123456'),(3,'li','123456')
-- 加密
update `md5test` set pwd=MD5(pwd) where id =1
update `md5test` set pwd=MD5(pwd) -- 加密全部的密码
-- 插入时就加密
insert into `md5test` values(4,'qiudajiang',MD5('234235'))
-- 如何校验 将用户传递进来的密码进行MD5加密,然后比对加密后的值
select * from md5test where `name`='qiudajiang' and pwd=MD5('123456')
事务
将一组SQL放在一个批次中执行
事务原则:ACID 原则 原子性,一致性,隔离性,持久性 (脏读,幻读。。。。)
原子性:
要么都成功,要么都失败
一致性
事务前后的数据完整性要保证一致
持久性
事务一旦提交则不可逆,被持久化到数据库中
隔离性
执行事务
事务的隔离性是多个用户并访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,事务之间要相互隔离
-- ====================== 事务 ===============
-- MySql 是默认开启事务自动提交的
-- 手动处理事务
set autocommit = 0 -- 关闭自动提交
-- 事务开启
start transaction -- 标记一个事务的开始 从这个之后的sql都在同一个事务内
insert xxx
insert xxx
-- 提交:持久化(成功)
commit
-- 回滚:回到原来的样子(失败)
rollback
-- 事务结束
set aotocommit = 1 -- 开启自动提交
-- 了解
savepoint 保存点名 -- 设置一个事务的保存点
rollback to savepoint 保存点名 -- 回滚到保存点
release savepoint 保存点名 -- 撤销保存点
模拟场景 :转账
-- 转账
create database shop character set utf8 collate utf8_general_ci
use shop
create 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=utf8
insert into `account`(`name`,`money`)
values ('求大奖',200.00),('阿迪斯',2000.00)
-- 模拟转账:事务
set autocommit = 0 -- 关闭自动提交
start transaction -- 开启一个事务
update account set money = money-100 where `name` = '求大奖'; -- 求大奖减100
update account set money = money+100 where `name` = '阿迪斯'; -- 阿迪斯加100
commit; -- 提交事务,成功就会被持久化,无法回滚
rollback; -- 回滚
set autocommit = 1; -- 恢复默认值
索引
索引的分类
- 主键索引 primary key
- 唯一的标识 主键不可重复 只能有一个列作为主键
- 唯一索引 unique key
- 避免重复的列出现,唯一索引可以重复,多个列都可以标识为唯一索引
- 常规索引 key/index
- 默认的
- 全文索引 fulltext
- 在特定的数据库引擎下才有
- 快速定位数据
基础语法
-- 索引的使用
-- 1、创建表的时候增加索引
-- 2、创建完毕后增加索引
-- 显示索引信息
show index from student
-- 增加全文索引
-- alter table 表名 add 索引类型 索引名(列名)
alter table school.`student` add fulltext index `studentname`(`studentname`)
-- 删除索引
-- ALTER TABLE 表名 DROP INDEX 索引名;
ALTER TABLE schoole.`student` DROP INDEX `studentname`;
-- explain 分析SQL语句执行的状况
explain select * from student ; -- 非全文索引
explain select * from student where MATCH(studentname) against('张伟')
索引原则
- 索引不是越多越好
- 不要对经常变动的数据加索引
- 小数据量的表不需要索引
- 索引一般加在常用来查询的字段上
用户管理
- 用户管理
SQL命令操作
用户表mysql.user
本质:对这张表增删改查
-- 创建用户 create user 用户名 identified by '密码'
create user qiudashuai identified by '123456';
-- 修改密码(修改当前用户密码)
set password = PASSWORD('123456');
-- 修改指定用户密码
set password for qiudashuai = PASSWORD('123456');
-- 重命名 rename user 旧名字 to 新名字
rename user qiudashuai to qiudajiang;
-- 用户授权 all privileges 全部的权限,库,表
-- all privileges 除了给别人授权,其他的都能干
grant all privileges on *.* to qiudajiang;
-- 查看指定用户的权限
show grants for qiudajiang;
-- 查看管理员的权限
show grants for root@localhost;
-- 撤销权限
-- revoke 哪些权限 在哪个库撤销 给谁撤销
revoke all privileges on *.* from qiudajiang
-- 删除用户
drop user qiudajiang;
MySql备份
- 保证重要的数据不丢失
- 数据转移
mysql数据库备份方式
-
直接拷贝物理文件
-
在可视化工具上直接导出
-
使用命令行导出 mysqldump
-- 导出 mysqldump -uroot -pmyPass@123456 school student >D:/a.sql -- 导入 -- 先登录进去 mysql -uroot -pmyPass@123456 -- 导入过程 use school; source d:/a.sql
数据库的规约和三大范式(数据库设计)
糟糕的数据库设计:
- 数据冗余,浪费空间
- 数据插入和删除都会麻烦
- 程序的性能差
良好的数据库设计:
- 节省内存空间
- 保证数据的完整性
- 方便开发系统
软件开发中,关于数据库的设计
- 分析需求:分析业务和需要处理的数据库需求
- 概要设计:设计关系图 E-R图
设计数据库的步骤:(个人博客)
- 收集信息,分析需求
- 用户表
- 分类表(文章分类,谁创建的)
- 文章表(文章的信息)
- 评论表
- 友情链接表
- 自定义表(系统信息,某个关键的字,或者一些主字段) key:value
- 标识实体之间的关系(把需求落地到每个字段)
- user->blog 写博客
- user->category 创建分类
- user->user 关注
- links 友情链接
- user-user-blog 评论
三大范式
-
第一范式
原子性,保证每一列不可再分
-
第二范式
前提:满足第一范式
每张表只描述一件事情
-
第三范式
满足第一范式和第二范式
确保数据表中的每一列数据都和主键直接相关,而不能间接相关
规范性和性能的问题:
关联查询的表不能超过三张表
- 考虑商业化的需求和目标(成本,用户体验!)数据库的性能更加重要
- 在规范性能的问题的时候,需要适当考虑一下规范性
- 故意给某些表增加一些冗余的字段(从多表查询变为单表查询)
- 故意增加一些计算列(从大数据量降低为小数据量的查询)
JDBC(重点)
数据库驱动
驱动:声卡,显卡,数据库
我们的程序会通过数据库驱动,和数据库打交道
SUN公司为了简化开发人员对数据库的统一操作,提供了一个Java操作数据库的规范,俗称 JDBC
java.sql
javax.sql
还需要导入一个数据库驱动包
创建测试数据库
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.qiu.demo; import java.sql.*; //我的第一个JDBC程序 public class jdbcFirst { public static void main(String[] args) throws ClassNotFoundException, SQLException { //1.加载驱动 Class.forName("com.mysql.cj.jdbc.Driver"); //固定写法 加载驱动 //2.用户信息和url //useUnicode=true&&characterEncoding=utf8&&useSSL=true 支持中文编码 设置字符集为utf8 使用安全连接 //serverTimezone=GMT%2B8 时区设置 如果时区不是本地时区会报错 String url="jdbc:mysql://localhost:3306/jdbcstudy" + "?useUnicode=true&&characterEncoding=utf8&&useSSL=true&&serverTimezone=GMT%2B8"; String username="root"; String pwd="myPass@123456"; //3.连接成功 返回数据库对象 Connection 代表数据库 Connection connection = DriverManager.getConnection(url, username, pwd); //4.执行SQL的对象 Statement 执行SQL的对象 Statement statement = connection.createStatement(); //5.执行SQL String sql="select * from users"; ResultSet resultSet = statement.executeQuery(sql); //返回的结果集,结果集中封装了我们全部的查询出来的结果 while(resultSet.next()){ System.out.println("id="+resultSet.getObject("id")); System.out.println("name="+resultSet.getObject("NAME")); System.out.println("pwd="+resultSet.getObject("PASSWORD")); System.out.println("email="+resultSet.getObject("email")); System.out.println("birth="+resultSet.getObject("birthday")); System.out.println("================="); } //6.释放连接 resultSet.close(); statement.close(); connection.close(); } }
步骤总结:
- 加载驱动
- 连接数据库DriverManager
- 获取执行SQL的对象 Statement对象
- 获得返回的结果集
- 释放连接
代码实现
-
提取工具类
public class jdbcUtils { private static String driver = null; private static String url = null; private static String name = null; private static String pwd = 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"); name = properties.getProperty("name"); pwd = properties.getProperty("pwd"); //加载驱动 驱动只需要加载一次 Class.forName(driver); } catch (IOException | ClassNotFoundException e){ e.printStackTrace(); } } // 获取连接 public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url,name,pwd); } //释放连接资源 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(),这里只写一个插入。
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(); //获得SQL的执行对象 String sql = "insert into users(id,`NAME`,`PASSWORD`,`email`,`birthday`)" + "values (4,'大帅','123456','12865@qq.com','2020-01-01')"; int i = statement.executeUpdate(sql); if (i>0){ System.out.println("插入成功"); } } catch (SQLException e) { e.printStackTrace(); } finally { jdbcUtils.release(connection,statement,resultSet); } } }
3.查询
public class TestSelect { public static void main(String[] args) { Connection connection = null; Statement statement = null; ResultSet resultSet = null; try { connection = jdbcUtils.getConnection(); statement = connection.createStatement(); //SQL String sql = "select * from users where id =1"; resultSet =statement.executeQuery(sql); //查询完毕会返回一个结果集 while (resultSet.next()){ System.out.println(resultSet.getString("NAME")); } } catch (SQLException e) { e.printStackTrace(); } finally { jdbcUtils.release(connection,statement,resultSet); } } }
SQL注入的问题
存在漏洞,会被攻击,导致数据泄露
PreparedStatement对象
PreparedStatement可以防止SQL注入,并且效率更高
-
增删改
package com.qiu.demo02; import com.qiu.demo01.utils.jdbcUtils; import java.util.Date; import java.sql.*; public class TestPreparedStatement { public static void main(String[] args) { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { connection = jdbcUtils.getConnection(); //使用问好占位符,代替参数 String sql= "insert into users(id,`NAME`,`PASSWORD`,`email`,`birthday`)" + "values (?,?,?,?,?)"; preparedStatement = connection.prepareStatement(sql); //预编译sql 先写,不执行 //手动给参数赋值 preparedStatement.setInt(1,5); preparedStatement.setString(2,"qiu"); preparedStatement.setString(3,"123456"); preparedStatement.setString(4,"12386@qq.com"); preparedStatement.setDate(5,new java.sql.Date(new Date().getTime())); //执行 int i = preparedStatement.executeUpdate(); if (i>0){ System.out.println("插入成功"); } } catch (SQLException e) { e.printStackTrace(); } finally { jdbcUtils.release(connection,preparedStatement,resultSet); } } }
-
查询
package com.qiu.demo02; import com.qiu.demo01.utils.jdbcUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class TestSelect { public static void main(String[] args) { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try{ connection = jdbcUtils.getConnection(); String sql = "select * from users where id = ?"; preparedStatement = connection.prepareStatement(sql); //预编译 preparedStatement.setInt(1,1); //传递参数 //执行 resultSet = preparedStatement.executeQuery(); while (resultSet.next()){ System.out.println(resultSet.getString("NAME")); } } catch (SQLException e) { e.printStackTrace(); } finally { jdbcUtils.release(connection,preparedStatement,resultSet); } } }
IDEA连接数据库
连接成功后可以选择数据库
查看表信息:双击
更新数据 修改之后点击上面db按钮
写SQL语句的地方
事务
要么都成功要么都失败
ACID原则
原子性:要么全部完成,要么都不完成
一致性:总数不变
隔离性:多个进程互不干扰
持久性:一旦提交,就持久化到数据库
隔离性的问题
脏读:一个事务读取了另一个没有提交的事务
不可重复读:在同一个事务内,重复读取表中的数据,表数据发生了改变
虚读幻读:在一个事务内,读取到了别人插入的数据,导致前后读出的结果不一致
提交事务测试代码
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try{
connection = jdbcUtils.getConnection();
connection.setAutoCommit(false);//关闭数据库自动提交,会自动开启事务
String sql = "update account set money = money - 100 where name = 'A'";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
String sq2 = "update account set money = money + 100 where name = 'B'";
preparedStatement = connection.prepareStatement(sq2);
preparedStatement.executeUpdate();
//业务完毕 提交事务
connection.commit();
System.out.println("操作成功");
} catch (SQLException e) {
try {
connection.rollback(); //如果失败 则回滚事务 如果不写默认也会回滚
} catch (SQLException throwables) {
throwables.printStackTrace();
}
e.printStackTrace();
} finally {
jdbcUtils.release(connection,preparedStatement,resultSet);
}
}
数据库连接池
数据库连接-》执行完毕-》释放 十分浪费资源
池化技术:准备一些预先的资源,过来就连接预先准备好的
最小连接数:10
最大连接数:100 业务最高承载上线
等待超时: 100ms
编写连接池,实现一个接口DataSource
开源数据源实现
dbcp
c3p0
Druid:阿里巴巴
使用了这些数据库连接池之后,我们在项目开发中就不需要编写连接数据库的代码了
DBCP
需要用到的jar包:commons-dbcp-1.4 commons-pool-1.6
实现代码:
- 数据库连接池
//数据库连接池
#连接设置
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true&&serverTimezone=GMT%2B8
username=root
password=myPass@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 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
-
jdbcUtils_DBCP工具类
package com.qiu.Demo5.utils; import org.apache.commons.dbcp.BasicDataSourceFactory; import javax.sql.DataSource; 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_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"); Properties properties = new Properties(); properties.load(inputStream); //创建数据源 工厂模式-->创建对象 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(); } } } }
-
测试类
package com.qiu.Demo5; import com.qiu.Demo5.utils.jdbcUtils_DBCP; import com.qiu.demo01.utils.jdbcUtils; import java.sql.*; public class Test { public static void main(String[] args) { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { connection = jdbcUtils_DBCP.getConnection(); //SQL String sql = "insert into users(id,`NAME`,`PASSWORD`,`email`,`birthday`)"+ "values (?,?,?,?,?)"; preparedStatement =connection.prepareStatement(sql); //手动给参数赋值 preparedStatement.setInt(1,6); preparedStatement.setString(2,"qiudajianga"); preparedStatement.setString(3,"123456"); preparedStatement.setString(4,"12386@qq.com"); preparedStatement.setDate(5,new java.sql.Date(new java.util.Date().getTime()));//查询完毕会返回一个结果集 int i = preparedStatement.executeUpdate(); if (i>0){ System.out.println("插入成功"); } } catch (SQLException e) { e.printStackTrace(); } finally { jdbcUtils.release(connection,preparedStatement,resultSet); } } }
C3P0
需要用到的jar包
mchange-commons-java-0.2.20 c3p0-0.9.5.5
-
c3p0配置文件
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <!-- c3p0的缺省(默认)配置 如果在代码中"ComboPooledDataSource ds=new ComboPooledDataSource();"这样写就表示使用的是c3p0的缺省(默认)--> <default-config> <property name="driverClass">com.mysql.cj.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&characterEncoding=utf8&uesSSL=true&serverTimezone=UTC</property> <property name="user">root</property> <property name="password">myPass@123456</property> <property name="acquiredIncrement">5</property> <property name="initialPoolSize">10</property> <property name="minPoolSize">5</property> <property name="maxPoolSize">20</property> </default-config> </c3p0-config>
-
工具类
public class jdbcUtils_c3p0 { private static ComboPooledDataSource dataSource = null; static { try{ dataSource = new ComboPooledDataSource();//参数默认是mysql } 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(); } } } }
-
测试代码:
public class Test { public static void main(String[] args) { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { connection = jdbcUtils_c3p0.getConnection(); //SQL String sql = "insert into users(id,`NAME`,`PASSWORD`,`email`,`birthday`)"+ "values (?,?,?,?,?)"; preparedStatement =connection.prepareStatement(sql); //手动给参数赋值 preparedStatement.setInt(1,7); preparedStatement.setString(2,"dada"); preparedStatement.setString(3,"123456"); preparedStatement.setString(4,"12386@qq.com"); preparedStatement.setDate(5,new java.sql.Date(new java.util.Date().getTime()));//查询完毕会返回一个结果集 int i = preparedStatement.executeUpdate(); if (i>0){ System.out.println("插入成功"); } } catch (SQLException e) { e.printStackTrace(); } finally { jdbcUtils_c3p0.release(connection,preparedStatement,resultSet); } } }
结论
无论使用上面数据源,本质还是一样的DataSource接口不变,方法就不会变