本文最后编辑于 前,其中的内容可能需要更新。
初始Mysql https://www.kuangstudy.com/bbs/1467760102697095170#header2
什么是数据库 概念: 数据仓库(软件),安装在操作系统(window,Linux,mac…)之上。可以存储大量数据。500万
作用: 数据库是存储数据,管理数据,
为什么学习数据库
趋势,岗位需求
大数据时代,得数据者得天下
被迫需求:存数据
数据库是所有软件体系中最核心得存在
数据库分类 关系型数据库:(SQL)
MySQL、Oracle、SQL server、DB2、SQLlite…
通过表与表之间、行与行之间的关系进行数据的存储。
非关系型数据库:(NoSQL)Not Only SQL
redis、MongDB
对象存储,通过对象自身的属性来决定。
连接数据库 命令行连接:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 mysql update mysql.user set authentication_string= password('123456' ) where user = 'root' and Host = 'localhost' ; flush privileges; show databases; mysql> use school Database changed show tables; describe student; create database westos; exit;
数据库XXX语言 DDL 定义
DML 操作
DQL 查询
DCL 控制
MySQL属性和操作 1.常用密令 1 2 3 show create database user; --查看创建数据库user的语句 show create table user; --查看创建数据表user的语句 desc user; --查看数据表user的结构
mysql的关键字不区分字母的大小写
2.操作数据库。数据库名:demo
创建数据库
1 create database [if not exists] demo;
删除数据库
1 drop database [if exists] demo;
使用数据库
1 use `demo`; --tab键上面,如果表名或者字段名是一个特殊字符,就需要使用``
查看数据库
1 show databases; --查看所有数据库
3.数据库列的类型
mysql数据类型长度问题总结:https://blog.csdn.net/yaruli/article/details/79187814
数值
tinyint:十分小的数据,1个字节。
smallint:较小的数据,2个字节。
mediumint:中等大小的数据,3个字节。
int:标准的证书,4个字节。(常用的变量)
bigint:较大的数据,8个字节。
float:浮点数,4个字节。
double:浮点数,8个字节。
decimal:字符串形式的浮点数。涉及到金钱计算时,一般使用decimal
字符串
char:字符串固定大小的,0~255字节。
varchar:可变字符串,0~65535字节。(常用的变量)
tinytext:微型文本,2^8-1字节。
text:文本串,2^16-1字节。
时间
date:YYYY-MM-DD,日期格式。
time:HH:mm:ss,时间格式。
datatime:YYYY-MM-DD HH:mm:ss,最常用的时间格式。
timestamp:时间戳,1970.1.1道现在的毫秒数。
year:年份表示。
null
没有值,未知。
注意:不要使用NULL进行运算,没有意义,计算结果为NULL
4.数据库的字段属性
Unsigned
zerofill
0填充
不足的位数使用0填充。int(5)类型存储数字9会变成00009
自增
通常理解为自增,自动在上一条记录的基础上+1(默认)。
通常用来设计唯一的主键,必须是整数类型。
可以自定义设计主键自增的起始值和步长。
非空
勾选上not null,如果不给字段赋值,则会报错。
默认
设置默认的值。
sex,默认值为男,如果不指定该列的值,则会有默认的值。
拓展:
5.创建数据库
6.数据表类型
关于数据库引擎
InnoDB:5.5版本以后默认使用。
MyISam:早些年使用的默认。
MyISam
InnoDB
事务支持
不支持
支持
数据行锁定
不支持
支持
外键约束
不支持
支持
全文索引
支持
不支持
表空间大小
较小
较大,约为myisam两倍
1 2 3 常规使用操作: - MYISAM节约空间,速度较快。 - INNODB安全性高,事务的处理,多表多用户操作。
在物理空间存在的位置 所有的数据库文件都存在data目录下,一个文件夹就对应一个数据库,本质还是文件的存储。
MySQL引擎在物理文件上的区别。
InnoDB在数据库表中只有一个*.frm文件,以及上级目录下的ibdata1文件。
MYISAM对应文件:
*.frm:表结构的定义文件
*.MYD:数据文件(data)
*.MYI:索引文件(index)
7.设置数据库表的字符集编码
MySQL默认字符编码的设置:https://www.jianshu.com/p/ec0c86ee3e04
不设置的话,会是mysql默认的字符集编码(不支持中文)
MySQL的默认编码是Latin1,不支持中文。
在my.ini中配置默认的编码
1 character-set-server=utf8
8.修改数据表字段 1 2 3 4 5 6 7 8 9 10 11 12 13 -- 修改表名:ALTER TABLE 旧表名 RENAME AS 新表名 ALTER TABLE teacher RENAME AS teacher1 -- 增加表的字段:ALTER TABLE 表名 ADD 字段名 列属性 ALTER TABLE teacher1 ADD age INT(11) -- 修改表的字段(重命名,修改约束) -- ALTER TABLE 表名 MODIFY 字段名 列属性[] ALTER TABLE teacher1 MODIFY age VARCHAR(11) -- 修改约束 -- ALTER TABLE 表名 CHANGE 旧名字 新名字 列属性[] ALTER TABLE teacher1 CHANGE age age1 INT(1) -- 字段重命名 -- 删除表的字段:ALTER TABLE 表名 DROP 字段名 ALTER TABLE teacher1 DROP age1
*change一般用来字段重命名,modify不用来字段重命名,只能修改字段类型和约束*
9.删除数据表字段 1 2 -- 删除表(如果表存在再删除) drop table if exists `teacher1`
MySQL数据管理 外键(了解即可) 方式一:在创建表的时候,增加约束(麻烦,比较复杂)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 -- 学生表的gradeid字段,要去引用年级表的gradeid -- 定义外键key -- 给这个外键添加约束(执行引用) references 引用 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(20) NOT NULL DEFAULT '匿名' COMMENT '姓名', `pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码', `sex` VARCHAR(2) NOT NULL DEFAULT '男' COMMENT '性别', `gradeid` INT(10) NOT NULL COMMENT '年级', `birthday` DATETIME DEFAULT NULL COMMENT '生日', `address` VARCHAR(20) DEFAULT NULL COMMENT '地址', `email` VARCHAR(20) DEFAULT NULL COMMENT '邮箱', PRIMARY KEY(`id`), KEY `FK_gradeid`(`gradeid`), CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade`(`gradeid`) )ENGINE=INNODB DEFAULT CHARSET=utf8
删除有外键关系的表的时候,必须要先删除引用别人的表(从表),再删除被引用的表(主表)
方式二:创建表成功后,添加外键约束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 -- 学生表的gradeid字段,要去引用年级表的gradeid -- 定义外键key -- 给这个外键添加约束(执行引用) references 引用 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(20) NOT NULL DEFAULT '匿名' COMMENT '姓名', `pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码', `sex` VARCHAR(2) NOT NULL DEFAULT '男' COMMENT '性别', `gradeid` INT(10) NOT NULL COMMENT '年级', `birthday` DATETIME DEFAULT NULL COMMENT '生日', `address` VARCHAR(20) DEFAULT NULL COMMENT '地址', `email` VARCHAR(20) DEFAULT NULL COMMENT '邮箱', PRIMARY KEY(`id`) )ENGINE=INNODB DEFAULT CHARSET=utf8 -- 创建表的时候没有外键关系 ALTER TABLE `student` ADD CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade`(`gradeid`); -- ALTER TABLE 表 ADD CONSTRAINT 约束名 FOREIGN KEY(作为外键的列) REFERENCES 那个表(那个字段)
以上操作都是物理外键,数据库级别的外键,不建议使用!(避免数据库过多造成困扰)
==最佳实践==
数据库就是单纯的表,只用来存数据,只有行(数据)和列(字段)
我们想使用多张表的数据,想使用外键(程序去实现)
DML语言(全部记住) 数据库意义:数据存储,数据管理
DML语言:数据操作语言
添加 1 2 3 4 5 6 insert into `student`(id,name,age)values (1 ,'zhangsan' ,12 );INSERT INTO `grade`(`gradename`)VALUES ('大一' ),('大二' )
注意事项:
字段和字段之间使用 英文逗号 隔开
字段可以省略,但是后面的值必须要一一对应,不能少
可以同时插入多条数据,VALUES后面的值,需要使用,隔开即可 (),()
修改 1 2 3 4 -- 不指定条件的情况下,会修改所有的记录:update 表名 set column_name=value,[column_name=value,...] where [条件] update `student` set name='张三' where id=1 UPDATE student SET NAME = '行初雾',email = '1092315259@qq.com' WHERE id = 1
条件:where子句
操作符:
=、<>或!=、<、>、<=、>=、BETWEEN…and… 、AND、OR
1 UPDATE `student` set `name` = 'hello' WHERE `name` = '1' AND sex = '女'
语法:update 表名 set column_name=value,[column_name=value,…] where [条件]
注意:
colnum_name 是数据库的列,尽量带上``
条件,筛选的条件,如果没有指定,则会修改所有的列
value,是一个具体的值,也可以是一个变量
多个设置的属性之间,使用英文逗号隔开
删除 delete命令:
INSERT INTO test
(coll
) VALUES(‘1’),(‘2’),(‘3’)
DELETE FROM test
– 不会影响自增
TRUNCATE TABLE test
– 自增会归零
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 delete删除问题(了解即可):重启数据库,现象 - InnoDB 自增列会重1开始,(存在内存当中,断电即失) - MyISAM 继续从上一个自增量开始(存在文件中,不会丢失) ## DQL查询数据(最重点) ### 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_countOFFSET offset}]; – 指定查询的记录从哪条至哪条
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 注意:[]括号代表可选的,{}括号代表必选的 ### 指定查询字段 ```sql -- 查询全部的学生 SELECT 字段 FROM 表 select * from student; -- 查询指定字段 select `StudentId`,`StudentName` from student; -- 别名,给结果起一个名字 AS 可以给字段起别名,也可以给表起别名 select `StudentId` as 学号 , `StudentName` AS 学生姓名 from student as s; -- 函数 Concat(a,b) 拼接 select concat('姓名:',StudentName) as 新名字 from student;
语法:==SELECT 字段,… FROM 表==
有时,列表名不是那么见名知意,可以起别名:AS
去重及数据库的表达式 去重:distinct
1 2 3 4 5 6 7 8 9 10 -- 查询全部考试成绩 SELECT * FROM result; -- 查询有哪些同学参加了考试 SELECT `StudentID` FROM result; -- 发现数据重复,去重 SELECT DISTINCT `StudentID` FROM result;
数据库的列(表达式)
1 2 3 4 5 6 7 8 9 10 11 12 SELECT VERSION();SELECT 100 * 3 -1 AS 计算结果;SELECT @@auto _increment_increment;SELECT `StudentID`,`StudentResult`+ 1 AS '+1后' FROM result ;
==数据库中的表达式:文本值、列、Null、函数、计算表达式、系统变量…==
select ==表达式== from 表
where子句之逻辑运算符 作用:检索数据中符合条件
的值
搜索的条件由一个或者多个表达式组成,结果布尔值
逻辑运算符
运算符
语法
描述
and &&
a and b a&&b
逻辑与
or ||
a orb a||b
逻辑或
not !
not a !a
逻辑非
==尽量使用英文字母==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 -- 查询考试成绩在 95 ~ 100分之间 SELECT studentid,studentresult FROM result; -- and SELECT studentid,studentresult FROM result WHERE studentresult >= 95 AND studentresult <= 100; -- && SELECT studentid,studentresult FROM result WHERE studentresult >= 95 && studentresult <= 100; -- 模糊查询(区间) SELECT studentid,studentresult FROM result WHERE studentresult BETWEEN 95 AND 100; -- 除了1000号学生之外的学生的成绩 -- != SELECT studentid,studentresult FROM result WHERE studentid != 1000; -- not SELECT studentid,studentresult FROM result WHERE NOT studentid = 1000;
模糊查询:比较运算符
模糊查询:比较运算符
运算符
语法
描述
IS NULL
a is null
如果操作符为null,结果为真
IS NOT NULL
a is not null
如果操作符不为null,结果为真
BWTWEEN…AND…
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,…其中的某一个,则结果为真
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 -- 查询姓王的同学 -- like 结合 -- %(代表0到任意个字符) -- _(代表1) -- 查询姓王的同学 SELECT `studentId`,`studentName` FROM `student` WHERE studentName LIKE '王%'; -- 查询姓王的同学,名字后面只有一个字的 SELECT `studentId`,`studentName` FROM `student` WHERE studentName LIKE '王_'; -- 查询姓王的同学,名字后面有两个字的 SELECT `studentId`,`studentName` FROM `student` WHERE studentName LIKE '王__'; -- 查询名字中间有嘉字的同学 SELECT `studentId`,`studentName` FROM `student` WHERE studentName LIKE '%嘉%'; -- in (具体的一个或者多个值) -- 查询学号1001,1002,1003号学号 SELECT `studentId`,`studentName` FROM `student` WHERE studentId IN ('1001','1002','1003'); -- 查询在北京的学生 SELECT `studentId`,`studentName` FROM `student` WHERE address IN ('北京'); -- null -- 查询地址为空的学生 SELECT `studentId`,`studentName` FROM `student` WHERE address = '' OR address IS NULL; -- not null -- 查询有出生日期的同学 不为空 SELECT `studentId`,`studentName` FROM `student` WHERE borndate IS NOT NULL; -- 查询没有出生日期的同学 为空 SELECT `studentId`,`studentName` FROM `student` WHERE borndate IS NULL;
连表查询join on
join 对比
七种join理论
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 -- =========连表查询================== -- 查询参加了考试的同学(学号,姓名,科目编号,分数) SELECT * FROM student; SELECT * FROM result; -- join on 连接查询 -- where 等值查询 -- inner join SELECT s.`StudentId`,`StudentName`,`SubjectID`,`StudentResult` FROM student AS s INNER JOIN result AS r ON s.`StudentId` = r.`StudentID` -- right join SELECT s.`StudentId`,`StudentName`,`SubjectID`,`StudentResult` FROM student AS s RIGHT JOIN result AS r ON s.`StudentId` = r.`StudentID` -- left join SELECT s.`StudentId`,`StudentName`,`SubjectID`,`StudentResult` FROM student AS s LEFT JOIN result AS r ON s.`StudentId` = r.`StudentID` -- 查询缺考的同学 SELECT s.`StudentId`,`StudentName`,`SubjectID`,`StudentResult` FROM student AS s LEFT JOIN result AS r ON s.`StudentId` = r.`StudentID` WHERE r.`StudentID` IS NULL -- 查询了参加考试的同学信息(学号,学生姓名,科目名称,分数) SELECT s.`StudentId`,`StudentName`,`Subjectname`,`StudentResult` FROM student AS s INNER JOIN result AS r ON s.`StudentId` = r.`StudentID` INNER JOIN SUBJECT AS sub ON r.`SubjectID` = sub.`subjectno` -- 查询了参加高等数学-1考试的同学信息(学号,学生姓名,科目名称,分数) SELECT s.`StudentId`,`StudentName`,`Subjectname`,`StudentResult` FROM student AS s INNER JOIN result AS r ON s.`StudentId` = r.`StudentID` INNER JOIN SUBJECT AS sub ON r.`SubjectID` = sub.`subjectno` WHERE sub.`subjectname` = '高等数学-1' -- 我要查询哪些数据 select ... -- 从哪几个表中查 from 表 XXX join 连接的表 on 交叉条件 -- 假设存在一种多张表查询,慢慢来,先查询两张表然后再慢慢增加
操作
描述
inner join
如果表中至少有一个匹配,就返回行
left join
会从左边中返回所有的值,即使右表中没有匹配
right join
会从右边中返回所有的值,即使左表中没有匹配
自连接 自己的表和自己的表连接,核心:==一张表拆为两张一样的表即可==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 -- 创建表 -- unsigned 无符号 -- auto_increment=9 自增的起始值 DROP TABLE IF EXISTS `category` ; CREATE TABLE `category` ( `category_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主题id', `pid` INT(10) NOT NULL COMMENT '父id', `category_name` VARCHAR(50) NOT NULL COMMENT '主题名字', PRIMARY KEY (`category_id`) ) ENGINE=INNODB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; -- 插入值 INSERT INTO `category`(`category_id`,`pid`,`category_name`) VALUES('2','1','信息技术'), ('3','1','软件开发'), ('4','3','数据库'), ('5','1','美术设计'), ('6','3','web开发'), ('7','5','ps技术'), ('8','2','办公信息'); SELECT * FROM `category`;
父类
pid
category_id
category_name
1
2
信息技术
1
3
软件开发
1
5
美术设计
子类
pid
category_id
category_name
3
4
数据库
2
8
办公信息
3
6
web开发
5
7
ps技术
操作:查询父类对应的子类关系
父类
子类
信息技术
办公信息
软件开发
数据库
软件开发
web开发
美术设计
ps技术
1 2 3 4 -- 查询父子信息,把一张表看为两个一模一样的表 SELECT a.`category_name` AS '父栏目',b.`category_name` AS '子栏目' FROM `category` AS a, `category` AS b WHERE a.`category_id`=b.`pid`;
分页和排序
排序
1 2 3 4 5 6 7 8 9 10 SELECT s.`StudentId`,`StudentName`,`Subjectname`,`StudentResult`FROM student AS sINNER JOIN result AS rON s.`StudentId` = r.`StudentID`INNER JOIN SUBJECT AS subON r.`SubjectID` = sub.`subjectno`ORDER BY `studentResult` ASC
分页
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 -- 100w -- 为什么要分页 -- 缓解数据库压力,给人更好的体验 瀑布流 -- 分页,每页只显示五条数据 -- 语法 : limit 起始值,页面的大小 -- 网页应用:当前,总的页数,每页大小 -- LIMIT 0,5 1~5 -- LIMIT 1,5 2~6 -- LIMIT 6,5 SELECT s.`StudentId`,`StudentName`,`Subjectname`,`StudentResult` FROM student AS s INNER JOIN result AS r ON s.`StudentId` = r.`StudentID` INNER JOIN SUBJECT AS sub ON r.`SubjectID` = sub.`subjectno` ORDER BY `studentResult` ASC LIMIT 0,3 -- 第一页 limit 0,5 (1-1)*5 -- 第二页 limit 5,5 (2-1)*5 -- 第三页 limit 10,5 (3-1)*5 -- 第N页 limit 10,5 (n-1)*pageSize,pageSize -- pageSize,页面大小 -- (n-1)*pageSize,起始值 -- n,当前页 -- 总页数 = (数据总数%页面大小==0)? (数据总数/页面大小) : (数据总数/页面大小 + 1)
语法: limit (查询起始下标,页面大小)
子查询和嵌套查询 where(这个值是计算出来的)
本质:在where语句中嵌套一个子查询语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 -- 1.查询数据库结构-1的所有考试结果(学号,科目名,成绩),降序排列 -- 方式1:使用连接查询 SELECT r.`StudentId`,`Subjectid`,`StudentResult` FROM result AS r INNER JOIN `subject` AS sub ON r.`SubjectID` = sub.`subjectno` WHERE sub.`subjectname` = '高等数学-1' ORDER BY res.`StudentResult` DESC; -- 使用子查询(由里及外) select r.`StudentId`,subjectId,studentResult from result as r where SubjectID = ( select Subjectno from `subject` where SubjectName = '高等数学-1' ) ORDER BY res.`StudentResult` DESC; -- 分数不小于80分的学生的学号和姓名 SELECT DISTINCT s.`StudentId`,s.`StudentName` FROM student s INNER JOIN result r ON s.`StudentId` = r.`StudentID` WHERE r.`StudentResult` > 80 -- 在这个基础上增加一个科目,查询课程为高等数学-1,且分数不小于80分的学生的学号和姓名 SELECT DISTINCT s.`StudentId`,s.`StudentName` FROM student s INNER JOIN result r ON s.`StudentId` = r.`StudentID` WHERE r.`StudentResult` > 80 AND r.`SubjectID` = ( SELECT subjectno FROM `subject` WHERE subject.`subjectname` = '高等数学-1' ) SELECT DISTINCT s.`StudentId`,s.`StudentName` FROM student s INNER JOIN result r ON s.`StudentId` = r.`StudentID` INNER JOIN `subject` sub ON r.`SubjectID` = sub.`subjectno` WHERE sub.`subjectname` = '高等数学-1' AND r.`StudentResult` > 80 -- 再次改造(由里及外) SELECT DISTINCT s.`StudentId`,s.`StudentName` FROM student s WHERE studentId IN ( SELECT studentId FROM result WHERE StudentResult > 80 AND subjectid = ( SELECT subjectno FROM `subject` WHERE subjectname = '高等数学-1' ) )
常用函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 SELECT ABS (-8 ); SELECT CEILING (9.4 ) ;SELECT FLOOR (9.4 );SELECT RAND(); SELECT SIGN(-10 ); SELECT CHAR_LENGTH ('哈哈' ); SELECT CONCAT('我' ,'爱' ,'你' ); SELECT INSERT ('我爱编程helloworld' ,1 ,2 ,'超级热爱' ); SELECT LOWER ('ZYY' ); SELECT UPPER ('zyy' ); SELECT INSTR('zyy' ,'y' ); 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();
聚合函数及分组过滤
函数名称
描述
count()
计数
sum()
求和
avg()
平均值
max()
最大值
min()
最小值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 -- 聚合函数 -- 都能统计 表中数据 -- count(字段) 会忽略所有的null值(想查询一个表中有多少个记录,就使用这个count()) SELECT COUNT(studentname) FROM student; -- COUNT(*) 不会忽略所有的null值 本质计算行数 SELECT COUNT(*) FROM student; -- COUNT(1) 不会忽略所有的null值 本质计算行数 SELECT COUNT(1) FROM student; SELECT SUM(studentResult) AS '总和' FROM result; SELECT AVG(studentResult) AS '平均值' FROM result; SELECT MAX(studentResult) AS '最大值' FROM result; SELECT MIN(studentResult) AS '最小值' FROM result; -- 查询不同课程的平均分,最高分,最低分 SELECT sub.subjectName,AVG(r.studentResult),MAX(r.studentResult),MIN(r.studentResult) FROM result r INNER JOIN `subject` sub ON r.`SubjectID` = sub.`subjectno` GROUP BY r.`SubjectID` HAVING AVG(r.`StudentResult`) > 80
数据库级别的md5加密(扩展) 什么是MD5?
主要增加算法复杂度和不可逆性。
MD5不可逆,具体的值的md5是一样的
MD5破解网站的原理,背后有一个字典,MD5加密后的值,加密前的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 CREATE TABLE `testmd5`( `id` INT(4) NOT NULL, `name` VARCHAR(20) NOT NULL, `pwd` VARCHAR(50) NOT NULL, PRIMARY KEY (`id`) )ENGINE=INNODB DEFAULT CHARSET=utf8; -- 明文密码 INSERT INTO `testmd5`(`id`,`name`,`pwd`) VALUES (1,'张三','123456'), (2,'李四','123456'), (3,'王五','123456'); SELECT * FROM `testmd5`; -- 加密 UPDATE testmd5 SET pwd=MD5(pwd) WHERE id=2; -- 插入的时候加密 INSERT INTO `testmd5`(`id`,`name`,`pwd`) VALUES (4,'小明',MD5('123456')); -- 如何校验,将用户传递进来的密码,进行MD5加密,然后比对加密后的值 SELECT * FROM `testmd5` WHERE `name`='小明' AND pwd = MD5('123456');
事务 什么是事务 ==要么都成功,要么都失败==
将一组sql放到一个批次中去执行
事务原则:ACID原则 原子性 、一致性、隔离性、持久性 (脏读,幻读。。。)
参考博客链接:事务ACID理解
原子性(Atomicity)
要么都成功,要么都失败
一致性(Consistency)
事务前后的数据完整性要保持一致
下图操作前和操作后的总和都是1000
隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性(Durability)
事务一旦移交不可逆,被持久化到数据库中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 SET autocommit = 0 SET autocommit = 1 SET autocommit = 0 START TRANSACTION INSERT xxINSERT xxCOMMIT ROLLBACK SET autocommit = 1 SAVEPOINT 保存点名 ROLLBACK TO SAVEPOINT 保存点名 RELEASE SAVEPOINT 保存点名 CREATE DATABASE shop CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci USER shopCREATE 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= utf8mb4 INSERT INTO account(`name`,`money`)VALUE ('A' ,2000.00 ),('B' ,10000.00 )SET autocommit = 0 ; START TRANSACTION UPDATE account SET money= money-500 WHERE `name` = 'A' UPDATE account SET money= money+ 500 WHERE `name` = 'B' COMMIT ; ROLLBACK ; SET autocommit = 1 ;
隔离所导致的一些问题 脏读: 1、在事务A执行过程中,事务A对数据资源进行了修改,事务B读取了事务A修改后的数据。
2、由于某些原因,事务A并没有完成提交,发生了RollBack操作,则事务B读取的数据就是脏数据。
这种读取到另一个事务未提交的数据的现象就是脏读(Dirty Read)。
不可重复读: 事务B读取了两次数据资源,在这两次读取的过程中事务A修改了数据,导致事务B在这两次读取出来的数据不一致。
这种**==在同一个事务中==,前后两次读取的数据不一致的现象就是不可重复读(Nonrepeatable Read)。**
虚读(幻读) 事务B前后两次读取同一个范围的数据,在事务B两次读取的过程中事务A新增了数据,导致事务B后一次读取到前一次查询没有看到的行。
幻读和不可重复读有些类似,但是幻读强调的是集合的增减,而不是单条数据的更新。
第一类更新丢失 事务A和事务B都对数据进行更新,但是事务A由于某种原因事务回滚了,把已经提交的事务B的更新数据给覆盖了。这种现象就是第一类更新丢失。
第二类更新丢失 其实跟第一类更新丢失有点类似,也是两个事务同时对数据进行更新,但是事务A的更新把已提交的事务B的更新数据给覆盖了。这种现象就是第二类更新丢失。
事务隔离级别 为了解决以上的问题,主流的关系型数据库都会提供四种事务的隔离级别。事务隔离级别从低到高分别是:读未提交,读已提交,可重复读,串行化。事务隔离级别越高,越能保证数据的一致性和完整性,但是执行效率也越低,所以在设置数据库的事务隔离级别时需要做一下权衡,mysql默认是可重复读
读未提交 读未提交(Read Uncommitted),是最低的隔离级别,所有的事务都可以看到其他未提交的事务的执行结果。 只能防止第一类更新丢失,不能解决脏读,可重复读,幻读,所以很少应用于实际项目。
读已提交 读已提交(Read Committed),在该隔离级别下,一个事务的更新操作只有在该事务提交之后,另外一个事务才可能读取到同一笔数据更新后的结果。 可以防止脏读和第一类更新丢失,但是不能解决可重复和幻读的问题。
可重复读(重要) 可重复读(Repeatable Read),mysql默认的隔离级别。在该隔离级别下,一个事务多次读同一个数据,在这个事务还没有结束时,其他事务不能访问该数据(包括了读写) ,这样就可以在同一个事务内两次读到的数据是一样的。可以防止脏读、不可重复读、第一类更新丢失,第二类更新丢失的问题,不过还是会出现幻读。
串行化 串行化(Serializable),这是最高的隔离级别。它要求事务序列化执行,事务只能一个接着一个的执行,不能并发执行。在这个级别,可以解决上面提到的所有并发问题,但是可能导致大量的超时现象和锁竞争,通常不会用这个隔离级别。
总结
扩展:回滚机制 在mysql中,恢复机制是通过回滚日志(undo log)实现的,所有的事务进行的修改都会先记录到这个回滚日志中,然后在堆数据库中的对应进行写入。
mysql的事务是由redo和undo的,redo操作的所有信息都是记录到重做日志(redo_log)中,也就是说当一个事务做commit操作时,需要先把这个事务的操作写到redo_log中,然后在把这些操作flush到磁盘上,当出现故障时,只需要读取redo_log,然后在重新flush到磁盘就行了。
而对于undo就比较麻烦,mysql在处理事务时,会在数据共享表空间里申请一个段就做segment段,用保存undo信息,当在处理rollback,不是完完全全的物理undo,而是逻辑undo,也就是说会之前的操作进行反操作(对于每个insert,回滚时会执行delete;对于每个delete,回滚时会执行insert;对于每个update,回滚时会执行一个相反的update,把数据改回去。),但是这些共享表空间是不进行回收的。这些表空间的回收需要由mysql的master thread进程进行回收。
索引
Msql官方对索引的定义为:索引(index)是帮助MySQL高效获取数据的数据结构 。
提取句子主干,就可以得到索引的本质:索引是数据结构。
索引的分类
在一个表中,主键索引只能有一个,唯一索引可以有多个
主键索引(primary key)
唯一索引 (unique key)
避免重复的列出现,可以重复,多个列都可以标示为唯一索引
常规索引(key/index)
全文索引(FullText)
在特定的数据库引擎下才有,myisam
快速定位数据
基础语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -- 索引的使用 -- 1.在创建表的时候给字段增加索引 -- 2.创建完毕后,增加索引 -- 显示所有的索引信息 SHOW INDEX FROM student; -- 新增一个索引 (索引名) 列名 ALTER TABLE `student` ADD UNIQUE KEY `UK_IDENTITY_CARD` (`identity_card`); ALTER TABLE `student` ADD KEY `K_STUDENT_NAME`(`student_name`); ALTER TABLE `student` ADD FULLTEXT INDEX `FI_PHONE` (`phone`); -- explain 分析sql执行的状况 EXPLAIN SELECT * FROM student; -- 非全文索引 EXPLAIN SELECT * FROM student WHERE MATCH(`phone`) AGAINST('138'); -- 全文索引
【MySQL优化】——看懂explain_漫漫长途,终有回转;余味苦涩,终有回甘-CSDN博客_explain
测试索引 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 CREATE TABLE app_user ( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', `name` VARCHAR(50) DEFAULT '' COMMENT '用户昵称', `email` VARCHAR(50) NOT NULL COMMENT '用户邮箱', `phone` VARCHAR(20) DEFAULT '' COMMENT '手机号', `gender` TINYINT(4) UNSIGNED DEFAULT '0' COMMENT '性别(0:男 1:女)', `password` VARCHAR(100) NOT NULL COMMENT '密码', `age` TINYINT(4) DEFAULT '0' COMMENT '年龄', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='app用户表' -- 创建函数时报错信息:This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled -- 错误分析:我们创建函数时必须指定我们的函数是否是 /* 1. DETERMINISTIC 不确定的 2. NO SQL 没有SQl语句,当然也不会修改数据 3. READS SQL DATA 只是读取数据,当然也不会修改数据 4. MODIFIES SQL DATA 要修改数据 5. CONTAINS SQL 包含了SQL语句*/ -- 解决办法:set global log_bin_trust_function_creators=TRUE; -- 插入100万数据b (函数) 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),'123345@qq.com',CONCAT('18',FLOOR(RAND()*((999999999-100000000)+100000000))),FLOOR(RAND()*2),UUID(),FLOOR(RAND()*100)); SET i = i+1; END WHILE; RETURN i; END; -- 执行函数 SELECT mock_data(); SELECT * FROM app_user; -- 函数中间的插入语句 INSERT INTO app_user(`name`,`email`,`phone`,`gender`,`password`,`age`) VALUES(CONCAT('用户X'), '123345@qq.com', CONCAT('18',FLOOR(RAND()*((999999999-100000000)+100000000))), FLOOR(RAND()*2), UUID(), FLOOR(RAND()*100));
测试:
1 2 3 4 5 6 7 8 9 10 11 -- 加索引前 SELECT * FROM app_user WHERE `name` = '用户9999'; -- 0.440 sec EXPLAIN SELECT * FROM app_user WHERE `name` = '用户9999'; -- 创建索引 -- id_表名_字段名 索引名 -- CREATE INDEX 索引名 ON 表名(`字段名`); CREATE INDEX id_app_user_name ON app_user(`name`); -- 加索引后 SELECT * FROM app_user WHERE `name` = '用户9999'; -- 0.002 sec EXPLAIN SELECT * FROM app_user WHERE `name` = '用户9999';
索引在小数据量的时候,用处不大,但是再大数据的时候,区分十分明显
索引原则
索引不是越多越好
不要对经常变动的数据加索引
小数据量的表不需要加索引
索引一般加载常用来查询的字段上
索引的数据结构
Hash类型的索引
Btree :innodb的默认数据结构
CodingLabs - MySQL索引背后的数据结构及算法原理
权限管理和备份 用户管理
sql 命令操作
用户表:mysql.user
本质:读这张表进行增删改查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 -- 创建用户 CREATE USER zyy IDENTIFIED BY '123456'; -- 修改密码(修改当前用户密码) SET PASSWORD = PASSWORD('123456'); -- 修改密码(修改指定用户密码) SET PASSWORD FOR zyy = PASSWORD('123456'); -- 重命名 RENAME 原名子 zyy TO 新名字; RENAME USER zyy TO newzyy; -- 用户授权 ALL PRIVILEGES 全部的权限,库,表 -- ALL PRIVILEGES 除了给别人授权不行,其他都能干 GRANT ALL PRIVILEGES ON *.* TO newzyy; -- 查询权限 SHOW GRANTS FOR newzyy; -- 查看指定用户的权限 SHOW GRANTS FOR root@localhost; -- 查看root用户的权限 -- 撤销权限 REVOKE哪些权限,在哪个库,给谁撤销 REVOKE ALL PRIVILEGES ON *.* FROM newzyy; -- 删除用户 DROP USER newzyy;
MySQL备份 为什么要备份?
mysql数据库备份的方式
直接拷贝物理文件
在sqlyog这种可视化工具中手动导出
使用命令行导出 mysqldump 命令行使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 一张表 mysqldump -h主机 -u用户名 -p密码 数据库 表名 >物理磁盘位置/文件名 mysqldump -hlocalhost -uroot -p123456 school student >D:/a.sql # 多张表 mysqldump -h主机 -u用户名 -p密码 数据库 表名1 表名2 >物理磁盘位置/文件名 mysqldump -hlocalhost -uroot -p123456 school student result >D:/a.sql # 数据库 mysqldump -h主机 -u用户名 -p密码 数据库 >物理磁盘位置/文件名 mysqldump -hlocalhost -uroot -p123456 school >D:/a.sql # 导入 # 登录的情况下,切换到指定的数据库 # source 备份文件 # 也可以这样 mysql -u用户名 -p密码 库名<备份文件
假设你要备份数据库,防止数据丢失。
把数据库给别人,直接给sql即可。
如何设计一个项目的数据库 为什么需要设计
==当数据库比较复杂的时候,我们就需要设计了==
糟糕的数据库设计
数据冗余,浪费空间
数据库插入和删除都会麻烦、异常(屏蔽使用物理外键)
程序的性能差
良好的数据库设计
节省内存空间
保证数据库的完整性
方便我们开发系统
软件开发中,关于数据库的设计
分析需求,分析业务和需要处理的数据库的需求
概要设计:设计关系图E-R图
设计数据库的步骤(个人博客)
收集信息,分析需求
用户表(用户登录注销,用户的个人信息,写博客,创建分类)
分类表(文章分类,谁创建的)
文章表(文章信息)
评论表
友链表(友情链接信息)
自定义表(系统信息,某个关键的字,或者一些主字段) key:value
关注表(粉丝数)
说说表(发表心情, id…content…create_time)
标识实体(把需求落到每个字段)
标识实体之间的关系
写博客:user –> blog
创建分类:user –> category
关注:user –> user
友链:links
评论:user –> user –> blog
(bbs / crm)
三大范式 第一范式(1NF)
原子性:保证每一列不可再分
第二范式(2NF)
前提:满足第一范式
第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。
每张表只描述一件事情
第三范式(3NF)
前提:满足第一范式和第二范式
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。
规范数据库的设计
规范性和性能的问题
关联查询的表不得超过三张表
考虑商业化的需求和目标(成本,用户体验)数据库的性能更加重要
在规范性能的问题的时候,需要适当的考虑一下规范性
故意给某些表增加一些冗余的字段。(从多表查询中变为单表查询)
故意增加一些计算列(从大数据库降低为小数据量的查询:索引)
JDBC 数据库驱动 驱动:声卡,显卡,数据库
我们的程序会通过数据库驱动,和数据库打交道!
JDBC SUN公司为了简化开发人员的(对数据库的统一)操作,提供了一个(java操作数据库的)规范,俗称JDBC
这些规范的实现由具体的厂商去做~
对于开发人员来说,我们只需要掌握JDBC接口的操作即可!
java.sql
javax.sql
还需要导入一个数据库驱动包 mysql-connector-java-5.1.47.jar
第一个JDBC程序
创建测试数据库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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,'张三','123456','zs@sina.com','1980-12-04'), (2,'李四','123456','lisi@sina.com','1981-12-04'), (3,'王五','123456','wangwu@sina.com','1982-12-04');
创建一个普通项目
导入数据库驱动(jar包)
编写测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class jdbcDemo1 { public static void main(String[] args) throws ClassNotFoundException, SQLException { //1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); //固定写法,加载驱动 //2.用户信息和url //useUnicode=true 支持中文 //characterEncoding=utf8 //useSSL=false 使用安全的连接 String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false"; String username = "root"; String password = "cjy521213"; //3.连接成功,数据库对象 Connection 代表数据库 Connection connection = DriverManager.getConnection(url, username, password); //4.执行SQL的对象 Statement 执行SQL的对象 Statement statement = connection.createStatement(); //5.执行SQL的对象 去 执行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("password = " + resultSet.getObject("password")); System.out.println("email = " + resultSet.getObject("email")); System.out.println("birthday = " + resultSet.getObject("birthday")); System.out.println("======================="); } //6.释放连接 resultSet.close(); statement.close(); connection.close(); } }
步骤总结:
加载驱动
连接数据库DriverManager
获取执行SQL的对象 Statement
获得返回的结果集
释放连接
JDBC对象解释
DriverManager
1 2 3 4 5 6 7 8 9 10 11 12 13 //1.加载驱动 //DriverManager.registerDriver(new com.mysql.jdbc.Driver()); //推荐这种写法加载驱动 Class.forName("com.mysql.jdbc.Driver"); Connection connection = DriverManager.getConnection(url, userName, passWord); // connection代表数据库 // 数据库设置自动提交 // 事务提交 // 事务回滚 connection.setAutoCommit(true); connection.commit(); connection.rollback();
URL
1 2 3 4 5 6 String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false"; // mysql默认端口3306 // 协议://主机地址:端口号/数据库名?参数1&参数2&参数3 // oracle默认端口1521 // jdbc:oracle:thin:@localhost:1521:sid
Statement 执行sql对象 、 PreparedStatement 执行sql对象
1 2 3 4 5 String sql = "SELECT * FROM users";//编写SQL statement.executeQuery();//执行查询 返回ResultSet statement.executeUpdate();//新增,删除,修改,都用这个,返回受影响的行数 statement.execute();//执行任何SQL
ResultSet 查询的结果集,封装了所有的查询结果
获得指定的数据类型
1 2 3 4 5 6 7 8 9 10 //在不知道列类型的情况下使用 resultSet.getObject(); //如果知道列类型,就使用指定的类型 resultSet.getString(); resultSet.getInt(); resultSet.getDouble(); resultSet.getBigDecimal(); resultSet.getFloat(); resultSet.getDate(); //...
遍历,指针
1 2 3 4 5 resultSet.beforeFirst();//移动到最前面 resultSet.afterLast();//移动到最后面 resultSet.next();//移动到下一个数据 resultSet.previous();//移动到前一行 resultSet.absolute(row);//移动到指定行
释放资源
1 2 3 resultSet.close(); statement.close(); connection.close();//消耗资源
statement对象详解 ==jdbc中的statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可。==
Statement对象的executeUpdate方法,用于向数据库发送增、删、改的SQL语句,executeUpdate执行完后,将会返回一个整数(即增删改语句导致了数据库几行数据发送了变化)。
Statement.executeQuery方法用于向数据库发送查询语句,executeQuery方法返回代表查询结果的ResultSet对象。
CRUD操作-create
使用executeUpdate(String sql)方法完成数据添加操作,示例操作:
1 2 3 4 5 6 Statement statement = connection.createStatement(); String sql = "insert into user(...) values(...)"; int num = statement.executeUpdate(sql); if (num > 0) { System.out.println("插入成功~"); }
CRUD操作-delete
1 2 3 4 5 6 Statement statement = connection.createStatement(); String sql = "delete from user where id=1"; int num = statement.executeUpdate(sql); if (num > 0) { System.out.println("删除成功~"); }
CRUD操作-update
1 2 3 4 5 6 Statement statement = connection.createStatement(); String sql = "update user set name='' where name =''"; int num = statement.executeUpdate(sql); if (num > 0) { System.out.println("修改成功~"); }
CRUD操作-read
1 2 3 4 5 6 Statement statement = connection.createStatement(); String sql = "SELECT * FROM users"; ResultSet resultSet = statement.executeQuery(sql); while (resultSet.next()) { //根据获取列的数据类型,分别调用resultSet的相应方法映射到java对象中 }
代码实现
提取工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 package com.chang.lesson2.utils; import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.Properties; /** * @author 行初雾 * @PackageName:com.chang.lesson2.utils * @ClassName: jdbcUtil * @Description: * @date 2022/1/21 2:55 */ public class jdbcUtil { private static String driver = null; private static String url = null; private static String username = null; private static String password = null; static { try { InputStream in = jdbcUtil.class.getClassLoader().getResourceAsStream("db.properties"); Properties properties = new Properties(); properties.load(in); driver = properties.getProperty("driver"); url = properties.getProperty("url"); username = properties.getProperty("username"); password = properties.getProperty("password"); //驱动只用加载一次 Class.forName(driver); } catch (Exception e) { e.printStackTrace(); } } //获取连接 public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url,username,password); } //释放连接资源 public static void release(Connection conn, Statement st, ResultSet rs){ if(rs!=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if(st!=null){ try { st.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
配置文件db.properties
1 2 3 4 driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false username=root password=cjy521213
编写增删改的方法,executeUpdate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.chang.lesson2; import com.chang.lesson2.utils.jdbcUtil; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /** * @author 行初雾 * @PackageName:com.chang.lesson2 * @ClassName: testInsert * @Description: * @date 2022/1/21 2:55 */ public class testInsert { public static void main(String[] args) { Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = jdbcUtil.getConnection(); //获取数据库连接 st = conn.createStatement(); //获取SQL的执行对象 String sql = "INSERT INTO users(id,`name`,`password`,`email`,`birthday`)\n" + "VALUE(4,'行初雾','123456','1092315259@qq.com','1999-05-25')"; int i = st.executeUpdate(sql); if(i > 0){ System.out.println("插入成功!"); } } catch (SQLException e) { e.printStackTrace(); } finally { jdbcUtil.release(conn,st,rs); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.chang.lesson2; import com.chang.lesson2.utils.jdbcUtil; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /** * @author 行初雾 * @PackageName:com.chang.lesson2 * @ClassName: testDelete * @Description: * @date 2022/1/21 3:15 */ public class testDelete { public static void main(String[] args) { Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = jdbcUtil.getConnection(); //获取数据库连接 st = conn.createStatement(); //获取SQL的执行对象 String sql = "DELETE FROM users WHERE id = 4"; int i = st.executeUpdate(sql); if(i > 0){ System.out.println("删除成功!"); } } catch (SQLException e) { e.printStackTrace(); } finally { jdbcUtil.release(conn,st,rs); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package com.chang.lesson2; import com.chang.lesson2.utils.jdbcUtil; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /** * @author 行初雾 * @PackageName:com.chang.lesson2 * @ClassName: testUpdate * @Description: * @date 2022/1/21 3:17 */ public class testUpdate { public static void main(String[] args) { Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = jdbcUtil.getConnection(); //获取数据库连接 st = conn.createStatement(); //获取SQL的执行对象 String sql = "UPDATE users SET `name` = '行初雾',`email` = '1092315259@qq.com' WHERE id=1"; int i = st.executeUpdate(sql); if(i > 0){ System.out.println("更新成功!"); } } catch (SQLException e) { e.printStackTrace(); } finally { jdbcUtil.release(conn,st,rs); } } }
查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package com.chang.lesson2; import com.chang.lesson2.utils.jdbcUtil; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /** * @author 行初雾 * @PackageName:com.chang.lesson2 * @ClassName: testSelect * @Description: * @date 2022/1/21 3:20 */ public class testSelect { public static void main(String[] args) { Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = jdbcUtil.getConnection(); //获取数据库连接 st = conn.createStatement(); //获取SQL的执行对象 String sql = "SELECT * FROM users WHERE id = 1"; rs = st.executeQuery(sql); while(rs.next()){ System.out.println(rs.getString("name")); } } catch (SQLException e) { e.printStackTrace(); } finally { jdbcUtil.release(conn,st,rs); } } }
sql注入问题 sql存在漏洞,会被攻击导致数据泄露 ==SQL会被拼接==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package com.chang.lesson2; import com.chang.lesson2.utils.jdbcUtil; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /** * @author 行初雾 * @PackageName:com.chang.lesson2 * @ClassName: sqlReinject * @Description: sql注入 * @date 2022/1/21 3:34 */ public class sqlReinject { public static void main(String[] args) { //正常登录 login("行初雾","123456"); //sql注入 login(" 'or '1=1","'or '1=1"); } public static void login(String name,String password){ Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = jdbcUtil.getConnection(); //获取数据库连接 st = conn.createStatement(); //获取SQL的执行对象 String sql = "SELECT * FROM users WHERE `name`= '"+name+"' AND `password`='"+password+"'"; rs = st.executeQuery(sql); while(rs.next()){ System.out.println(rs.getString("name")); System.out.println(rs.getString("password")); } } catch (SQLException e) { e.printStackTrace(); } finally { jdbcUtil.release(conn,st,rs); } } }
导致结果:错误的用户名或者密码可以获取到全部的用户信息
PreparedStatement对象 PreparedStatement可以防止SQL注入,效率更好
增加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.chang.lesson3; import com.chang.lesson2.utils.jdbcUtil; import java.sql.*; /** * @author 行初雾 * @PackageName:com.chang.lesson3 * @ClassName: testInsert * @Description: * @date 2022/1/21 4:21 */ public class testInsert { public static void main(String[] args) { Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = jdbcUtil.getConnection(); //使用?占位符代替参数 String sql = "INSERT INTO users(`id`,`name`,`password`,`email`,`birthday`) VALUES (?,?,?,?,?)"; //预编译SQL,先写SQL,然后不执行 st = conn.prepareStatement(sql); //手动给参数赋值 st.setInt(1,4); st.setString(2,"行初雾"); st.setString(3,"123456"); st.setString(4,"1092315259@qq.con"); st.setDate(5,new java.sql.Date(new java.util.Date().getTime())); int i = st.executeUpdate(); if(i > 0){ System.out.println("插入成功!"); } } catch (SQLException e) { e.printStackTrace(); } finally { jdbcUtil.release(conn,st,rs); } } }
删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import com.zyy.lesson02.utils.JDBCUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * @ClassName: TestDelete * @Description: TODO 类描述 * @Author: zyy * @Date: 2021/07/14 18:19 * @Version: 1.0 */ public class TestDelete { public static void main(String[] args) { Connection con = null; PreparedStatement st = null; ResultSet rs = null; try { con = JDBCUtils.getConnection(); //使用?占位符代替参数 String sql = "DELETE FROM users WHERE `id`=?"; //预编译SQL,先写SQL,然后不执行 st = con.prepareStatement(sql); //手动给参数赋值 st.setInt(1, 5); int num = st.executeUpdate(); if (num > 0) { System.out.println("删除成功!"); } } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtils.release(con, st, rs); } } }
更新
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.chang.lesson3; import com.chang.lesson2.utils.jdbcUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * @author 行初雾 * @PackageName:com.chang.lesson3 * @ClassName: testUpdate * @Description: * @date 2022/1/21 4:38 */ public class testUpdate { public static void main(String[] args) { Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = jdbcUtil.getConnection(); //使用?占位符代替参数 String sql = "update users set `name`=? where `id` = ?"; //预编译SQL,先写SQL,然后不执行 st = conn.prepareStatement(sql); //手动给参数赋值 st.setInt(2,5); st.setString(1,"行初雾"); int i = st.executeUpdate(); if(i > 0){ System.out.println("修改成功!"); } } catch (SQLException e) { e.printStackTrace(); } finally { jdbcUtil.release(conn,st,rs); } } }
查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.chang.lesson3; import com.chang.lesson2.utils.jdbcUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * @author 行初雾 * @PackageName:com.chang.lesson3 * @ClassName: testSelect * @Description: * @date 2022/1/21 4:40 */ public class testSelect { public static void main(String[] args) { Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = jdbcUtil.getConnection(); //使用?占位符代替参数 String sql = "select * from users where id=?"; //预编译SQL,先写SQL,然后不执行 st = conn.prepareStatement(sql); //手动给参数赋值 st.setInt(1,5); rs = st.executeQuery(); while(rs.next()){ System.out.println("id="+rs.getInt("id")); System.out.println("name=" + rs.getString("name")); System.out.println("======"); } } catch (SQLException e) { e.printStackTrace(); } finally { jdbcUtil.release(conn,st,rs); } } }
防止sql注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.chang.lesson3; import com.chang.lesson2.utils.jdbcUtil; import java.sql.*; import java.util.Properties; /** * @author 行初雾 * @PackageName:com.chang.lesson3 * @ClassName: sqlReinject * @Description: * @date 2022/1/21 4:43 */ public class sqlReinject { public static void main(String[] args) { //正常登录 login("行初雾","123456"); //sql注入 //login("' or '1=1","123456"); } public static void login(String name,String password){ Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = jdbcUtil.getConnection(); //获取数据库连接 String sql = "SELECT * FROM users WHERE `name`=? AND `password`=?"; st = conn.prepareStatement(sql); st.setString(1,name); st.setString(2,password); rs = st.executeQuery(); while(rs.next()){ System.out.println(rs.getString("name")); System.out.println(rs.getString("password")); } } catch (SQLException e) { e.printStackTrace(); } finally { jdbcUtil.release(conn,st,rs); } } }
执行结果:查不到任何结果
使用idea连接数据库
连接成功后,就可以选择数据库
连接不上的话,可以看一下下面这里,配置对应的mysql版本
双击数据库
更新数据(提交)
idea编写sql
1 2 3 4 5 6 7 8 9 10 11 create table account ( id int primary key auto_increment, name varchar(40), money float ); insert into account(name, money) values ('A', 1000), ('B', 1000), ('C', 1000);
JDBC操作事务 ==要么都成功,要么都失败==
ACID原则
原子性:要么全部成功,要么全部失败
一致性:总数不变
隔离性:多个进程互不干扰
持久性:一旦提交不可逆,持久化到数据库了
隔离性的问题:
脏读:一个事务读取了另外一个没有提交的事务
不可重复读:在同一个事务内,重复读取表中数据,表数据发生了改变
幻读:在一个事务内,读取到了别人插入的数据,导致前后读出来的结果不一致
代码实现
开启事务con.setAutoCommit(false);
一组业务执行完毕,提交事务
可以在catch语句中显示的定义回滚语句,但是默认失败就会回滚
正常情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.chang.lesson4; import com.chang.lesson2.utils.jdbcUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * @author 行初雾 * @PackageName:com.chang.lesson4 * @ClassName: testTransaction * @Description: * @date 2022/1/21 5:07 */ public class testTransaction { public static void main(String[] args) { Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = jdbcUtil.getConnection(); //关闭自动提交 自动开启事务 conn.setAutoCommit(false);//开启事务 String sql1 = "update account set money=money-100 where name='A"; st = conn.prepareStatement(sql1); st.executeUpdate(); String sql2 = "update account set money=money+100 where name='B"; st = conn.prepareStatement(sql2); st.executeUpdate(); //事务完毕,提交事务 conn.commit(); System.out.println("成功!"); } catch (SQLException e) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); } finally { jdbcUtil.release(conn,st,rs); } } }
异常情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package com.chang.lesson4; import com.chang.lesson2.utils.jdbcUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * @author 行初雾 * @PackageName:com.chang.lesson4 * @ClassName: testTransaction * @Description: * @date 2022/1/21 5:07 */ public class testTransaction { public static void main(String[] args) { Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = jdbcUtil.getConnection(); //关闭自动提交 自动开启事务 conn.setAutoCommit(false);//开启事务 String sql1 = "update account set money=money-100 where name='A"; st = conn.prepareStatement(sql1); st.executeUpdate(); //默认失败 int x = 1/0;//一定会异常 String sql2 = "update account set money=money+100 where name='B"; st = conn.prepareStatement(sql2); st.executeUpdate(); //事务完毕,提交事务 conn.commit(); System.out.println("成功!"); } catch (SQLException e) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); } finally { jdbcUtil.release(conn,st,rs); } } }
DBCP-C3P0连接池 数据库连接 – 执行完毕 – 释放
连接– 释放 是十分浪费系统资源的
池化技术:准备一些预先的资源,过来就连接预先准备好的
最小连接数:10(常用连接)
最大连接数:100 (业务最高承载上线)
等待超时:100ms
编写连接池,实现一个接口DataSource
开源数据源实现
DBCP
C3p0
Druid:阿里巴巴
使用了这些数据库连接池之后,我们在项目开发中就不需要编写连接数据库的代码了
DBCP
需要用到的jar包
commons-dbcp-1.4
commons-pool-1.6
配置文件dbcp.properties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #连接设置 driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false username=root password=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_COMMITTED
工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 import org.apache.commons.dbcp.BasicDataSourceFactory; import javax.sql.DataSource; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; /** * @ClassName: JDBCDBCPUtils * @Description: TODO 类描述 * @Author: zyy * @Date: 2021/07/14 17:48 * @Version: 1.0 */ public class JDBCDBCPUtils { private static DataSource dataSource = null; static { try { InputStream in = JDBCDBCPUtils.class.getClassLoader().getResourceAsStream("dbcp.properties"); Properties properties = new Properties(); properties.load(in); //创建数据源 工厂模式 dataSource = BasicDataSourceFactory.createDataSource(properties); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /** * 获取连接 */ public static Connection getConnection() throws SQLException { //从数据源中获取连接 return dataSource.getConnection(); } /** * 释放资源 */ public static void release(Connection con, Statement st, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (st != null) { try { st.close(); } catch (SQLException e) { e.printStackTrace(); } } if (con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import com.zyy.lesson02.utils.JDBCUtils; import com.zyy.lesson05.utils.JDBCDBCPUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * @ClassName: TestDBCP * @Description: TODO 类描述 * @Author: zyy * @Date: 2021/07/15 22:41 * @Version: 1.0 */ public class TestDBCP { public static void main(String[] args) { Connection con = null; PreparedStatement st = null; ResultSet rs = null; try { con = JDBCDBCPUtils.getConnection(); //使用?占位符代替参数 String sql = "INSERT INTO users(`id`,`name`,`password`,`email`,`birthday`) VALUES (?,?,?,?,?)"; //预编译SQL,先写SQL,然后不执行 st = con.prepareStatement(sql); //手动给参数赋值 st.setInt(1, 5); st.setString(2, "钱七"); st.setString(3, "123456"); st.setString(4, "qianqi@sina.com"); st.setDate(5, new java.sql.Date(new java.util.Date().getTime())); int num = st.executeUpdate(); if (num > 0) { System.out.println("插入成功!"); } } catch (SQLException e) { e.printStackTrace(); } finally { JDBCDBCPUtils.release(con, st, rs); } } }
C3P0
需要用到的jar包
c3p0-0.9.5.5.jar
mchange-commons-java-0.2.19.jar
配置文件c3p0-config.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?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=false&serverTimezone=UTC</property> <property name="user">root</property> <property name="password">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的命名配置 如果在代码中ComboPooledDataSource ds=new ComboPooledDataSource("MySQL");这样写就表示使用的是name是MySQL --> <name-config name="MySQL"> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC</property> <property name="user">root</property> <property name="password">123456</property> <property name="acquiredIncrement">5</property> <property name="initialPoolSize">10</property> <property name="minPoolSize">5</property> <property name="maxPoolSize">20</property> </name-config> </c3p0-config>
工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 import com.mchange.v2.c3p0.ComboPooledDataSource; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /** * @ClassName: JDBCC3P0Utils * @Description: TODO 类描述 * @Author: zyy * @Date: 2021/07/14 17:48 * @Version: 1.0 */ public class JDBCC3P0Utils { private static DataSource dataSource = null; //private static ComboPooledDataSource dataSource = null; static { try { //代码的方式配置 // dataSource = new ComboPooledDataSource(); // dataSource.setDriverClass(); // dataSource.setJdbcUrl(); // dataSource.setUser(); // dataSource.setPassword(); // dataSource.setMaxPoolSize(); // dataSource.setMinPoolSize(); //配置文件写法 dataSource = new ComboPooledDataSource("MySQL"); } catch (Exception e) { e.printStackTrace(); } } /** * 获取连接 */ public static Connection getConnection() throws SQLException { //从数据源中获取连接 return dataSource.getConnection(); } /** * 释放资源 */ public static void release(Connection con, Statement st, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (st != null) { try { st.close(); } catch (SQLException e) { e.printStackTrace(); } } if (con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import com.zyy.lesson05.utils.JDBCC3P0Utils; import com.zyy.lesson05.utils.JDBCDBCPUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * @ClassName: TestC3P0 * @Description: TODO 类描述 * @Author: zyy * @Date: 2021/07/15 22:41 * @Version: 1.0 */ public class TestC3P0 { public static void main(String[] args) { Connection con = null; PreparedStatement st = null; ResultSet rs = null; try { con = JDBCC3P0Utils.getConnection(); //使用?占位符代替参数 String sql = "INSERT INTO users(`id`,`name`,`password`,`email`,`birthday`) VALUES (?,?,?,?,?)"; //预编译SQL,先写SQL,然后不执行 st = con.prepareStatement(sql); //手动给参数赋值 st.setInt(1, 6); st.setString(2, "刘八"); st.setString(3, "123456"); st.setString(4, "liuba@sina.com"); st.setDate(5, new java.sql.Date(new java.util.Date().getTime())); int num = st.executeUpdate(); if (num > 0) { System.out.println("插入成功!"); } } catch (SQLException e) { e.printStackTrace(); } finally { JDBCC3P0Utils.release(con, st, rs); } } }
总结
无论用什么数据源,本质还是一样的,DataSource接口不会变,方法就不会变
Welcome to The Apache Software Foundation!