MySql

连接数据库

命令行连接

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;

联表查询

img

-- =============== 联表查询 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 评论

三大范式

  • 第一范式

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

  • 第二范式

    前提:满足第一范式

    每张表只描述一件事情

  • 第三范式

    满足第一范式和第二范式

    确保数据表中的每一列数据都和主键直接相关,而不能间接相关

规范性和性能的问题:

关联查询的表不能超过三张表

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

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')
  1. 创建一个普通项目

  2. 导入数据库驱动

    1599619001075

  3. 编写测试代码

    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对象
    • 获得返回的结果集
    • 释放连接

代码实现

  1. 提取工具类

    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();
                }
            }
        }
    }
    
    
  2. 编写增删改的方法一样 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注入,并且效率更高

  1. 增删改

    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);
            }
        }
    }
    
  2. 查询

    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连接数据库

1599643216514

连接成功后可以选择数据库

1599643740019

查看表信息:双击

1599643803975

更新数据 修改之后点击上面db按钮

1599643894878

写SQL语句的地方

1599643995077

事务

要么都成功要么都失败

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&amp;characterEncoding=utf8&amp;uesSSL=true&amp;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接口不变,方法就不会变

原文地址:https://www.cnblogs.com/qiudajiang/p/13623538.html