MySQL基础
1、初识MySQL
javaEE:企业级java开发 Web
前端(页面:展示,数据!)
后台(连接点:连接数据库JDBC,连接前端(控制,控制视图调转,和前端传递数据))
数据库(存数据,Txt,Excel,word)
只会写代码,学好数据库,
操作系统,数据结构与算法!当一个不错的程序员!
离散数学,数字电路,体系结构,编译原理。+实战经验,高级程序员
1.1、为什么学习数据库
1、岗位需求
2、现在的世界,大数据时代,得数据库者得天下
3、被迫需求:存数据 去IOE
4、数据库是所有软件体系中最核心的存在 DBA
1.2、什么是数据库
数据库(DB,DataBase)
概念:数据仓库,软件、安装在操作系统(window,linux,mac,……)之上!SQL,可以存储大量的数据。500万一下顺便存,500万以上要做SQL查询的优化。
作用:存储数据、管理数据
1.3、数据库分类
关系型数据库:(SQL)
- MySQL、Oracle、Sql Server、DB2、SQLlite
- 通过表和表之间,行和列之间的关系进行数据的存储, 学员信息表,考勤表,……
非关系型数据库:(NoSQL) Not Only
- Redis,MongDB
- 非关系型数据库,对象存储,通过对象的自身的属性来决定。
DBMS(数据库管理系统)
- 数据库的管理软件,科学有效的管理我们的数据。维护和获取数据;
- MySQL,数据库管理系统!
1.4、MySQL简介
MySQL是一个关系型数据库管理系统
前世:瑞典MySQL AB公司
今生:属于Oracle旗下产品
MySQL是最好的RDBMS(Realational Database Management System,关系型数据库管理系统)应用软件之一
开源的数据库软件
体积小、速度快、总体拥有成本低、找人成本比较低、所有人必须会
中小型网站、或者大型网站、集群!
安装建议:
1、尽量不要使用exe,注册表
2、尽可能使用压缩包安装
1.5、安装MySQL
教程:https://www.cnblogs.com/godles/p/12202175.html
-
解压
-
把这个包放到自己环境的目录下
-
配置环境变量,把bin目录的绝对路径拷贝到环境变量path中
-
新建mysql配置文件my.ini
注意:目录一定要换成自己的
[mysqld] basedir=D:\Environment\mysql-5.7.19\ datadir=D:\Environment\mysql-5.7.19\data\ port=3306 skip-grant-tables -
启动管理员模式下的CMD,切换到bin目录下,运行所有命令
-
安装MySQL服务
mysqld -install
-
初始化数据库文件(可能需要等一会)
mysqld --initialize-insecure --user=mysql
-
启动mysql
net start mysql
-
通过命令进入mysql
mysql -u root -p
-
修改root密码
update mysql.user set authentication_string=password('111111') where user='root' and Host='localhost';
-
刷新权限
flush privileges;
-
修改my.ini文件,删除或注释最后一句
skip-grant-tables -
重启mysql后即可正常使用,先执行exit命令退出mysql后再执行以下命令
net stop mysqlnet start mysql
-
连接测试,如果连接成功就ok了
mysql -u root -p回车后输入密码mysql -u root -p111111密码紧跟其后按回车也行
安装可能出现的问题:
- 缺少组件.dll
- 命令输错
解决方法:
- sc delete mysql(清空服务)
- 然后再次重新安装
1.6、安装Navicat Premium 15
1、无脑安装
2、注册
3、打开


4、新建一个数据库school

以上选择防止中文乱码
select version() 查看数据库版本
5、新建一张表student
字段:id,name,age

6、查看表
7、添加多条记录

1.7、基本命令
mysql -u root -p11111 --连接数据库
select version(); --查看数据库版本
update mysql.user set authentication_string=password('111111') where user='root' and Host='localhost'; --修改用户密码
flush privileges; --刷新权限
-----------------------------------------------
--所有的语句都要使用;结尾
show databases; --查看所有数据库
use school; --切换数据库 use 数据库名
show tables; --查看数据库中所有的表
describe student; --显示数据库中表的结构
create database mydb; --创建一个数据库
exit; --退出连接
-- 单行注释
/*
多行
注释
*/
数据库xxx语言 CRUD 增删改查
DDL 定义
DML 操作
DQL 查询
DCL 控制
2、操作数据库
操作数据库 > 操作数据库中的表 > 操作数据库中表的数据
mysql关键字不区分大小写
2.1、操作数据库(了解)
-
创建数据库
create database [if not exists] test; -
删除数据库
drop database [if exists] test; -
使用数据库
-- 如果你的表名或者字段名是一个特殊字符,就需要带``(tab键上面的) use `school`; -
查看数据库
show databases; --查看所有的数据库
2.2、数据库的列类型
-
数值
- tinyint 十分小的数据 1个字节
- smallint 较小的数据 2个字节
- mediumint 中等大小的数据 3个字节
- int 标准的整数 4个字节 (常用的,对应java的数据类型为int)
- bigint 较大的数据 8个字节
- float 浮点数 4个字节
- double 浮点数 8个字节 (精度问题!)
- decimal 字符串形式的浮点数 金融计算的时候,一般使用这个
-
字符串
- char 字符串固定大小 0~255
- varchar 可变字符串 0~65535 (常用的,对应java的数据类型为String)
- 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(默认)
- 通常用来设计唯一的主键index,必须是整数类型
- 可以自定义设计主键自增的起始值和步长
- 非空 not null
- 假设设置为not null,如果不给他赋值,就会报错!
- null,如果不填写值,默认就是null!
- 默认
- 设置默认的值!
拓展:
每一个表,都必须存在以下五个字段!未来做项目用的,表示一个记录存在的意义
- id:主键
- version:乐观锁
- is_delete:伪删除
- gmt_create:创建时间
- gmt_update:修改时间
2.4、创建数据库表(重点)
CREATE TABLE IF NOT EXISTS `student` (
`id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
`name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名',
`pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码',
`sex` VARCHAR(2) NOT NULL DEFAULT '男' COMMENT '性别',
`birthday` DATETIME DEFAULT NULL COMMENT '出生日期',
`address` VARCHAR(100) DEFAULT NULL COMMENT '家庭住址',
`email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY(`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
格式
CREATE TABLE [IF NOT EXISTS] `表名` (
`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
......
`字段名` 列类型 [属性] [索引] [注释],
) [表类型][字符集设置][注释]
常用命令
SHOW CREATE DATABASE school --查看创建数据库的语句
SHOW CREATE TABLE student --查看student数据表的定义语句
DESC student --显示表的结构
2.5、数据表的类型
-
关于数据库引擎
-
INNODB:默认使用
-
MYISAM:早些年使用的
MYISAM INNODB 事务支持 不支持 支持 数据行锁定 不支持 支持 外键约束 不支持 支持 全文搜索 支持 不支持 表空间的大小 较小 较大,约为2倍
常规使用操作:
- MYISAM 节约空间,速度较快
- INNODB 安全性高,事务的处理,多表多用户操作
-
-
在物理空间存在的位置
所有的数据库文件都存在data目录下,一个文件夹就对应一个数据库
本质还是文件的存储
MySQL引擎在物理文件上的区别
-
INNODB 在数据库表中只有一个*.frm文件,以及上级目录下的ibdata1文件
-
MYISAM 对应文件
- *.frm 表结构的定义文件
- *.MYD 数据文件(data)
- *.MYI 索引文件(index)
-
-
设置数据库表的字符集彪马
CHARSET=utf8不设置的话,回是mysql默认的字符集编码(不支持中!)
MySQL的默认编码是Latin1,不支持中文
在my.ini中配置默认的编码
character-set-server=uft8
2.6、修改和删除表
-
修改
--修改表名 ALTER TABLE 旧表名 RENAME AS 新表名 ALTER TABLE teacher RENAME AS teacher1; --增加表字段 ALTER TABLE 表名 ADD 字段名 列属性 ALTER TABLE teacher1 ADD age INT(3) --修改表字段 ALTER TABLE teacher1 MODIFY age VARCHAR(3); --修改约束 ALTER TABLE teacher1 CHANGE age age1 INT(3); --字段重命名 --删除表字段 ALTER TABLE teacher1 DROP age1; -
删除
--删除表(如果存在再删除) DROP TABLE IF EXISTS teacher1;
注意点:
- 使用``符号包裹字段名
- sql关键字大小写不敏感,建议大家写小写
- 多有的符号全部用英文
3、MySQL的数据管理
3.1、外键(了解)
-
方式一:在创建表的时候,增加约束(麻烦,比较复杂)
CREATE TABLE `grade`( `gradeid` INT(10) NOT NULL AUTO_INCREMENT COMMENT '年级id', `gradename` VARCHAR(50) NOT NULL COMMENT '年级名称', PRIMARY KEY (`gradeid`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 CREATE TABLE IF NOT EXISTS `student` ( `id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号', `name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名', `pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码', `sex` VARCHAR(2) NOT NULL DEFAULT '男' COMMENT '性别', `birthday` DATETIME DEFAULT NULL COMMENT '出生日期', `gradeid` INT(10) NOT NULL COMMENT '学生的年级', `address` VARCHAR(100) DEFAULT NULL COMMENT '家庭住址', `email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱', PRIMARY KEY(`id`), KEY `FK_gradeid` (`gradeid`), CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade`(`gradeid`) ) ENGINE=INNODB DEFAULT CHARSET=utf8删除有外键关系的表的时候,必须要先删除引用别人的表(从表),再删除被引用的表(主表)
-
方式二:创建表成功后,添加外键约束
CREATE TABLE `grade`( `gradeid` INT(10) NOT NULL AUTO_INCREMENT COMMENT '年级id', `gradename` VARCHAR(50) NOT NULL COMMENT '年级名称', PRIMARY KEY (`gradeid`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 CREATE TABLE IF NOT EXISTS `student` ( `id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号', `name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名', `pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码', `sex` VARCHAR(2) NOT NULL DEFAULT '男' COMMENT '性别', `birthday` DATETIME DEFAULT NULL COMMENT '出生日期', `gradeid` INT(10) NOT NULL COMMENT '学生的年级', `address` VARCHAR(100) DEFAULT NULL COMMENT '家庭住址', `email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱', PRIMARY KEY(`id`), ) ENGINE=INNODB DEFAULT CHARSET=utf8 ALTER TABLE `student` ADD CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade`(`gradeid`);
以上的操作都是物理外键,数据库级别的外键,我们不建议使用!(避免数据库过多造成困扰,这里了解即可~)
-
最佳实践
- 数据库就是单纯的表,只用来存数据,只有行(数据)和列(字段)
- 我们想使用多张表的数据,想使用外键,用程序去实现
3.2、DML语言(全部记住)
数据库的意义:数据存储,数据管理
DML语言:数据操作语言
- insert
- update
- delete
3.3、添加
--insert into 表名([字段1,字段2,字段3,...]) values('值1','值2','值3',...)
INSERT INTO `grade`(`gradename`) VALUES('大一');
INSERT INTO `student`(`name`,`pwd`,`sex`) VALUES('张三','aaaaa','男');
--一次插入多条记录
INSERT INTO `grade`(`gradename`) VALUES('大一'),('大二'),('大三');
INSERT INTO `student`(`name`,`pwd`,`sex`) VALUES('张三','aaaaa','男'),('李四','bbbbbb','男');
语法:insert into 表名([字段1, 字段2, 字段3,...]) values('值1', '值2', '值3'),('值1', '值2', '值3'),('值1', '值2', '值3'),...
注意事项:
- 字段和字段之间使用英文逗号隔开
- 字段是可以省略的,但后面的值必须要一一对应,不能少
- 可以同时插入多条数据,values后面的值,需要使用英文逗号隔开
3.4、修改
UPDATE `student` SET `name` = '王五' WHERE `id` = 1;
--不指定条件,会修改整个表的数据
UPDATE `student` SET `name` = '孙六';
where条件操作符(操作符会返回布尔值):
| 操作符 | 含义 |
|---|---|
| = | 等于 |
| <> 或 != | 不等于 |
| > | 大于 |
| < | 小于 |
| >= | 大于等于 |
| <= | 小于等于 |
| between ... and ... | 在两者单位之间,[2, 5] |
| and | && |
| or | || |
语法:update 表名 set 字段名1 = 字段值1, 字段名2 = 字段值2, ... [where 条件字段 = 条件值]
注意事项:
- where 条件不带,则会修改所有的列
- 字段值是一个具体的值,也可以是一个变量(一般时间能用,CURRENT_TIME)
- 多个设置的值之间要用英文逗号隔开
3.5、删除
DELETE FROM `student`;
DELETE FROM `student` WHERE `id` = 1;
--清空表,完全清空一个表,表结构和约束都不会变
TRUNCATE `student`;
语法:delete from 表名 [where 条件] 、TRUNCATE 表名
delete和truncate
- 相同点:
- 都能删除数据,都不会删除表结构
- 不同点:
- truncate会重置自增列,计数器会归零
- truncate不会影响事务
了解:delete删除的问题,重启数据库现象
- InnoDB 自增列会从1开始(存在内存当中的,断电即失)
- MyISAM 继续从上一个自增量开始(存在文件当中的,不会丢失)
4、DQL查询数据(最重点)
4.1、DQL
Data Query Language:数据库查询语言
- 所有的查询操作都用它 select
- 简单的查询,复杂的查询它都能做
- 数据库最核心的语言,最重要的语句
- 使用频率最高的语句
select完整的语法:
select [all | distinct]
{* | table.* | [table.field1[as alias1][,table.field2[as alias2]][,...]]}
from table_name [as table_alias]
[left | right | inner join table_name2] --联合查询
[where ...] --指定结果需满足的条件
[group by ...] --指定结果按照哪几个字段来分组
[having] --过滤分组的记录必须满足的次要条件
[order by ...] --指定查询记录按一个或多个条件排序
[limit {[offset,]row_count | row_count offset offset}] --指定查询的记录从哪条到哪条
注意:[]括号代表可选的,{}括号代表必选的
4.2、指定查询字段
-- 查询所有字段
select * from student;
-- 查询指定字段
select `StudentNo`, `StudentName` from `student`;
-- 可以给字段、表起别名
select `StudentNo` as 学号, `StudentName` as 学生姓名 from `student` as s;
-- 函数concat(a, b)
select concat('姓名:', StudentNo) as 新名字 from `student`;
语法:select 字段,... from 表名
-
去重distinct
作用:去除select查询出来的结果中重复的数据,重复的数据只显示一条
select distinct `StudentNo` from result -
数据库的列(表达式)
select version() --查询系统版本 select 100*3-1 as 计算结果 --用来计算 select @@auto_increment_increment --查询自增的步长 -- 学员考试成绩+1分查看 select `StudentNo`, `StudentResult` + 1 as '提分后' from result数据库中的表达式:文本值、列、Null、函数、计算表达式、系统变量……
select
表达式from 表
4.3、where条件子句
作用:检索数据库中符合条件的数据
搜索的条件由一个或者多个表达式组成,结果为布尔值
-
逻辑运算符
运算符 语法 描述 and(&&) a and b、a && b 逻辑与,两个都为真,结果为真 or(||) a or b、a || b 逻辑或,其中一个为真,则结果为真 not(!) not a、!a 逻辑非,真为假,假为真 尽量使用英文字母
select StudentNo, StudentResult from result where StudentResult >= 95 and StudentResult <= 100; select StudentNo, StudentResult from result where StudentResult >= 95 && StudentResult <= 100; select StudentNo, StudentResult from result where StudentResult between 95 and 100; select StudentNo, StudentResult from result where StudentNo != 1000; select StudentNo, StudentResult from result where not StudentNo = 1000; -
比较运算符(模糊查询)
运算符 语法 描述 is null a is null 如果操作符为null,则结果为真 is not null a is not null 如果操作符不为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...其中的某一个值,则结果为真 -- 查询姓刘的同学 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 '%嘉%'; -- 查询1001,1002,1003号的学员 select StudentNo, StudentName from student where StudentNo in (1001, 1002, 1003); -- 查询地址为空的学生 select StudentNo, StudentName from student where address = '' or address is null;
4.4、联表查询
-
7种join理论
-
内连接

select * from A inner join B where A.key = B.key; -
左连接

select * from A left join B on A.key = B.key where B.key is null; -
右连接

select * from A right join B on A.key = B.key where A.key is null; -
左外连接

select * from A left join B on A.key = B.key; -
右外连接

select * from A right join B on A.key = B.key; -
全外连接

select * from A left join B where A.key = B.key union select * from A right join B where A.key = B.key; -
两表独有的数据集

select * from A left join B on A.key = B.key where B.key is null union select * from A right join B on A.key = B.key where A.key is null;
-
-
自连接
把一张表有层级的表(category),看为两个一摸一样的表
category_id pid categoryName 1 0 信息技术 2 0 软件开发 3 0 美术设计 4 1 办公信息 5 2 数据库 6 2 web开发 7 3 ps技术 所需查询结果:
父栏目 子栏目 信息技术 办公信息 软件开发 数据库 软件开发 web开发 美术设计 ps技术 select a.categoryName as 父栏目, b.categoryName as 子栏目 from category as a, category as b where a.category_id = b.pid
4.5、分页和排序
-
分页
目的:缓解数据库压力,给人的体验更好
语法:
limit 起始下标, pageSize以下以每页5条为例:
select * from A where ... limit 0, 5; --第一页 select * from A where ... limit 5, 5; --第二页 select * from A where ... limit 10, 5; --第三页 -- 第一页 limit 0,5 -- 第二页 limit 5,5 -- 第三页 limit 10,5 -- 第n页 limit (n-1)*5 -- 【pageSize:页面大小】 -- 【(n-1)*pageSize:起始值】 -- 【n:当前页】 -- 【总页数:数据总数/页面大小】 -
排序
-
升序:asc
-
降序:desc
select id, name, ... from A where ... order by asc(desc) -
4.6、子查询
本质:在where语句中嵌套一个子查询语句
select * from A where id = (select a_id from B where ...)
select * from A where id in (select a_id from B where ...)
...
where子查询比联合查询速度更快一些
4.7、分组和过滤
查询不同课程的平均分、最高分、最低分、平均分大于80
select SubjectName, avg(StudentResult) as 平均分, max(StudentResult) as 最高分, min(StudentResult) as 最低分
from result r inner join subject s
on r.SubjectNo = s.SubjectNo
group by r.SubjectNo
having 平均分 >= 80;
5、MySQL函数
5.1、常用函数
-- 数学运算
select abs(-8); -- 8,绝对值
select ceiling(9.4); -- 10,向上取整
select floor(9.4); -- 9,向下取整
select rand(); -- 返回一个0~1之间的随机数
select sign(-10); -- 判断一个数的符号,负数返回-1,正数返回1,0返回0
-- 字符串函数
select char_length('爱你一万年'); -- 字符串长度
select concat('我', '爱', '你们'); -- 拼接字符串
select insert('abcdefg', 1, 2, '111'); -- 111cdefg,在指定位置插入字符串
select lower('Hello World'); -- hello world,转小写字母
select upper('Hello World'); -- HELLO WORLD,转大写字母
select instr('abcdefg', 'c'); -- 3,返回第一次出现的字符串的索引
select replace('坚持就能成功', '坚持', '努力'); -- 替换出现的指定字符串
select substr('坚持就能成功', 5, 2); -- 成功,截取字符串
select reverse('坚持就能成功'); -- 反转
-- 时间和日期函数
select current_date(); -- 获取当前日期
select curdate(); -- 获取当前日期
select now(); -- 获取当前的时间
select localtime(); -- 获取本地时间
select sysdate(); -- 获取系统时间
select year(now()); -- 获取当前年
select month(now()); -- 获取当前月
select day(now()); -- 获取当前日
select hour(now()); -- 获取当前时
select minute(now()); -- 获取当前分
select second(now()); -- 获取当前秒
-- 系统
select system_user(); -- 获取系统用户
select user();
select version(); -- 获取版本号
5.2、聚合函数(常用)
| 函数名称 | 描述 |
|---|---|
| count() | 计数 |
| sum() | 求和 |
| avg() | 求平均值 |
| max() | 取最大值 |
| min() | 取最小值 |
-- 都能统计表中的数据(想查询一个表中有多少条记录就使用count())
select count(字段) from tablename; -- 会忽略所有的null值
select count(*) from tablename; -- 不会忽略null值,本质计算行数
select count(1) from tablename; -- 同上
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不可逆,具体的值的md5是一样的
-- 插入的时候给密码加密
insert into user(userName, pwd) values('张三', md5('11111'));
-- 验证
select * from user where username = '张三' and pwd = md5('11111');
6、事务
6.1、什么是事务
将一组SQL放在一个批次中去执行
事务原则:ACID原则,原子性、一致性、隔离性、持久性 (脏读、幻读)
-
原子性(Atomicity)
要么都成功,要么都失败
-
一致性(Consistency)
事务前后的数据完整性要保证一致
-
隔离性(Isolation)
多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作的数据所干扰,事务之间要相互隔离
-
持久性(Durability)
事务一旦提交则不可逆,被持久化到数据库中
隔离性会导致一些问题
- 脏读:指一个事务读取了另一个事务未提交的数据。
- 不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同(这个不一定是错误,只是某些场合不对)。
- 虚读(幻读):是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。
6.2、执行事务
-- mysql 是默认开启事务自动提交的
set autocommit = 0; -- 关闭
set autocommit = 1; -- 开启(默认)
-- 手动处理事务
set autocommit = 0; -- 关闭自动提交
start transaction; --事务开启,标记一个事务的开始,从这之后的sql都在同一个事务内
insert xxx...
insert xxx...
commit; --提交:持久化(成功)
rollback; --回滚:回到原来的样子(失败)
set autocommit = 1; -- 事务结束,开启自动提交
-- 了解
savepoint 保存点名; -- 设置一个事务的保存点
rollback to savepoint; -- 回滚到保存点
release savepoint 保存点名; -- 撤销保存点
6.3、模拟场景
-- 转账
create database shop character set utf8 collate utf8_general_ci;
use shop;
create table account(
id int(10) 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('A', 2000.00), ('B', 1000.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、索引
MySQL官方对索引的定义为:索引(index)是帮助MySQL高效获取数据结构。
提取句子主干,就可以得到索引的本质:索引是数据结构。
7.1、索引的分类
在一个表中,主键索引只能有一个,唯一索引可以有多个
- 主键索引(primary key)
- 唯一标识,主键不可重复,只能有一个列作为主键
- 唯一索引(unique key)
- 避免出现重复的列,唯一索引可以重复,多个列都可以标识为唯一索引
- 常规索引(key/index)
- 默认的,index、key关键字来设置
- 全文索引(FullText)
- 在特定的数据库引擎下才有,MyISAM
- 快速定位数据
基础语法
-- 索引的使用
-- 1、在创建表的时候给字段增加索引
-- 2、创建完毕后,增加索引
-- 显示所有的索引信息
show index from student;
-- 增加一个全文索引(索引名) 列名
alter table school.student add fulltext index `studentNameIndex`(studentName);
-- explain 分析sql执行的状况
explain select * from student; -- 非全文索引
explain select * from student where match(studentName) against('刘');
7.2、测试索引
-- 创建一个表
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(1) UNSIGNED DEFAULT '0' COMMENT '性别(0:男;1:女)',
password VARCHAR(100) NOT NULL COMMENT '密码',
age TINYINT(3) DEFAULT '0' COMMENT '年龄',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY(ID)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='app用户表';
-- 插入100万条数据
DELIMITER $$
CREATE FUNCTION mock_data()
RETURNS INT
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), '815899463@qq.com', CONCAT('1',FLOOR(RAND() * 9999999999)), 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'; -- 0.784s
SELECT * FROM app_user WHERE name = '用户9999'; -- 0.834s
EXPLAIN SELECT * FROM app_user WHERE name = '用户9999'; -- 发现查找的行数为992262
-- 添加索引
-- create index 索引名(id_表名_字段名) on 表(字段)
CREATE INDEX id_app_user_name ON app_user(name);
SELECT * FROM app_user WHERE name = '用户9999'; -- 0.019s
EXPLAIN SELECT * FROM app_user WHERE name = '用户9999'; -- 发现查找的行数为1,直接定位了
索引在小数据量的时候,用处不大,但是在大数据量的时候,区别十分明显。
7.3、索引的原则
- 索引不是越多越好
- 不要对经常变动的数据加索引
- 小数据量的表不需要加索引
- 索引一般加在常用来查询的字段上
索引的数据结构
- Hash
- Btree(innoDB的默认数据结构)
阅读:http://blog.codinglabs.org/articles/theory-of-mysql-index.html
8、权限管理和备份
8.1、用户管理
用户表:mysql.user
本质:对这张表进行增删改查
-- 创建用户 create user 用户名 identified by '密码'
create user test identified by '111111';
-- 修改当前用户密码
set password = password('123456');
-- 修改指定用户密码
set password for test = password('123456');
-- 重命名 rename user 原来的名字 to 新的名字
rename user test to test2;
-- 用户授权 all privileges 全部权限
-- all privileges除了给别人授权,其他都能干
grant all privileges on *.* to test2;
-- 查询权限
show grants for test2; -- 查看指定用户的权限
show grants for root@localhost -- 查看root用户的权限
-- 撤销权限
revoke all privileges on *.* from test2;
-- 删除用户
drop user test2;
8.2、MySQL备份
-
为什么要备份
- 保证重要的数据不丢失
- 数据转移
-
MySQL数据备份的方式
-
直接拷贝物理文件
-
在可视化工具中手动导出
-
使用命令行导出
mysqldump命令导出:
# 导出一张表 # mysqldump -h主机 -u用户名 -p密码 数据库 表名 > 物理磁盘位置/文件名 mysqldump -hlocalhost -uroot -p123456 school student > D:/a.sql # 导出多张表 # mysqldump -h主机 -u用户名 -p密码 数据库 表1 表2 表3 > 物理磁盘位置/文件名 mysqldump -hlocalhost -uroot -p123456 school student result > D:/b.sql # 导出整个库 # mysqldump -h主机 -u用户名 -p密码 数据库 > 物理磁盘位置/文件名 mysqldump -hlocalhost -uroot -p123456 school > D:/c.sql导入:
# 登录的情况下,切换到指定的数据库 # source 备份文件 source d:/a.sql; # 非登录情况 # mysql -u用户名 -p密码 库名 < 备份文件 mysql -uroot -p111111 school < d:/c.sql;
-
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接口的操作即可!

java.sql
javax.sql
还需要导入一个数据库驱动包
10.3、第一个JDBC程序
创建测试数据库
CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;
USE jdbcStudy;
CREATE TABLE users(
id INT PRIMARY KEY,
name VARCHAR(40),
password VARCHAR(40),
email VARCHAR(60),
birthday DATE
);
INSERT INTO users(id, name, password, email, birthday)
VALUES(1, '张三', '111111', 'zhangsan@sina.com', '1980-12-04'),
(2, '李四', '123456', 'lisi@sina.com', '1981-12-04'),
(3, '王五', '222222', 'wangwu@sina.com', '1979-11-02');
-
创建一个普通项目
-
导入数据库驱动
-
编写测试代码
import java.sql.*; public class JdbcFirstDemo { public static void main(String[] args) throws ClassNotFoundException, SQLException { //1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); //2.url和用户信息 String url = "jdbc:mysql://localhost:3306/jdbcStudy?useUnicode=true&characterEncoding=utf8&useSSL=true"; String username = "root"; String password = "111111"; //3.连接成功,数据库连接对象 Connection connection = DriverManager.getConnection(url, username, password); //4.执行sql的对象 Statement statement = connection.createStatement(); //5.用对象去执行sql String sql = "select * from users"; ResultSet resultSet = statement.executeQuery(sql); while (resultSet.next()) { System.out.println("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
- 获得返回的结果集
- 释放连接
DriverManager
// DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection(url, username, password);
//connection代表数据库
//数据库设置自动提交
connection.setAutoCommit();
//事务提交
connection.commit();
//事务回滚
connection.rollback();
URL
String url = "jdbc:mysql://localhost:3306/jdbcStudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
//mysql --3306
// 协议://主机地址:端口号/数据库名?参数1&参数2&参数3
//oracle --1521
// jdbc:oracle:thin@localhost:1521:sid
Statement执行SQL的对象 (PrepareStatement)
Statement statement = connection.createStatement();
statement.executeQuery(); //查询操作返回的数据集
statement.execute(); //执行任何sql
statement.executeUpdate(); //更新、插入、删除都是用这个,返回一个受影响的行数
ResultSet查询的结果集:封装了所有的查询结果
获得指定的数据类型
ResultSet resultSet = statement.executeQuery(sql);
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getDate();
resultSet.getObject();
遍历,指针
resultSet.beforeFirst(); //移动到最前面
resultSet.afterLast(); //移动到最后面
resultSet.next(); //移动到下一个
resultSet.previous(); //移动到前一行
resultSet.absolute(row); //移动到指定行
释放资源
resultSet.close();
statement.close();
connection.close();
10.4、Statement对象
Jdbc中的Statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查的语句即可。
Statement对象的executeUpdate方法,用于向数据库发送增、删、改的sql语句,executeUpdate执行完后,将会返回一个整数(即增删改语句导致了数据库几行数据发生了变化)。
Statement.executeQuery方法用于向数据库发送查询语句,executeQuery方法返回代表查询结果的ResultSet对象。
-
CRUD操作-create
使用executeUpdate(String sql)方法完成数据添加操作,示例代码:
Statement st = conn.createStatement(); String sql = "insert into user(...) values(...)"; int num = st.executeUpdate(sql); if(num > 0) { System.out.println("插入成功!") } -
CRUD操作-delete
使用executeUpdate(String sql)方法完成数据删除操作,示例代码:
Statement st = conn.createStatement(); String sql = "delete from user where id = 1"; int num = st.executeUpdate(sql); if(num > 0) { System.out.println("删除成功!") } -
CRUD操作-update
使用executeUpdate(String sql)方法完成数据修改操作,示例代码:
Statement st = conn.createStatement(); String sql = "update user set name = '' where name = ''"; int num = st.executeUpdate(sql); if(num > 0) { System.out.println("修改成功!") } -
CRUD操作-read
使用executeQuery(String sql)方法完成数据查询操作,示例代码:
Statement st = conn.createStatement(); String sql = "select * from user where id = 1"; ResultSet rs = st.executeQuery(sql); while(rs.next()) { //根据获取列的数据类型,分别调用rs的相应方法映射到java的对象中 }
具体代码实现
-
提取工具类
db.properties,放在根目录下driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSL=true username=root password=111111JdbcUtils.javaimport java.io.InputStream; import java.sql.*; import java.util.Properties; public class JdbcUtils { private static String driver = null; private static String url = null; private static String username = null; private static String password = null; static { InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties"); Properties properties = new Properties(); try { properties.load(in); driver = properties.getProperty("driver"); url = properties.getProperty("url"); username = properties.getProperty("username"); password = properties.getProperty("password"); //1.驱动只用加载一次 Class.forName(driver); } catch (Exception e) { e.printStackTrace(); } } /** * 获取连接 * * @return * @throws SQLException */ public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, username, password); } /** * 释放连接资源 * * @param conn * @param st * @param rs */ public static void release(Connection conn, Statement st, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (st != null) { try { st.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } } -
编写增删改的方法,
executeUpdateTestInsert.javaimport demo02.utils.JdbcUtils; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; public class TestInsert { public static void main(String[] args) { Connection conn = null; Statement st = null; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); String sql = "insert into app_user(name, email, phone, gender, password, age) " + "values('张三', '815899463@qq.com', 15628744621, 0, md5('111111'), 23)"; int i = st.executeUpdate(sql); if (i > 0) { System.out.println("插入成功!"); } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JdbcUtils.release(conn, st, null); } } }TestDelete.javaimport demo02.utils.JdbcUtils; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; public class TestDelete { public static void main(String[] args) { Connection conn = null; Statement st = null; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); String sql = "delete from app_user where id = 1000001"; int i = st.executeUpdate(sql); if (i > 0) { System.out.println("删除成功!"); } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JdbcUtils.release(conn, st, null); } } }TestUpdate.javaimport demo02.utils.JdbcUtils; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; public class TestUpdate { public static void main(String[] args) { Connection conn = null; Statement st = null; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); String sql = "update app_user set name = '张三' where id = 1"; int i = st.executeUpdate(sql); if (i > 0) { System.out.println("更新成功!"); } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JdbcUtils.release(conn, st, null); } } } -
编写查询的方法
TestSelect.javaimport demo02.utils.JdbcUtils; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class TestSelect { public static void main(String[] args) { Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); String sql = "select name, email, phone, gender, age from app_user where id = 1"; rs = st.executeQuery(sql); while (rs.next()) { System.out.println("name:" + rs.getString("name")); System.out.println("email:" + rs.getString("email")); System.out.println("phone:" + rs.getString("phone")); System.out.println("gender:" + rs.getInt("gender")); System.out.println("age:" + rs.getInt("age")); } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JdbcUtils.release(conn, st, rs); } } }
SQL注入的问题
sql存在漏洞,会被攻击导致数据泄露,SQL会被拼接or
import demo02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestSqlInjection {
public static void main(String[] args) {
// login("用户999999", "0ca530fa-e3eb-11eb-8225-f832e48908f3");
login("' or '1 = 1", "' or '1 = 1"); //sql拼接技巧
}
/**
* 登录
*/
private static void login(String username, String password) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
st = conn.createStatement();
String sql = "select name, email, phone, gender, age " +
"from app_user where name = '" + username + "' and password = '" + password + "'";
rs = st.executeQuery(sql);
while (rs.next()) {
System.out.println("name:" + rs.getString("name"));
System.out.println("email:" + rs.getString("email"));
System.out.println("phone:" + rs.getString("phone"));
System.out.println("gender:" + rs.getInt("gender"));
System.out.println("age:" + rs.getInt("age"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JdbcUtils.release(conn, st, rs);
}
}
}
10.5、PrepareStatement对象
PrepareStatement可以防止SQL注入,并且效率更高!
-
新增
import utils.JdbcUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class TestInsert { public static void main(String[] args) { Connection conn = null; PreparedStatement pst = null; try { conn = JdbcUtils.getConnection(); //使用占位符替代参数 String sql = "insert into app_user(name, email, phone, gender, password, age) " + "values(?, ?, ?, ?, md5(?), ?)"; //预编译sql,先写sql pst = conn.prepareStatement(sql); //手动给参数赋值 pst.setString(1, "张三"); pst.setString(2, "815899463@qq.com"); pst.setString(3, "15628744621"); pst.setInt(4, 0); pst.setString(5, "111111"); pst.setInt(6, 23); int i = pst.executeUpdate(); if (i > 0) { System.out.println("插入成功!"); } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JdbcUtils.release(conn, pst, null); } } } -
删除
import utils.JdbcUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class TestDelete { public static void main(String[] args) { Connection conn = null; PreparedStatement pst = null; try { conn = JdbcUtils.getConnection(); //使用占位符替代参数 String sql = "delete from app_user where id = ?"; //预编译sql,先写sql pst = conn.prepareStatement(sql); //手动给参数赋值 pst.setLong(1, 1000001); int i = pst.executeUpdate(); if (i > 0) { System.out.println("删除成功!"); } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JdbcUtils.release(conn, pst, null); } } } -
修改
import utils.JdbcUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class TestUpdate { public static void main(String[] args) { Connection conn = null; PreparedStatement pst = null; try { conn = JdbcUtils.getConnection(); //使用占位符替代参数 String sql = "update app_user set name = ? where id = ?"; //预编译sql,先写sql pst = conn.prepareStatement(sql); //手动给参数赋值 pst.setString(1, "李四"); pst.setLong(2, 1000002); int i = pst.executeUpdate(); if (i > 0) { System.out.println("修改成功!"); } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JdbcUtils.release(conn, pst, null); } } } -
查询
import utils.JdbcUtils; import java.sql.*; public class TestSelect { public static void main(String[] args) { Connection conn = null; PreparedStatement pst = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); String sql = "select name, email, phone, gender, age from app_user where id = ?"; pst = conn.prepareStatement(sql); pst.setLong(1, 1); rs = pst.executeQuery(); while (rs.next()) { System.out.println("name:" + rs.getString("name")); System.out.println("email:" + rs.getString("email")); System.out.println("phone:" + rs.getString("phone")); System.out.println("gender:" + rs.getInt("gender")); System.out.println("age:" + rs.getInt("age")); } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JdbcUtils.release(conn, pst, rs); } } } -
防止SQL注入
import utils.JdbcUtils; import java.sql.*; public class TestSqlInjection { public static void main(String[] args) { // login("用户999999", "0ca530fa-e3eb-11eb-8225-f832e48908f3"); login("'' or 1 = 1", "'' or 1 = 1"); } private static void login(String username, String password) { Connection conn = null; PreparedStatement pst = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); //PreparedStatement防止SQL注入的本质是把传递进来的参数当作字符 //如果其中存在转义字符,会被直接转义 String sql = "select name, email, phone, gender, age " + "from app_user where name = ? and password = ?"; pst = conn.prepareStatement(sql); pst.setString(1, username); pst.setString(2, password); rs = pst.executeQuery(); while (rs.next()) { System.out.println("name:" + rs.getString("name")); System.out.println("email:" + rs.getString("email")); System.out.println("phone:" + rs.getString("phone")); System.out.println("gender:" + rs.getInt("gender")); System.out.println("age:" + rs.getInt("age")); } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JdbcUtils.release(conn, pst, rs); } } }
10.6、使用IDEA连接数据库




sql控制台

更新数据

10.7、JDBC操作事务
- 开启事务
conn.setAutoCommit(false); - 一组业务执行完,提交事务
- 可以在catch语句中显示定义回滚语句,但默认失败就会回滚
代码实现:
import utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestTransaction {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pst = null;
try {
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false); //开启事务
String sql1 = "update account set money = money - 500 where name = 'A'";
pst = conn.prepareStatement(sql1);
pst.executeUpdate();
// int x = 1/0; //报错
String sql2 = "update account set money = money + 500 where name = 'B'";
pst = conn.prepareStatement(sql2);
pst.executeUpdate();
conn.commit(); //业务完毕,提交事务
System.out.println("转账成功!");
} catch (SQLException throwables) {
//如果失败,则默认回滚,下面的代码可以不写
// try {
// if (conn != null) {
// conn.rollback();
// }
// } catch (SQLException e) {
// e.printStackTrace();
// }
throwables.printStackTrace();
} finally {
JdbcUtils.release(conn, pst, null);
}
}
}
10.8、数据库连接池
数据库连接 ---> 执行完毕 ---> 释放
连接 --- 释放,十分浪费系统资源
池化技术:准备一些预先的资源,过来就连接预先准备好的
最小连接数:10
最大连接数:15
等待超时:100ms
编写连接池,实现一个接口DataSource
开源数据源实现(拿来即用)
- DBCP
- C3P0
- Druid:阿里巴巴
使用了这些数据库连接池之后,我们在项目开发中就不需要编写连接数据库的代码了
DBCP
需要导入commons-dbcp-1.4.jar和commons-pool-1.6.jar,可从apache官网下载
dbcp.properties
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=111111
#!-- 初始化连接 --
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.java
package 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 {
InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcp.properties");
Properties properties = new Properties();
try {
properties.load(in);
//创建数据源 工厂模式--创建
dataSource = BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接
*
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
/**
* 释放连接资源
*
* @param conn
* @param st
* @param rs
*/
public static void release(Connection conn, Statement st, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (st != null) {
try {
st.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
TestDBCP.java
import utils.JdbcUtils;
import utils.JdbcUtils_DBCP;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;
public class TestDBCP {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pst = null;
try {
conn = JdbcUtils_DBCP.getConnection();
//使用占位符替代参数
String sql = "insert into users(name, password, email, birthday) values(?, ?, ?, ?)";
//预编译sql,先写sql
pst = conn.prepareStatement(sql);
//手动给参数赋值
pst.setString(1, "张三");
pst.setString(2, "111111");
pst.setString(3, "815899463@qq.com");
pst.setDate(4, new java.sql.Date(new Date().getTime()));
int i = pst.executeUpdate();
if (i > 0) {
System.out.println("插入成功!");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JdbcUtils.release(conn, pst, null);
}
}
}
C3P0
需要导入c3p0-0.9.5.5.jar和mchange-commons-java-0.2.19.jar,可从https://sourceforge.net/projects/c3p0/这里下载
c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!--
c3p0的缺省(默认)配置
如果在代码中ComboPooledDataSource ds=new ComboPooledDataSource();这样写就表示使用的是c3p0的缺省(默认)
-->
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="acquiredIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
<!--
c3p0的命名配置
如果在代码中ComboPooledDataSource ds=new ComboPooledDataSource(MySQL);这样写就表示使用的是mysql的缺省(默认)
-->
<named-config name="MySQL">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true</property>
<property name="user">root</property>
<property name="password">111111</property>
<property name="acquiredIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</named-config>
</c3p0-config>
JdbcUtils_C3P0.java
package utils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcUtils_C3P0 {
private static ComboPooledDataSource dataSource = null;
static {
try {
//代码版配置
//dataSource = new ComboPooledDataSource();
//dataSource.setDriverClass();
//dataSource.setUser();
//dataSource.setPassword();
//dataSource.setJdbcUrl();
//dataSource.setMaxPoolSize();
//dataSource.setMinPoolSize();
//配置文件写法
dataSource = new ComboPooledDataSource("MySQL");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接
*
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
/**
* 释放连接资源
*
* @param conn
* @param st
* @param rs
*/
public static void release(Connection conn, Statement st, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (st != null) {
try {
st.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
TestC3P0.java
import utils.JdbcUtils;
import utils.JdbcUtils_C3P0;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;
public class TestC3P0 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pst = null;
try {
conn = JdbcUtils_C3P0.getConnection();
//使用占位符替代参数
String sql = "insert into users(name, password, email, birthday) values(?, ?, ?, ?)";
//预编译sql,先写sql
pst = conn.prepareStatement(sql);
//手动给参数赋值
pst.setString(1, "张三");
pst.setString(2, "111111");
pst.setString(3, "815899463@qq.com");
pst.setDate(4, new java.sql.Date(new Date().getTime()));
int i = pst.executeUpdate();
if (i > 0) {
System.out.println("插入成功!");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JdbcUtils.release(conn, pst, null);
}
}
}
结论
无论使用什么数据源,本质还是一样的,DataSource接口不会变,方法就不会变

浙公网安备 33010602011771号