解压
把这个包放到自己的电脑环境目录下~
配置环境变量
新建mysql配置文件
[mysqld]
# 目录要换成自己的
basedir=D:\mysql\mysql-8.0.21\
datadir=D:\mysql\mysql-8.0.21\data\
port=3306
skip-grant-tables
启动管理员模式下的CMD,进入到mysql的bin目录下
安装mysql服务
初始化数据库文件
启动
默认密码为空,登录命令
密码修改 由于是Mysql8.0,所以不支持password函数的使用
刷新:flush privileges; 刷新后退出:exit; 退出再重新登录即可
--连接数据库
mysql -u root -p123456
--修改用户密码
update mysql.user set authentication_string=password('123456') where user='root' and Host = 'localhost';
-- 查看时区,显示 SYSTEM 就是没有设置时区啦。
show variables like'%time_zone';
--设置时区 设置完需要重新连接sql重新查看
set global time_zone = '+8:00';
--刷新权限
flush privileges;
--所有的sql语句都使用;结尾
--查看所有的数据库
show databases;
--切换数据库
use 数据库名
--查看数据库中所有的表
show tables;
--显示表中所有的信息
describe 表名
---------------------
--创建一个数据库
create database 数据库名
--退出连接
exit;
--单行注释
/*多行注释*/
数据库xxx语言
DDL 数据库控制语言
DML 数据库操作语言
DQL 数据库查询语言
DCL 数据库控制语言
操作数据库>操作数据库中的表>操作数据库中表的数据
mysql关键字不区分大小写 [ ]表示可选
**如果你的表明或数据库名是一个特殊字符的话要加`` **
创建数据库
CREATE DATABASE [IF NOT EXISTS] 数据库名
删除数据库
DROP DATABASE IF EXISTS 数据库名
使用数据库
USE `数据库名`
查看数据库
SHOW DATABASE
数值
字符串
时间日期
null
Unsigned:
zerofill:
自增
非空NULL not null
-- 每一个表都必须存在以下五个字段,未来做项目用的,表示一个表记录存在的意义
id 主键
version 乐观锁
is_delete 伪删除
gmt_create 创建时间
gmt_update 修改时间
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 '密码',
`set` 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
格式: [ ] 表示可选的 IF NOT EXISTS判断是否存在
CREATE TABLE [IF NOT EXISTS] `表名`(
`字段名` 列类型 [非空] [自增] [注释] '学号',
`字段名` 列类型 [非空] [默认值] '匿名' [注释] '姓名',
.....(注意最后一行不要加,)
)[引擎](ENGINE=INNODB) [字符集](CHARSET=utf8)
常用命令
-- 查看数据库的语句
SHOW CREATE DATABASE 数据库名;
-- 查看表中数据的定义语句
SHOW CREATE TABLE 表名;
-- 显示表的结构
DESC 表名;
修改
-- 修改表名 ALTER TABLE 旧表名 RENAME AS 新表名;
ALTER TABLE student RENAME AS student1;
-- 增加表的字段 ALTER TABLE 表名 ADD 字段名 列属性;
ALTER TABLE student1 ADD age INT(11);
-- 修改表的字段约束 ALTER TABLE 表名 MODIFY 字段名 列属性;
ALTER TABLE student1 MODIFY age VARCHAR(20);
-- 修改表字段名 ALTER TABLE 表名 CHANGE 旧字段 新字段 列属性;
ALTER TABLE student1 CHANGE age age1 INT(1);
-- 删除表字段 ALTER TABLE 表名 DROP 字段名;
ALTER TABLE student1 DROP age1;
删除
-- 删除表 (判断表存在 在删除)
DROP TABLE IF EXISTS student1;
方式一、在创建表的时候,增加约束(麻烦,比较复杂)
CREATE TABLE IF NOT EXISTS `grade`(
`gradeid` INT(10) NOT NULL AUTO_INCREMENT COMMENT '年级id',
`gradename` VARCHAR(50) NOT NULL COMMENT '年级名称',
PRIMARY KEY(`gradeid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
-- 学生表的gradeid字段要去引用年级表的gradeid
-- 定义外键 key
-- 给这个外键添加约束(执行引用) references 引用
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 '密码',
`set` 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`), --定义外键 key
CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`) --
)ENGINE=INNODB DEFAULT CHARSET=utf8
删除有外键关系的表时,必须先删除引用别人的表(从表),在删除被引用的表(主表)
方式二、创建表成功后,添加外键约束
CREATE TABLE IF NOT EXISTS `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 '密码',
`set` 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 表名 ADD CONSTRAINT 约束名 FOREIGN KEY(作为外键的列) REFERENCES 哪个表(哪个字段);
ALTER TABLE `student` ADD CONSTRAINT `FK_gradeid` FOREIGN KEY(`gradeid`) REFERENCES `grade`(`gradeid`);
数据库的意义 :数据存储,数据管理
DML语言:数据操作语言
insert
-- insert into 表名(字段1,字段2,...)values(值1),(值2)
-- 一般写插入语句,我们一定要数据和字段一一对应
INSERT INTO `grade`(`gradeid`,`gradename`)VALUES(1,'大一'),(2,'大二');
语法:insert into 表名(字段1,字段2,…)values(值1),(值2)…
注意事项:
apdate
UPDATE `grade` SET `gradename` = '大1' WHERE `gradeid` = 1;
-- 不指定条件的情况下,会改动表里的这个字段全部的值
UPDATE `grade` SET `gradename` = '大2';
-- 修改多个属性 逗号隔开
UPDATE `grade` SET `gradeid` = 11,`gradename` = '大11' WHERE `gradeid` = 1;
-- 语法
update 表名 set 字段(需要修改的哪个字段) = 值 ,.... where =
条件:where 子句 运算符 id 等于某个值 ,大于某个值,在某个区间内修改…
操作符会返回布尔值
操作符 | 含义 | 范围 | 结果 |
---|---|---|---|
= | 等于 | 5=6 | false |
<>或!= | 不等于 | 5<>6 | true |
> | 大于 | 5>4 | true |
< | 小于 | 5<6 | true |
>= | 大于等于 | 5>=4 | true |
<= | 小于等于 | 5<=6 | true |
BETWEEN…AND… | 在某个范围内比如2~5 | [2,5] | |
AND | 我和你&& | 5>1 and 1>2 | false |
OR | 我或你|| | 5>1 or 1>2 | true |
语法:UPDATE 表名SET colnum_name=value,… WHERE [条件]
注意:
delete 命令
语法:delete from 表名 [where 条件]
-- 删除数据(避免这样写,会全部删除)
DELETE FROM 数据库名;
-- 删除指定数据
DELETE FROM 数据库名 WHERE 条件;
truncate命令
作用:完全清空一个数据库表,表的结构和索引约束不会变!
-- 清空表
TRUNCATE 表名;
delete跟truncate区别
了解即可:delete删除问题,重启数据库现象
select 语法 [ ] 代表可选的 { } 代表必选的
select [all | distinct] -- 去重
{
* | table.* | [table.field1[as alias1],[,table.field2[as alias2]][....]} -- 要查询的字段
from table_name [as table_alias] -- (from 表名 as取别名)
[left | right | inner join table_name2] -- 联合查询 (... join 要连接的表 on 等值判断)
[where ...] -- 指定结果需满足的条件(具体的值,子查询语句)
[group by ...] -- 指定结果按照哪几个字段来分组(通过哪个字段来进行分组)
[having] -- 过滤分组的记录必须满足的次要条件 (过滤分组后的信息,条件和where是一样的,就是上下位置不同)
[order by ...] -- 指定查询记录按一个或多个条件排序 (通过哪个字段排序)[升序/降序]
[limit {
[offset,]row_count | row_countOFFSET offset}]; -- 指定查询的记录从哪条至哪条 (起始下标值,页面有多少条记录)
-- 查询表中全部信息
-- SELECT * FROM 表名;
SELECT * FROM `student`;
-- 查询指定字段
-- SELECT 字段1,字段2... FROM 表名;
SELECT `StudentName`,`Address` FROM `student`;
-- 别名 给结果起一个别名
-- SELECT 字段1 AS 别名,字段2 AS 别名 FROM 表名;
SELECT `StudentName` AS 姓名,`Address`AS 地区 FROM `student`;
-- 函数 concat(a,b)拼接
SELECT CONCAT('姓名:',StudentName) AS 名字 FROM `student`;
语法:select 字段,…from 表名
有的时候,列名字不是那么的见名知意。就起别名 AS 字段名 as 别名
去重复 distinct
作用:去除select查询出来的结果中重复的数据,重复的数据只显示一条
-- 发现重复数据,去重
-- SELECT DISTINCT 字段名 FROM 表名;
SELECT DISTINCT `StudentNo` FROM `result`;
数据库的列(表达式)
-- 查询系统版本
SELECT VERSION();
-- 用来计算
SELECT 100*3-1 AS 计算结果
-- 查询自增的步长
SELECT @@auto_increment_increment;
作用:检索数据中符合条件的值
搜索的条件由一个或多个表达式组成!结果 布尔值
运算符 | 语法 | 描述 |
---|---|---|
and && | a and b 或 a&&b | 逻辑与 |
or || | a or b 或 a||b | 逻辑或 |
Not ! | not a 或 !a | 逻辑非 |
语法:
and
select 字段名 from 表名 where 字段名=5 and 字段名=6;
or
select 字段名 from 表名 where 字段名=6 or 字段名=7;
not
select 字段名 from 表名 where not 字段名=5;
运算符 | 语法 | 描述 |
---|---|---|
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…其中的某一个值中,结果为真 |
-- Like结合 %(代表0到任意一个字符) _ (表示一个字符)
SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE `SubjectName` LIKE '高%';
SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE `SubjectName` LIKE 'C#__';
SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE SubjectName LIKE '%数%';
-- in (具体的一个或多个值)
SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE SubjectNo IN (3,5,2,13);
-- not null 相当于不为空
SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE SubjectNo IS NOT NULL;
JOIN 对比
思路是必看的,很有用
-- 查询参加了考试的同学(学号,姓名,科目编号,分数)
SELECT * FROM `student`;
SELECT * FROM `result`;
/* 思路
1.分析需求,分析查询的字段来自哪张表,如果超过一张表就使用(连接查询)
2.确定使用哪种连接查询? 有7种
确定交叉点(这两个表中哪个数据是相同的)如果没有交叉点是查不了东西的
判断的条件就是, 学生表中 StudentNo = 成绩表中 StudentNo
就是学生表拥有StudentNo 成绩表也拥有 StudentNo 共同字段
*/
-- join(连接的表) on(判断的条件) 连接查询
-- where 等值查询
-- inner join 内连接--
SELECT s.StudentNo,StudentName,SubjectNo,StudentResult -- 因为有两个同样的字段,所以需要表名区引用,要不然不知道是哪个表的字段
FROM student AS s
INNER JOIN result AS r -- 定义了别名,就可以用别名区访问东西了,当然表名也可以,只是会稍微繁琐
WHERE s.StudentNo = r.StudentNo -- 判两张表是否都有这个字段
-- right join 右连接查询--
SELECT student.StudentNo,StudentName,SubjectNo,StudentResult
FROM student
RIGHT JOIN result -- 这是不用别名,用表名的操作,比较繁琐
ON student.StudentNo = result.StudentNo
-- left join 左连接查询--
SELECT s.StudentNo,StudentName,SubjectNo,StudentResult
FROM student s
LEFT JOIN result r -- 别名的 AS 是可以省略的
ON s.StudentNo = r.StudentNo
操作 | 描述 |
---|---|
inner join | 并集 ,只要有一张表有这个数据就可以拿到,就是多表中有一个匹配就行了 |
left join | 左连接,会从左表中返回所有的值,即使右表中没有匹配 |
right join | 右连接,会从右表中返回所有的值,即使左表中没有匹配 |
-- (查询了参加考试同学的信息:学号,学生姓名,科目名,分数) (StudentNo StudentName SubjectName StudentResult)
/* 思路
1.分析需求,分析查询的字段来自哪张表,如果超过一张表就使用(连接查询) 来自( student , result , subject )表
2.确定使用哪种连接查询? 有7种
确定交叉点(这两个表中哪个数据是相同的)如果没有交叉点是查不了东西的
*/
SELECT s.`StudentNo`,`StudentName`,`SubjectName`,`StudentResult`
FROM `student` AS s
RIGHT JOIN `result` AS r
ON s.`StudentNo` = r.`StudentNo`
INNER JOIN `subject` AS sub
ON r.`SubjectNo` = sub.`SubjectNo`
-- 步骤
/*
我要查询哪些数据 select ....
从哪几个表中查 from 表 inner(right,left) join 连接的表 on 交叉条件
假设存在多张表查询,先查询两张表然后再慢慢增加
自连接
自己的表和自己的表连接,核心:一张表拆成两张一样的表即可
-- ==========自连接==============
CREATE TABLE categorg (
categoryid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主题id',
pid INT(10) NOT NULL COMMENT '父id',
categoryName VARCHAR(50) NOT NULL COMMENT '主题名字',
PRIMARY KEY (categoryid)
)ENGINE=INNODB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8
INSERT INTO categorg(categoryid,pid,categoryName)
VALUES(2,1,'信息技术'),
(3,1,'软件开发'),
(4,3,'数据库'),
(5,1,'美术设计'),
(6,3,'web开发'),
(7,5,'ps技术'),
(8,2,'办公信息');
-- 查询父子信息
SELECT f.categoryName AS '父栏目',z.categoryName AS '子栏目'
FROM categorg AS f,categorg AS z
WHERE f.categoryid = z.pid
-- 就相当于 categoryid的值 = pid的值
-- 比如 办公信息的2=信息技术的2 因为1不匹配 所以是单独的
排序 语法:ORDER BY 排序的字段 ASC 或 DESC
-- order by排序:升序asc,降序desc
-- 语法 ORDER BY 排序的字段 ASC(DESC)
-- 根据查询的结果进行排序
SELECT s.StudentNo,StudentName,SubjectName,StudentResult
FROM student s
INNER JOIN `result` r
ON s.StudentNo=r.StudentNo
INNER JOIN `subject` sub
ON r.`SubjectNo` = sub.`SubjectNo`
ORDER BY StudentResult ASC
分页 语法: limit 起始下标值,页面有多少条记录
-- 为什么要分页?
-- 为了缓解数据库压力,给人的体验更好, 还有一种不分页的叫做瀑布流
SELECT s.StudentNo,StudentName,SubjectName,StudentResult
FROM student s
INNER JOIN `result` r
ON s.StudentNo=r.StudentNo
INNER JOIN `subject` sub
ON r.`SubjectNo` = sub.`SubjectNo`
ORDER BY StudentResult ASC
LIMIT 0,5 -- 语法 limit 起始下标值,页面有多少条记录
where (这个值是计算出来的)
使用子查询 (由里及外) 适用于,后面还加条件的
本质:在 where 语句中嵌套一个子查询语句
-- 查询高等数学的所有考试结果(学号 科目名称 成绩),降序查询
-- 方式一: 使用连接查询
SELECT `StudentNo`,SubjectName,StudentResult
FROM `result` AS r
INNER JOIN `subject` sub
ON r.`SubjectNo` = sub.`SubjectNo`
WHERE SubjectName LIKE '高等数学%'
ORDER BY StudentResult DESC
-- 方式二:使用子查询 (由里及外) 适用于,后面还加条件的
-- 查询所有考试结果(学号 科目名称 成绩)>70分的,
SELECT `StudentNo`,SubjectName,StudentResult
FROM `result` AS r
INNER JOIN `subject` sub
ON r.`SubjectNo` = sub.`SubjectNo`
WHERE StudentResult>=70
-- 在基础上增加一个 且是高等数学的
AND SubjectName = (
SELECT SubjectName FROM `subject`
WHERE SubjectName = '高等数学-1'
)
-- 改造方式二:查询学生学号,学生姓名 成绩大于70分的
SELECT StudentNo,StudentName FROM student WHERE StudentNo IN (
SELECT StudentNo FROM result WHERE StudentResult>70 AND SubjectNo =(
SELECT SubjectNo FROM `subject` WHERE SubjectName = '高等数学-1'
)
)
-- 查询不同课程的平均分,最高分,最低分,平均分大于90
-- 核心:根据不同的课程分组 不分组的话只有一个,按照最高的 最低的来排 因为每个课程都有最高分最低分
SELECT SubjectName,AVG(StudentResult)AS 平均分,MAX(StudentResult)AS 最大值,MIN(StudentResult)AS 最小值
FROM `result` AS r
INNER JOIN `subject` AS sub
ON r.`SubjectNo` = sub.`SubjectNo`
GROUP BY r.SubjectNo -- 指定结果按照哪几个字段来分组
HAVING 平均分>=80 -- 过滤分组的记录必须满足的次要条件
官网:https://dev.mysql.com/doc/refman/5.7/en/func-op-summary-ref.html
-- 数学运算
SELECT ABS(-8) -- abs 绝对值
SELECT CEILING(9.4) -- ceiling 向上取整
SELECT FLOOR(9.4) -- floor 向下取整
SELECT RAND() -- rand 返回一个0~1之间的随机数
SELECT SIGN(7) -- sign 判断一个数的符号 负数返回-1 正数返回1
-- 字符串函数
SELECT CHAR_LENGTH('dffhf附件四') -- char_length 字符串长度
SELECT CONCAT('热','爱','编程') -- concat 拼接字符串
SELECT INSERT('我热爱编程helloword',2,3,'我超级喜欢') -- insert 查询 从某个位置开始 替换某个长度
SELECT LOWER('ASDDlele') -- lower 小写字母
SELECT UPPER('asddLELE') -- upper 大写字母
SELECT INSTR('abcdeh','h') -- instr 返回第一次出现的字串的索引
SELECT REPLACE('坚持就一定能成功','坚持','努力') -- replace 替换出现的指定字符串
SELECT SUBSTR('坚持就一定能成功',4,6) -- substr 返回指定的子字符串 从哪里开始,到哪里结束
SELECT REVERSE('坚持就一定能成功') -- reverse 反转
-- 查询性李的 然后替换名字 利
SELECT REPLACE(StudentName,'李','利') FROM student
WHERE StudentName LIKE '李%'
-- 时间和日期函数(记住)
SELECT CURRENT_DATE() -- current_date 获取当前日期
SELECT CURDATE() -- curdate 获取当前日期
SELECT NOW() -- now 获取当前的时间
SELECT LOCALTIME() -- localtime 本地时间
SELECT SYSDATE() -- sysdate 系统时间
SELECT YEAR(NOW()) -- year 年
SELECT MONTH(NOW()) -- month 月
SELECT DAY(NOW()) -- day 日
SELECT HOUR(NOW()) -- hour 时
SELECT MINUTE(NOW()) -- minute 分
SELECT SECOND(NOW()) -- second 秒
-- 系统
SELECT SYSTEM_USER() -- system_user 获取当前用户
SELECT USER() -- user 获取当前用户
SELECT VERSION() -- version 获取当前版本
函数名称 | 描述 |
---|---|
count() | 计数 |
sum() | 求和 |
avg() | 平均值 |
max() | 最大值 |
min() | 最小值 |
… | … |
-- ========聚合函数========
-- 都能够统计表中的数据
SELECT COUNT(`StudentNo`)FROM student; -- count(字段) 会忽略所有的null值
SELECT COUNT(*) FROM student -- count(*) 不会忽略null值,本质 计算行数
SELECT COUNT(1) FROM student -- count(1) 不会忽略所有的null值,本质 计算行数
SELECT SUM(StudentResult)AS 总和 FROM result; -- sum 总和
SELECT AVG(StudentResult)AS 平均分 FROM result; -- avg 平均分
SELECT MAX(StudentResult)AS 最高分 FROM result; -- max 最大值
SELECT MIN(StudentResult)AS 最低分 FROM result; -- min 最小值
什么是MD5?
主要增强算法复杂度和不可逆转。
MD5不可逆,具体的值的md5是一样的
MD5破解网站的原理,背后有一个字典,MD5加密后的值,加密的前值
-- ============测试MD5 加密=========
CREATE TABLE `testmd5`(
`id` INT(4) NOT NULL,
`name` VARCHAR(20) NOT NULL,
`pwd` VARCHAR(50) NOT NULL,
PRIMARY KEY(`id`)
)
-- 明文密码
INSERT INTO `testmd5` VALUES(1,'zhangsan','123456'),(2,'lishi','123456'),(3,'wangwu','123456');
-- 加密
UPDATE testmd5 SET pwd = MD5(pwd) WHERE id = 1;
-- 加密全部的密码
UPDATE testmd5 SET pwd = MD5(pwd);
-- 插入的时候加密
INSERT INTO testmd5 VALUES(4,'xiaoming',MD5('123456')) ;
-- 如何校验:将用户传递进来的密码,进行md5加密,然后比对加密后的值
SELECT * FROM testmd5 WHERE `name` = 'xiaoming' AND pwd = MD5('123456')
要么都成功,要么都失败
将一组SQL放在一批次中去执行~
事物原则:DCID原则 原子性,一致性,隔离性,持久性 (脏读,幻读…)
原子性(atomicity)
要么都成功,要么都失败
一致性(consistency)
事物前后的数据完整性要保证一致
隔离性(isolation)
事物的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事物,不能被其他事务的操作数据所干扰,事物之间要相互隔离
持久性(durability)
事物一旦提交则不可逆,被持久化到数据库中
隔离所导致的一些问题
脏读
指一个事物读取了另外一个事务未提交的数据。
不可重复读
在一个事务内读取表中的某一行数据,多次读取结果不同。(这个不一定是错误,只是某些场合不对)
虚读(幻读)
是指在一个事务内读取到了别的事物插入的数据,导致前后读取不一致。
-- =============事物
-- mysql 是默认开启事务自动提交的
SET autocommit = 0 -- 关闭
SET autocommit = 1 -- 开启(默认的)
-- 手动处理事物
SET autocommit = 0 -- 关闭自动提交
-- 事物开启
START TRANSACTION -- 标记一个事务的开始,从这个之后的sql都在同一个事物内
-- 提交:持久化(成功)
COMMIT
-- 回滚:回到原来的样子(失败)
ROLLBACK
-- 事务结束
SET autocommit = 1 -- 开启自动提交
-- 了解
SAVEPOINT 保存点名 -- 设置一个事务的保存点
ROLLBACK TO SAVEPOINT 保存点名 -- 回滚到保存点
RELEASE SAVEPOINT 保存点名 -- 撤销保存点
模拟场景
-- 转账例子
CREATE DATABASE shop CHARACTER SET utf8 COLLATE utf8_general_ci
USE shop
CREATE TABLE `account`(
`id` INT(3) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(30) NOT NULL,
`money` DECIMAL(9,2) NOT NULL,
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `account`(`name`,`money`)
VALUES('A',2000.00),('B',10000.00)
-- 模拟转账:事务
SET autocommit = 0; -- 关闭自动提交
START TRANSACTION -- 开启一个事物
UPDATE `account` SET `money` = `money`-500 WHERE `name` = 'A'; -- A-500
UPDATE `account` SET `money` = `money`+500 WHERE `name` = 'B'; -- B+500
COMMIT; -- 提交
ROLLBACK; -- 回滚
SET autocommit = 1; -- 恢复默认值,开启自动提交
mysql官方对索引的定义为:**索引(index)是帮助mysql高效获取数据的数据结构。
提取句子主干,就可以得到索引的本质:索引是数据结构
在一个表中,主键索引只能有一个,唯一索引可以有多个
-- 索引的使用
-- 1. 在创建表的时候给字段增加索引
-- 2. 创建完毕后,增加索引
-- 显示所有的索引信息 SHOW INDEX FROM 表名
SHOW INDEX FROM student
-- 增加一个全文索引(索引名) 列名
ALTER TABLE `school`.`student` ADD FULLTEXT INDEX `StudentName`(`StudentName`);
-- explain 分析sql执行的状况
EXPLAIN SELECT * FROM student; -- 非全文索引
EXPLAIN SELECT * FROM student WHERE MATCH(studentName)AGAINST('李')
CREATE TABLE `app_user` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) DEFAULT '' COMMENT '用户名称',
`email` VARCHAR(50) NOT NULL COMMENT '用户邮箱',
`phone` VARCHAR(20) DEFAULT '' COMMENT '手机号',
`gender` TINYINT(4) UNSIGNED DEFAULT '0' COMMENT '性别(0:男 1:女)',
`password` VARCHAR(100) NOT NULL COMMENT '密码',
`age` TINYINT(4) DEFAULT '0' COMMENT '年龄',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='app用户表'
SHOW CREATE DATABASE `school`
SHOW TABLES;
-- 插入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),'[email protected]',CONCAT('18',FLOOR(RAND()*((999999999-100000000)+100000000))),
FLOOR(RAND()*2),UUID(),FLOOR(RAND()*100));
SET i = i+1;
END WHILE;
RETURN i;
END;
SELECT mock_data();
SELECT * FROM app_user WHERE `name` = '用户500000'; -- 没创建索引之前执行6秒
-- id_表名_字段名
-- create index 索引名 no 表(字段)
CREATE INDEX id_app_user_name ON app_user(`name`);
SELECT * FROM app_user WHERE `id` = 500000; -- 创建索引之后执行0.007秒
索引在小数据量的时候,用处不大,但是在大数据的时候,区别十分明显
索引的数据结构
Hash类型的索引
Btree:InnoDB的默认数据结构
阅读:http://blog.codinglabs.org/articles/theory-of-mysql-index.html
SQL命令操作
用户表:mysql.user
本质:读这张表进行增删改查
-- 创建用户 CREATE USER 用户名 IDENTIFIED BY '密码';
CREATE USER xun IDENTIFIED BY '123456';
-- 修改密码 (修改当前用户密码)
SET PASSWORD = PASSWORD('111111');
-- 修改密码(修改指定用户密码)
SET PASSWORD FOR xun = PASSWORD('111111');
-- 重命名 RENAME USER 原来名字 TO 新的名字;
RENAME USER xun TO xun2;
-- 用户授权 ALL PRIVILEGES 全部的权限 ,库.表
-- ALL PRIVILEGES 除了给别人权限,其他的都能干
GRANT ALL PRIVILEGES ON *.* TO xun2
-- 查询权限
SHOW GRANTS FOR xun2; -- 查看指定用户的权限
SHOW GRANTS FOR root@localhost
-- 撤销权限
REVOKE ALL PRIVILEGES ON *.* FROM xun2
-- 删除用户
DROP USER xun2
为什么要备份:
MySQL数据库备份方式
# mysqldump -h主机 -u用户名 -p密码 数据库 表名 > 物理磁盘位置/文件名
mysqldump -hlocalhost -uroot -p123456 shop account >E:/aa.sql
# 导出多个表
# mysqldump -h主机 -u用户名 -p密码 数据库 表1 表2 表3 > 物理磁盘位置/文件名
# 导出数据库
mysqldump -h主机 -u用户名 -p密码 数据库 > 物理磁盘位置/文件名
#导入
#登录的情况下,切换到指定的数据库
# source 备份文件
source e:aa.sql
# 或
mysql -u用户名 -p密码 库名<备份文件
当数据库比较复杂的时候,我们就需要设计了
糟糕的数据库设计:
良好的数据库设计:
软件开发中,关于数据库的设计
分析需求:分析业务和需要处理的数据库的需求
概要设计:设计关系图E-R图
设计数据库步骤:(个人博客)
为什么需要数据规范化
三大范式
第一范式(1NF)
原子性:保证每一列不可再分
第二范式(2NF)
前提:满足第一范式
每张表只描述一件事情
第三范式(3NF)
前提:满足第一范式和第二范式
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关
(规范数据库的设计)
规范性 和 性能的问题
关联查询的表不得超过三张表
驱动:声卡、显卡、数据库 都是需要驱动才能运行的
应用程序 会先连接数据库的驱动 后 再从驱动连接数据库 而不是能直接连接数据库
SUN公司为了简化开发人员的(对数据库的统一) 操作,提供了一个(Java操作数据库的)规范,俗称JDBC
这些规范的实现由具体的厂商去做
对于开发人员来说,我们只需要掌握JDBC接口的操作即可!
需要导入 java.sql javax.sql 还需要导入一个数据库驱动包 mysql-connector-java-5.1.47.jar
先创建测试数据库
CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;
USE jdbcStudy;
CREATE TABLE users(
`id` INT PRIMARY KEY,
`name` VARCHAR(40),
`password` VARCHAR(60),
`email` VARCHAR(60),
`birthday` DATE
);
INSERT INTO users(id,`name`,`password`,email,birthday)
VALUES(1,'zhangsan','123456','[email protected]','1980-12-04'),
(2,'lishi','123456','[email protected]','1985-12-04'),
(3,'wangwu','123456','[email protected]','1981-12-04');
创建一个项目
导入数据库驱动,自己下载jar包导入也行 用maven导入也行(推荐) mysql-connector-java-5.1.47.jar
编写测试代码
package com.kuang.jdbc01;
import java.sql.*;
// 第一个JDBC程序
public class test01 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动
//2.用户信息和url
//"jdbc:mysql://localhost:3306/数据库名?支持中文编码&设置中文字符集=utf8&use使用安全的一个连接";
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
//用户名
String username = "root";
//密码
String password = "123456";
//3.连接成功 后,会返回 数据库对象
//驱动管理DriverManager 获得连接getConnection 代表数据库connection
Connection connection = DriverManager.getConnection(url, username, password);
//4.执行SQL的对象 用statement执行sql对象
Statement statement = connection.createStatement();
//5.执行SQL对象 去 执行SQL, 可能存在结果,查看返回结果
String sql = "SELECT * FROM users";
// 查询 sql
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
// DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动
// connection 代表数据库
//数据库设计自动提交
//事务提交
//事物回滚
connection.rollback();
connection.commit();
connection.setAutoCommit();
URL
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
//mysql -- 3306
// sTRING URL = 协议://主机地址+端口号/数据库名?参数1&参数2&参数3
// oralce --1521
// jdbc:oracle:thin:@localhost:1521:sid
Statement执行SQL的对象
String sql = "SELECT * FROM users"; //编写SQL
statement.executeQuery(); //查询操作 返回ResultSet(结果集)
statement.execute(); //执行任何sql
statement.executeUpdate(); //更新、插入、删除、都使用这个,返回一个受影响的行数
ResultSet 查询的结果集:封装了所有的查询结果
获得指定的数据类型
resultSet.getObject();// 在不知道列类型的情况下使用
//如果知道列的类型就使用指定的类型
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getTime();
...
遍历,指针
resultSet.beforeFirst();//移动到最前面
resultSet.afterLast();//移动到最后面
resultSet.next();//移动到下一个数据
resultSet.previous();//移动到下一行
resultSet.absolute(row);//指定到移动行
释放资源
resultSet.close();
statement.close();
connection.close();
//耗资源 ,用完关掉
jdbc中的 statement 对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可
Statement 对象的 executeUpdate 方法,用于向数据库发送增、删、改的sql语句,executeUpdate 执行完后,将会返回一个整数(即增删改语句导致了数据库几行数据发生了变化)。
Statement.executeQuery 方法用于向数据库发送查询语句,executeQuery 方法返回代表查询结果的ResultSet对象
CRUD操作-create
使用 executeUpdate (String sql)方法完成数据添加操作,示例操作:
Statement statement = connection.createStatement();
String sql = "insert into users(...)values(...)";
int num = statement.executeUpdate(sql);
if(num>0){
System.out.println("插入成功!!!");
}
CRUD操作-delete
使用 executeUpdate(String sql)方法完成数据删除操作,示例操作:
Statement statement = connection.createStatement();
String sql = "delete from users where id=1";
int sum = statement.executeUpdate(sql);
if(sum>0){
System.out.println("删除成功!!!");
}
CRUD操作-update
使用executeUpdate(String sql)方法完成数据修改操作,示例操作:
Statement statement = connection.createStatement();
String sql = "update users set name = '' where name = ''";
int sum = statement.executeUpdate(sql);
if(sum>0){
System.out.println("修改成功");
}
CRUD操作-read
使用 executeQuery(String sql)方法完成数据查询操作,实例操作:
Statement statement = connection.createStatement();
String sql = "select * from users where id=1";
ResultSet rs = statement.executeQuery(sql);
while(rs.next()){
//根据获取列的数据类型,分别调用rs的相应方法映射到java对象中
}
1、创建配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456
2、提取工具类
package com.kuang.jdbc02.utils;
import 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{
try{
//反射获取类的装载器,然后在获取资源(db.properties)
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
//属性
Properties properties = new Properties();
//加载获取到的属性
properties.load(in);
// 从文件把这4个信息读取出来
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();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
//释放资源
public static void release(Connection conn, Statement sta, ResultSet res) throws SQLException {
if (res!=null){
res.close();
}
if (sta!=null){
sta.close();
}
if (conn!=null){
conn.close();
}
}
}
3、编写增删改
增 insert
package com.kuang.jdbc02;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class TestInsert {
public static void main(String[] args) throws SQLException {
Connection connection = JdbcUtils.getConnection(); //通过工具类获取链接
Statement statement = connection.createStatement();//获得SQL的执行对象
String sql = "INSERT INTO users(id,`name`,`password`,`email`,`birthday`) VALUES(5,'fenqi','123456','[email protected]','1985-12-04')";
int i = statement.executeUpdate(sql);
if (i>0){
System.out.println("插入成功");
}
JdbcUtils.release(null,null,null); //释放资源
}
}
删 Delete
package com.kuang.jdbc02;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestSelete {
public static void main(String[] args) throws SQLException {
//通过工具类获取数据库连接
Connection connection = JdbcUtils.getConnection();
//获得SQL的执行对象
Statement statement = connection.createStatement();
//编写SQL语句
String sql = "select * from users";
//执行
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()){
System.out.println("id"+resultSet.getInt("id"));
System.out.println("name"+resultSet.getString("name"));
System.out.println("password"+resultSet.getString("password"));
System.out.println("email"+resultSet.getString("email"));
System.out.println("birthday"+resultSet.getDate("birthday"));
System.out.println("=================");
}
JdbcUtils.release(null,null,null);
}
}
改 Update
package com.kuang.jdbc02;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class TestUpdate {
public static void main(String[] args) throws SQLException {
//通过工具类获取连接
Connection connection = JdbcUtils.getConnection();
//获得SQL的执行对象
Statement statement = connection.createStatement();
//编写SQL语句
String sql = "update users set name='xiugai' where id = 1";
//执行
int i = statement.executeUpdate(sql);
if (i>0){
System.out.println("更新成功");
}
JdbcUtils.release(null,null,null);
}
}
查 Select
package com.kuang.jdbc02;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestSelete {
public static void main(String[] args) throws SQLException {
//通过工具类获取数据库连接
Connection connection = JdbcUtils.getConnection();
//获得SQL的执行对象
Statement statement = connection.createStatement();
//编写SQL语句
String sql = "select * from users";
//执行
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()){
System.out.println("id"+resultSet.getInt("id"));
System.out.println("name"+resultSet.getString("name"));
System.out.println("password"+resultSet.getString("password"));
System.out.println("email"+resultSet.getString("email"));
System.out.println("birthday"+resultSet.getDate("birthday"));
System.out.println("=================");
}
JdbcUtils.release(null,null,null);
}
}
sql存在漏洞,会被攻击导致数据泄露,SQL会被拼接or
package com.kuang.jdbc02;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class SQL注入 {
public static void main(String[] args) throws SQLException {
//正常登录
// login("lishi","123456");
login(" 'or' 1=1"," 'or' 1=1");
}
public static void login(String username,String password) throws SQLException {
//通过工具类获取数据库连接
Connection connection = JdbcUtils.getConnection();
//获得SQL的执行对象
Statement statement = connection.createStatement();
//编写SQL语句
String sql = "select * from users where `name` = '"+username+"' and password='"+password+"'";
//执行
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()){
System.out.println(resultSet.getString("name"));
System.out.println(resultSet.getString("password"));
System.out.println("======================");
}
}
}
PreparedStatement 可以防止SQL注入。效果更好
1、新增
package com.kuang.jdbc03;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.util.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestInsert {
public static void main(String[] args) throws SQLException {
//调用工具类获取数据库连接
Connection connection = JdbcUtils.getConnection();
//使用? 占位符代表参数
String sql = "insert into users(id,name,password,email,birthday)values(?,?,?,?,?)";
//预编译SQL,先写sql,然后不执行
PreparedStatement pr = connection.prepareStatement(sql);
//手动给参数赋值
pr.setInt(1,5); //第一个值为参数下标,第二个为参数 id
pr.setString(2,"王胜");
pr.setString(3,"123456");
pr.setString(4,"[email protected]");
// 注意点:sql.Date 数据库 java.sql.Date()
// util.Date Java new Date().getTime() 获得时间戳
pr.setDate(5,new java.sql.Date(new Date().getTime()));
//执行
int i = pr.executeUpdate();
if (i>0){
System.out.println("插入成功");
}
JdbcUtils.release(null,null,null);
}
}
2、删除
package com.kuang.jdbc03;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestDelete {
public static void main(String[] args) throws SQLException {
//通过工具类获取数据库的连接
Connection connection = JdbcUtils.getConnection();
//使用占位符? 代表参数
String sql = "delete from users where id = ?";
//预编译SQL 先写sql 然后不执行
PreparedStatement pre = connection.prepareStatement(sql);
//手动给参数赋值
pre.setInt(1,5);
//执行
int i = pre.executeUpdate();
if (i>0){
System.out.println("删除成功");
}
//释放资源
JdbcUtils.release(null,null,null);
}
}
3、更新
package com.kuang.jdbc03;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestUpdate {
public static void main(String[] args) throws SQLException {
//通过工具类获取数据库的连接
Connection connection = JdbcUtils.getConnection();
//使用占位符? 代表参数
String sql = "update users set name = ? where name = ?";
//预编译SQL 先写SQL 然后不执行
PreparedStatement pre = connection.prepareStatement(sql);
//手动给参数赋值
pre.setString(1,"zhangsan");
pre.setString(2,"xiugai");
//执行
int i = pre.executeUpdate();
if (i>0){
System.out.println("修改成功");
}
//关闭资源
JdbcUtils.release(null,null,null);
}
}
4、查询
package com.kuang.jdbc03;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TestSelete {
public static void main(String[] args) throws SQLException {
//通过工具类获取数据库的连接
Connection connection = JdbcUtils.getConnection();
//使用占位符? 代表参数
String sql = "select * from users where id=?";
//预编译SQL 先写sql 然后不执行
PreparedStatement pre = connection.prepareStatement(sql);
//手动传递参数
pre.setObject(1,2);
//执行
ResultSet resultSet = pre.executeQuery();
while (resultSet.next()){
System.out.println(resultSet.getInt("id"));
System.out.println(resultSet.getString("name"));
System.out.println(resultSet.getString("password"));
}
//释放资源
JdbcUtils.release(null,null,null);
}
}
5、防止Sql注入
package com.kuang.jdbc03;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.*;
public class SQL注入 {
public static void main(String[] args) throws SQLException {
//正常登录
// login("lishi","123456");
login("'' or 1=1","123456");
}
public static void login(String username,String password) throws SQLException {
//通过工具类获取数据库连接
Connection connection = JdbcUtils.getConnection();
//使用占位符? 传递参数
//PreparedStatement放置SQL注入的本质,把值传递进来的参数当作字符
//假设其中存在转义字符,比如说 ‘ 会被直接转义
String sql = "select * from users where name = ? and password = ?";
//预编译Sql 先写sql 然后不执行
PreparedStatement pre = connection.prepareStatement(sql);
//手动传递参数
pre.setString(1,username);
pre.setString(2,password);
//执行
ResultSet resultSet = pre.executeQuery();
while(resultSet.next()){
System.out.println(resultSet.getString("name"));
System.out.println(resultSet.getString("password"));
System.out.println("======================");
}
//释放资源
JdbcUtils.release(null,null,null);
}
}
找到右侧的Database 点击进去
点击 + 号 → 选择Data Source →选择MySql
然后输入用户名 和 密码 → 点击 Test Connection
成功的话会显示✔
不成功的话,要么是时区异常要么就是版本没选好
左侧NySQL点击进去
Class选择 com.musql.jdbc.Driver
Driver files 选择你对应的SQL版本 → 然后就可以点击Apply → 然后就去重新输入账号密码连接
时区的话就需要打开命令行的sql然后设计时区了
-- 查看时区,显示 SYSTEM 就是没有设置时区啦。
show variables like'%time_zone';
--设置时区 设置完需要重新连接sql重新查看
set global time_zone = '+8:00';
要么都成功,要么都失败
ACID原则
原子性:
要么全部完成,要么都不完成
一致性:
总数不变
隔离性:
多个进程互不干扰
持久性:
一旦提交不可逆,持久化到数据库
隔离性问题:
脏读:一个事务读取了另一个没有提交的事物
不可重复读:在同一个事物内,重复读取表中的数据,表数据发生了改变
虚读(幻读):在一个事务内,读取到了别人插入的数据,导致前后读出来结果不一致
代码实现
1、开启事务 conn.setAutoCommit(false);
2、一组业务执行完毕,提交事务
3、可以在catch语句中显示的定义 回滚语句,但默认失败就会回滚
package com.kuang.jdbc04;
import com.kuang.jdbc02.utils.JdbcUtils;
import com.sun.org.apache.xpath.internal.objects.XNull;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestTransation1 {
public static void main(String[] args) throws SQLException {
Connection conn = null;
PreparedStatement st = null;
try {
//通过工具类连接数据库
conn = JdbcUtils.getConnection();
//关闭数据库的自动提交,会自动开启事物
conn.setAutoCommit(false);
//编写SQL语句
String sql1 = "update account set miney = miney-100 where name = 'A'";
//预编译SQL
st = conn.prepareStatement(sql1);
st.executeUpdate();
//编写SQL语句
String sql2 = "update account set miney = miney+100 where name = 'B'";
//预编译SQL
st= conn.prepareStatement(sql2);
st.executeUpdate();
//业务完毕,提交事务
conn.commit();
System.out.println("成功");
}catch (Exception e){
try{
conn.rollback(); //如果失败则回滚事物
} catch (SQLException throwables)
{
throwables.printStackTrace();
}
e.printStackTrace();
}finally {
//释放资源
JdbcUtils.release(conn,st,null);
}
}
}
数据库连接 - 执行完毕 - 释放
连接-释放 十分浪费系统资源
池化技术:准备一些预先的资源,过来就连接预先准备好的
例子(银行)
比如去银行办理业务,开门-办理-关门 下一个 开门-办理-关门, 这是平常数据库的操作,很繁琐麻烦
使用连接池 开门-安排业务员-等待-办理-不使用了就彻底关门
常用连接数10个
最小连接数:10(按照常用连接数设计最小连接数)
最大连接数:(业务最高承载上线,超过了就需要等待排队)
等待超时:(超过等待的时间就不等了)
编写连接池,只需要实现一个接口 DataSource
开源数据源实现(拿来即用)
DBCP
C3P0
Druid:阿里巴巴
使用了这些数据库连接池之后我们在项目开发中就不需要编写连接数据库的代码了!
DBCP
需要用到的jar包
commons-dbcp-1.4、commons-pool-1.6
C3P0
需要用到的jar包
c3p0-0.9.5.5、mchange-commons-java-0.2.19
结论
无论使用什么数据源,本质还是一样的 ,DataSource接口不会变,方法就不会变# MySql
解压
把这个包放到自己的电脑环境目录下~
配置环境变量
新建mysql配置文件
[mysqld]
# 目录要换成自己的
basedir=D:\mysql\mysql-8.0.21\
datadir=D:\mysql\mysql-8.0.21\data\
port=3306
skip-grant-tables
启动管理员模式下的CMD,进入到mysql的bin目录下
安装mysql服务
初始化数据库文件
启动
默认密码为空,登录命令
密码修改 由于是Mysql8.0,所以不支持password函数的使用
刷新:flush privileges; 刷新后退出:exit; 退出再重新登录即可
--连接数据库
mysql -u root -p123456
--修改用户密码
update mysql.user set authentication_string=password('123456') where user='root' and Host = 'localhost';
-- 查看时区,显示 SYSTEM 就是没有设置时区啦。
show variables like'%time_zone';
--设置时区 设置完需要重新连接sql重新查看
set global time_zone = '+8:00';
--刷新权限
flush privileges;
--所有的sql语句都使用;结尾
--查看所有的数据库
show databases;
--切换数据库
use 数据库名
--查看数据库中所有的表
show tables;
--显示表中所有的信息
describe 表名
---------------------
--创建一个数据库
create database 数据库名
--退出连接
exit;
--单行注释
/*多行注释*/
数据库xxx语言
DDL 数据库控制语言
DML 数据库操作语言
DQL 数据库查询语言
DCL 数据库控制语言
操作数据库>操作数据库中的表>操作数据库中表的数据
mysql关键字不区分大小写 [ ]表示可选
**如果你的表明或数据库名是一个特殊字符的话要加`` **
创建数据库
CREATE DATABASE [IF NOT EXISTS] 数据库名
删除数据库
DROP DATABASE IF EXISTS 数据库名
使用数据库
USE `数据库名`
查看数据库
SHOW DATABASE
数值
字符串
时间日期
null
Unsigned:
zerofill:
自增
非空NULL not null
-- 每一个表都必须存在以下五个字段,未来做项目用的,表示一个表记录存在的意义
id 主键
version 乐观锁
is_delete 伪删除
gmt_create 创建时间
gmt_update 修改时间
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 '密码',
`set` 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
格式: [ ] 表示可选的 IF NOT EXISTS判断是否存在
CREATE TABLE [IF NOT EXISTS] `表名`(
`字段名` 列类型 [非空] [自增] [注释] '学号',
`字段名` 列类型 [非空] [默认值] '匿名' [注释] '姓名',
.....(注意最后一行不要加,)
)[引擎](ENGINE=INNODB) [字符集](CHARSET=utf8)
常用命令
-- 查看数据库的语句
SHOW CREATE DATABASE 数据库名;
-- 查看表中数据的定义语句
SHOW CREATE TABLE 表名;
-- 显示表的结构
DESC 表名;
修改
-- 修改表名 ALTER TABLE 旧表名 RENAME AS 新表名;
ALTER TABLE student RENAME AS student1;
-- 增加表的字段 ALTER TABLE 表名 ADD 字段名 列属性;
ALTER TABLE student1 ADD age INT(11);
-- 修改表的字段约束 ALTER TABLE 表名 MODIFY 字段名 列属性;
ALTER TABLE student1 MODIFY age VARCHAR(20);
-- 修改表字段名 ALTER TABLE 表名 CHANGE 旧字段 新字段 列属性;
ALTER TABLE student1 CHANGE age age1 INT(1);
-- 删除表字段 ALTER TABLE 表名 DROP 字段名;
ALTER TABLE student1 DROP age1;
删除
-- 删除表 (判断表存在 在删除)
DROP TABLE IF EXISTS student1;
方式一、在创建表的时候,增加约束(麻烦,比较复杂)
CREATE TABLE IF NOT EXISTS `grade`(
`gradeid` INT(10) NOT NULL AUTO_INCREMENT COMMENT '年级id',
`gradename` VARCHAR(50) NOT NULL COMMENT '年级名称',
PRIMARY KEY(`gradeid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
-- 学生表的gradeid字段要去引用年级表的gradeid
-- 定义外键 key
-- 给这个外键添加约束(执行引用) references 引用
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 '密码',
`set` 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`), --定义外键 key
CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`) --
)ENGINE=INNODB DEFAULT CHARSET=utf8
删除有外键关系的表时,必须先删除引用别人的表(从表),在删除被引用的表(主表)
方式二、创建表成功后,添加外键约束
CREATE TABLE IF NOT EXISTS `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 '密码',
`set` 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 表名 ADD CONSTRAINT 约束名 FOREIGN KEY(作为外键的列) REFERENCES 哪个表(哪个字段);
ALTER TABLE `student` ADD CONSTRAINT `FK_gradeid` FOREIGN KEY(`gradeid`) REFERENCES `grade`(`gradeid`);
数据库的意义 :数据存储,数据管理
DML语言:数据操作语言
insert
-- insert into 表名(字段1,字段2,...)values(值1),(值2)
-- 一般写插入语句,我们一定要数据和字段一一对应
INSERT INTO `grade`(`gradeid`,`gradename`)VALUES(1,'大一'),(2,'大二');
语法:insert into 表名(字段1,字段2,…)values(值1),(值2)…
注意事项:
apdate
UPDATE `grade` SET `gradename` = '大1' WHERE `gradeid` = 1;
-- 不指定条件的情况下,会改动表里的这个字段全部的值
UPDATE `grade` SET `gradename` = '大2';
-- 修改多个属性 逗号隔开
UPDATE `grade` SET `gradeid` = 11,`gradename` = '大11' WHERE `gradeid` = 1;
-- 语法
update 表名 set 字段(需要修改的哪个字段) = 值 ,.... where =
条件:where 子句 运算符 id 等于某个值 ,大于某个值,在某个区间内修改…
操作符会返回布尔值
操作符 | 含义 | 范围 | 结果 |
---|---|---|---|
= | 等于 | 5=6 | false |
<>或!= | 不等于 | 5<>6 | true |
> | 大于 | 5>4 | true |
< | 小于 | 5<6 | true |
>= | 大于等于 | 5>=4 | true |
<= | 小于等于 | 5<=6 | true |
BETWEEN…AND… | 在某个范围内比如2~5 | [2,5] | |
AND | 我和你&& | 5>1 and 1>2 | false |
OR | 我或你|| | 5>1 or 1>2 | true |
语法:UPDATE 表名SET colnum_name=value,… WHERE [条件]
注意:
delete 命令
语法:delete from 表名 [where 条件]
-- 删除数据(避免这样写,会全部删除)
DELETE FROM 数据库名;
-- 删除指定数据
DELETE FROM 数据库名 WHERE 条件;
truncate命令
作用:完全清空一个数据库表,表的结构和索引约束不会变!
-- 清空表
TRUNCATE 表名;
delete跟truncate区别
了解即可:delete删除问题,重启数据库现象
select 语法 [ ] 代表可选的 { } 代表必选的
select [all | distinct] -- 去重
{
* | table.* | [table.field1[as alias1],[,table.field2[as alias2]][....]} -- 要查询的字段
from table_name [as table_alias] -- (from 表名 as取别名)
[left | right | inner join table_name2] -- 联合查询 (... join 要连接的表 on 等值判断)
[where ...] -- 指定结果需满足的条件(具体的值,子查询语句)
[group by ...] -- 指定结果按照哪几个字段来分组(通过哪个字段来进行分组)
[having] -- 过滤分组的记录必须满足的次要条件 (过滤分组后的信息,条件和where是一样的,就是上下位置不同)
[order by ...] -- 指定查询记录按一个或多个条件排序 (通过哪个字段排序)[升序/降序]
[limit {
[offset,]row_count | row_countOFFSET offset}]; -- 指定查询的记录从哪条至哪条 (起始下标值,页面有多少条记录)
-- 查询表中全部信息
-- SELECT * FROM 表名;
SELECT * FROM `student`;
-- 查询指定字段
-- SELECT 字段1,字段2... FROM 表名;
SELECT `StudentName`,`Address` FROM `student`;
-- 别名 给结果起一个别名
-- SELECT 字段1 AS 别名,字段2 AS 别名 FROM 表名;
SELECT `StudentName` AS 姓名,`Address`AS 地区 FROM `student`;
-- 函数 concat(a,b)拼接
SELECT CONCAT('姓名:',StudentName) AS 名字 FROM `student`;
语法:select 字段,…from 表名
有的时候,列名字不是那么的见名知意。就起别名 AS 字段名 as 别名
去重复 distinct
作用:去除select查询出来的结果中重复的数据,重复的数据只显示一条
-- 发现重复数据,去重
-- SELECT DISTINCT 字段名 FROM 表名;
SELECT DISTINCT `StudentNo` FROM `result`;
数据库的列(表达式)
-- 查询系统版本
SELECT VERSION();
-- 用来计算
SELECT 100*3-1 AS 计算结果
-- 查询自增的步长
SELECT @@auto_increment_increment;
作用:检索数据中符合条件的值
搜索的条件由一个或多个表达式组成!结果 布尔值
运算符 | 语法 | 描述 |
---|---|---|
and && | a and b 或 a&&b | 逻辑与 |
or || | a or b 或 a||b | 逻辑或 |
Not ! | not a 或 !a | 逻辑非 |
语法:
and
select 字段名 from 表名 where 字段名=5 and 字段名=6;
or
select 字段名 from 表名 where 字段名=6 or 字段名=7;
not
select 字段名 from 表名 where not 字段名=5;
运算符 | 语法 | 描述 |
---|---|---|
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…其中的某一个值中,结果为真 |
-- Like结合 %(代表0到任意一个字符) _ (表示一个字符)
SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE `SubjectName` LIKE '高%';
SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE `SubjectName` LIKE 'C#__';
SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE SubjectName LIKE '%数%';
-- in (具体的一个或多个值)
SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE SubjectNo IN (3,5,2,13);
-- not null 相当于不为空
SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE SubjectNo IS NOT NULL;
JOIN 对比
思路是必看的,很有用
-- 查询参加了考试的同学(学号,姓名,科目编号,分数)
SELECT * FROM `student`;
SELECT * FROM `result`;
/* 思路
1.分析需求,分析查询的字段来自哪张表,如果超过一张表就使用(连接查询)
2.确定使用哪种连接查询? 有7种
确定交叉点(这两个表中哪个数据是相同的)如果没有交叉点是查不了东西的
判断的条件就是, 学生表中 StudentNo = 成绩表中 StudentNo
就是学生表拥有StudentNo 成绩表也拥有 StudentNo 共同字段
*/
-- join(连接的表) on(判断的条件) 连接查询
-- where 等值查询
-- inner join 内连接--
SELECT s.StudentNo,StudentName,SubjectNo,StudentResult -- 因为有两个同样的字段,所以需要表名区引用,要不然不知道是哪个表的字段
FROM student AS s
INNER JOIN result AS r -- 定义了别名,就可以用别名区访问东西了,当然表名也可以,只是会稍微繁琐
WHERE s.StudentNo = r.StudentNo -- 判两张表是否都有这个字段
-- right join 右连接查询--
SELECT student.StudentNo,StudentName,SubjectNo,StudentResult
FROM student
RIGHT JOIN result -- 这是不用别名,用表名的操作,比较繁琐
ON student.StudentNo = result.StudentNo
-- left join 左连接查询--
SELECT s.StudentNo,StudentName,SubjectNo,StudentResult
FROM student s
LEFT JOIN result r -- 别名的 AS 是可以省略的
ON s.StudentNo = r.StudentNo
操作 | 描述 |
---|---|
inner join | 并集 ,只要有一张表有这个数据就可以拿到,就是多表中有一个匹配就行了 |
left join | 左连接,会从左表中返回所有的值,即使右表中没有匹配 |
right join | 右连接,会从右表中返回所有的值,即使左表中没有匹配 |
-- (查询了参加考试同学的信息:学号,学生姓名,科目名,分数) (StudentNo StudentName SubjectName StudentResult)
/* 思路
1.分析需求,分析查询的字段来自哪张表,如果超过一张表就使用(连接查询) 来自( student , result , subject )表
2.确定使用哪种连接查询? 有7种
确定交叉点(这两个表中哪个数据是相同的)如果没有交叉点是查不了东西的
*/
SELECT s.`StudentNo`,`StudentName`,`SubjectName`,`StudentResult`
FROM `student` AS s
RIGHT JOIN `result` AS r
ON s.`StudentNo` = r.`StudentNo`
INNER JOIN `subject` AS sub
ON r.`SubjectNo` = sub.`SubjectNo`
-- 步骤
/*
我要查询哪些数据 select ....
从哪几个表中查 from 表 inner(right,left) join 连接的表 on 交叉条件
假设存在多张表查询,先查询两张表然后再慢慢增加
自连接
自己的表和自己的表连接,核心:一张表拆成两张一样的表即可
-- ==========自连接==============
CREATE TABLE categorg (
categoryid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主题id',
pid INT(10) NOT NULL COMMENT '父id',
categoryName VARCHAR(50) NOT NULL COMMENT '主题名字',
PRIMARY KEY (categoryid)
)ENGINE=INNODB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8
INSERT INTO categorg(categoryid,pid,categoryName)
VALUES(2,1,'信息技术'),
(3,1,'软件开发'),
(4,3,'数据库'),
(5,1,'美术设计'),
(6,3,'web开发'),
(7,5,'ps技术'),
(8,2,'办公信息');
-- 查询父子信息
SELECT f.categoryName AS '父栏目',z.categoryName AS '子栏目'
FROM categorg AS f,categorg AS z
WHERE f.categoryid = z.pid
-- 就相当于 categoryid的值 = pid的值
-- 比如 办公信息的2=信息技术的2 因为1不匹配 所以是单独的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KHFF2bTB-1597917601699)(C:\Users\黄潮旬\AppData\Roaming\Typora\typora-user-images\image-20200814100757157.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JoTsi5x5-1597917601702)(C:\Users\黄潮旬\AppData\Roaming\Typora\typora-user-images\image-20200814100827000.png)]
排序 语法:ORDER BY 排序的字段 ASC 或 DESC
-- order by排序:升序asc,降序desc
-- 语法 ORDER BY 排序的字段 ASC(DESC)
-- 根据查询的结果进行排序
SELECT s.StudentNo,StudentName,SubjectName,StudentResult
FROM student s
INNER JOIN `result` r
ON s.StudentNo=r.StudentNo
INNER JOIN `subject` sub
ON r.`SubjectNo` = sub.`SubjectNo`
ORDER BY StudentResult ASC
分页 语法: limit 起始下标值,页面有多少条记录
-- 为什么要分页?
-- 为了缓解数据库压力,给人的体验更好, 还有一种不分页的叫做瀑布流
SELECT s.StudentNo,StudentName,SubjectName,StudentResult
FROM student s
INNER JOIN `result` r
ON s.StudentNo=r.StudentNo
INNER JOIN `subject` sub
ON r.`SubjectNo` = sub.`SubjectNo`
ORDER BY StudentResult ASC
LIMIT 0,5 -- 语法 limit 起始下标值,页面有多少条记录
where (这个值是计算出来的)
使用子查询 (由里及外) 适用于,后面还加条件的
本质:在 where 语句中嵌套一个子查询语句
-- 查询高等数学的所有考试结果(学号 科目名称 成绩),降序查询
-- 方式一: 使用连接查询
SELECT `StudentNo`,SubjectName,StudentResult
FROM `result` AS r
INNER JOIN `subject` sub
ON r.`SubjectNo` = sub.`SubjectNo`
WHERE SubjectName LIKE '高等数学%'
ORDER BY StudentResult DESC
-- 方式二:使用子查询 (由里及外) 适用于,后面还加条件的
-- 查询所有考试结果(学号 科目名称 成绩)>70分的,
SELECT `StudentNo`,SubjectName,StudentResult
FROM `result` AS r
INNER JOIN `subject` sub
ON r.`SubjectNo` = sub.`SubjectNo`
WHERE StudentResult>=70
-- 在基础上增加一个 且是高等数学的
AND SubjectName = (
SELECT SubjectName FROM `subject`
WHERE SubjectName = '高等数学-1'
)
-- 改造方式二:查询学生学号,学生姓名 成绩大于70分的
SELECT StudentNo,StudentName FROM student WHERE StudentNo IN (
SELECT StudentNo FROM result WHERE StudentResult>70 AND SubjectNo =(
SELECT SubjectNo FROM `subject` WHERE SubjectName = '高等数学-1'
)
)
-- 查询不同课程的平均分,最高分,最低分,平均分大于90
-- 核心:根据不同的课程分组 不分组的话只有一个,按照最高的 最低的来排 因为每个课程都有最高分最低分
SELECT SubjectName,AVG(StudentResult)AS 平均分,MAX(StudentResult)AS 最大值,MIN(StudentResult)AS 最小值
FROM `result` AS r
INNER JOIN `subject` AS sub
ON r.`SubjectNo` = sub.`SubjectNo`
GROUP BY r.SubjectNo -- 指定结果按照哪几个字段来分组
HAVING 平均分>=80 -- 过滤分组的记录必须满足的次要条件
官网:https://dev.mysql.com/doc/refman/5.7/en/func-op-summary-ref.html
-- 数学运算
SELECT ABS(-8) -- abs 绝对值
SELECT CEILING(9.4) -- ceiling 向上取整
SELECT FLOOR(9.4) -- floor 向下取整
SELECT RAND() -- rand 返回一个0~1之间的随机数
SELECT SIGN(7) -- sign 判断一个数的符号 负数返回-1 正数返回1
-- 字符串函数
SELECT CHAR_LENGTH('dffhf附件四') -- char_length 字符串长度
SELECT CONCAT('热','爱','编程') -- concat 拼接字符串
SELECT INSERT('我热爱编程helloword',2,3,'我超级喜欢') -- insert 查询 从某个位置开始 替换某个长度
SELECT LOWER('ASDDlele') -- lower 小写字母
SELECT UPPER('asddLELE') -- upper 大写字母
SELECT INSTR('abcdeh','h') -- instr 返回第一次出现的字串的索引
SELECT REPLACE('坚持就一定能成功','坚持','努力') -- replace 替换出现的指定字符串
SELECT SUBSTR('坚持就一定能成功',4,6) -- substr 返回指定的子字符串 从哪里开始,到哪里结束
SELECT REVERSE('坚持就一定能成功') -- reverse 反转
-- 查询性李的 然后替换名字 利
SELECT REPLACE(StudentName,'李','利') FROM student
WHERE StudentName LIKE '李%'
-- 时间和日期函数(记住)
SELECT CURRENT_DATE() -- current_date 获取当前日期
SELECT CURDATE() -- curdate 获取当前日期
SELECT NOW() -- now 获取当前的时间
SELECT LOCALTIME() -- localtime 本地时间
SELECT SYSDATE() -- sysdate 系统时间
SELECT YEAR(NOW()) -- year 年
SELECT MONTH(NOW()) -- month 月
SELECT DAY(NOW()) -- day 日
SELECT HOUR(NOW()) -- hour 时
SELECT MINUTE(NOW()) -- minute 分
SELECT SECOND(NOW()) -- second 秒
-- 系统
SELECT SYSTEM_USER() -- system_user 获取当前用户
SELECT USER() -- user 获取当前用户
SELECT VERSION() -- version 获取当前版本
函数名称 | 描述 |
---|---|
count() | 计数 |
sum() | 求和 |
avg() | 平均值 |
max() | 最大值 |
min() | 最小值 |
… | … |
-- ========聚合函数========
-- 都能够统计表中的数据
SELECT COUNT(`StudentNo`)FROM student; -- count(字段) 会忽略所有的null值
SELECT COUNT(*) FROM student -- count(*) 不会忽略null值,本质 计算行数
SELECT COUNT(1) FROM student -- count(1) 不会忽略所有的null值,本质 计算行数
SELECT SUM(StudentResult)AS 总和 FROM result; -- sum 总和
SELECT AVG(StudentResult)AS 平均分 FROM result; -- avg 平均分
SELECT MAX(StudentResult)AS 最高分 FROM result; -- max 最大值
SELECT MIN(StudentResult)AS 最低分 FROM result; -- min 最小值
什么是MD5?
主要增强算法复杂度和不可逆转。
MD5不可逆,具体的值的md5是一样的
MD5破解网站的原理,背后有一个字典,MD5加密后的值,加密的前值
-- ============测试MD5 加密=========
CREATE TABLE `testmd5`(
`id` INT(4) NOT NULL,
`name` VARCHAR(20) NOT NULL,
`pwd` VARCHAR(50) NOT NULL,
PRIMARY KEY(`id`)
)
-- 明文密码
INSERT INTO `testmd5` VALUES(1,'zhangsan','123456'),(2,'lishi','123456'),(3,'wangwu','123456');
-- 加密
UPDATE testmd5 SET pwd = MD5(pwd) WHERE id = 1;
-- 加密全部的密码
UPDATE testmd5 SET pwd = MD5(pwd);
-- 插入的时候加密
INSERT INTO testmd5 VALUES(4,'xiaoming',MD5('123456')) ;
-- 如何校验:将用户传递进来的密码,进行md5加密,然后比对加密后的值
SELECT * FROM testmd5 WHERE `name` = 'xiaoming' AND pwd = MD5('123456')
要么都成功,要么都失败
将一组SQL放在一批次中去执行~
事物原则:DCID原则 原子性,一致性,隔离性,持久性 (脏读,幻读…)
原子性(atomicity)
要么都成功,要么都失败
一致性(consistency)
事物前后的数据完整性要保证一致
隔离性(isolation)
事物的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事物,不能被其他事务的操作数据所干扰,事物之间要相互隔离
持久性(durability)
事物一旦提交则不可逆,被持久化到数据库中
隔离所导致的一些问题
脏读
指一个事物读取了另外一个事务未提交的数据。
不可重复读
在一个事务内读取表中的某一行数据,多次读取结果不同。(这个不一定是错误,只是某些场合不对)
虚读(幻读)
是指在一个事务内读取到了别的事物插入的数据,导致前后读取不一致。
-- =============事物
-- mysql 是默认开启事务自动提交的
SET autocommit = 0 -- 关闭
SET autocommit = 1 -- 开启(默认的)
-- 手动处理事物
SET autocommit = 0 -- 关闭自动提交
-- 事物开启
START TRANSACTION -- 标记一个事务的开始,从这个之后的sql都在同一个事物内
-- 提交:持久化(成功)
COMMIT
-- 回滚:回到原来的样子(失败)
ROLLBACK
-- 事务结束
SET autocommit = 1 -- 开启自动提交
-- 了解
SAVEPOINT 保存点名 -- 设置一个事务的保存点
ROLLBACK TO SAVEPOINT 保存点名 -- 回滚到保存点
RELEASE SAVEPOINT 保存点名 -- 撤销保存点
模拟场景
-- 转账例子
CREATE DATABASE shop CHARACTER SET utf8 COLLATE utf8_general_ci
USE shop
CREATE TABLE `account`(
`id` INT(3) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(30) NOT NULL,
`money` DECIMAL(9,2) NOT NULL,
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `account`(`name`,`money`)
VALUES('A',2000.00),('B',10000.00)
-- 模拟转账:事务
SET autocommit = 0; -- 关闭自动提交
START TRANSACTION -- 开启一个事物
UPDATE `account` SET `money` = `money`-500 WHERE `name` = 'A'; -- A-500
UPDATE `account` SET `money` = `money`+500 WHERE `name` = 'B'; -- B+500
COMMIT; -- 提交
ROLLBACK; -- 回滚
SET autocommit = 1; -- 恢复默认值,开启自动提交
mysql官方对索引的定义为:**索引(index)是帮助mysql高效获取数据的数据结构。
提取句子主干,就可以得到索引的本质:索引是数据结构
在一个表中,主键索引只能有一个,唯一索引可以有多个
-- 索引的使用
-- 1. 在创建表的时候给字段增加索引
-- 2. 创建完毕后,增加索引
-- 显示所有的索引信息 SHOW INDEX FROM 表名
SHOW INDEX FROM student
-- 增加一个全文索引(索引名) 列名
ALTER TABLE `school`.`student` ADD FULLTEXT INDEX `StudentName`(`StudentName`);
-- explain 分析sql执行的状况
EXPLAIN SELECT * FROM student; -- 非全文索引
EXPLAIN SELECT * FROM student WHERE MATCH(studentName)AGAINST('李')
CREATE TABLE `app_user` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) DEFAULT '' COMMENT '用户名称',
`email` VARCHAR(50) NOT NULL COMMENT '用户邮箱',
`phone` VARCHAR(20) DEFAULT '' COMMENT '手机号',
`gender` TINYINT(4) UNSIGNED DEFAULT '0' COMMENT '性别(0:男 1:女)',
`password` VARCHAR(100) NOT NULL COMMENT '密码',
`age` TINYINT(4) DEFAULT '0' COMMENT '年龄',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='app用户表'
SHOW CREATE DATABASE `school`
SHOW TABLES;
-- 插入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),'[email protected]',CONCAT('18',FLOOR(RAND()*((999999999-100000000)+100000000))),
FLOOR(RAND()*2),UUID(),FLOOR(RAND()*100));
SET i = i+1;
END WHILE;
RETURN i;
END;
SELECT mock_data();
SELECT * FROM app_user WHERE `name` = '用户500000'; -- 没创建索引之前执行6秒
-- id_表名_字段名
-- create index 索引名 no 表(字段)
CREATE INDEX id_app_user_name ON app_user(`name`);
SELECT * FROM app_user WHERE `id` = 500000; -- 创建索引之后执行0.007秒
索引在小数据量的时候,用处不大,但是在大数据的时候,区别十分明显
索引的数据结构
Hash类型的索引
Btree:InnoDB的默认数据结构
阅读:http://blog.codinglabs.org/articles/theory-of-mysql-index.html
SQL命令操作
用户表:mysql.user
本质:读这张表进行增删改查
-- 创建用户 CREATE USER 用户名 IDENTIFIED BY '密码';
CREATE USER xun IDENTIFIED BY '123456';
-- 修改密码 (修改当前用户密码)
SET PASSWORD = PASSWORD('111111');
-- 修改密码(修改指定用户密码)
SET PASSWORD FOR xun = PASSWORD('111111');
-- 重命名 RENAME USER 原来名字 TO 新的名字;
RENAME USER xun TO xun2;
-- 用户授权 ALL PRIVILEGES 全部的权限 ,库.表
-- ALL PRIVILEGES 除了给别人权限,其他的都能干
GRANT ALL PRIVILEGES ON *.* TO xun2
-- 查询权限
SHOW GRANTS FOR xun2; -- 查看指定用户的权限
SHOW GRANTS FOR root@localhost
-- 撤销权限
REVOKE ALL PRIVILEGES ON *.* FROM xun2
-- 删除用户
DROP USER xun2
为什么要备份:
MySQL数据库备份方式
# mysqldump -h主机 -u用户名 -p密码 数据库 表名 > 物理磁盘位置/文件名
mysqldump -hlocalhost -uroot -p123456 shop account >E:/aa.sql
# 导出多个表
# mysqldump -h主机 -u用户名 -p密码 数据库 表1 表2 表3 > 物理磁盘位置/文件名
# 导出数据库
mysqldump -h主机 -u用户名 -p密码 数据库 > 物理磁盘位置/文件名
#导入
#登录的情况下,切换到指定的数据库
# source 备份文件
source e:aa.sql
# 或
mysql -u用户名 -p密码 库名<备份文件
当数据库比较复杂的时候,我们就需要设计了
糟糕的数据库设计:
良好的数据库设计:
软件开发中,关于数据库的设计
分析需求:分析业务和需要处理的数据库的需求
概要设计:设计关系图E-R图
设计数据库步骤:(个人博客)
为什么需要数据规范化
三大范式
第一范式(1NF)
原子性:保证每一列不可再分
第二范式(2NF)
前提:满足第一范式
每张表只描述一件事情
第三范式(3NF)
前提:满足第一范式和第二范式
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关
(规范数据库的设计)
规范性 和 性能的问题
关联查询的表不得超过三张表
驱动:声卡、显卡、数据库 都是需要驱动才能运行的
应用程序 会先连接数据库的驱动 后 再从驱动连接数据库 而不是能直接连接数据库
SUN公司为了简化开发人员的(对数据库的统一) 操作,提供了一个(Java操作数据库的)规范,俗称JDBC
这些规范的实现由具体的厂商去做
对于开发人员来说,我们只需要掌握JDBC接口的操作即可!
需要导入 java.sql javax.sql 还需要导入一个数据库驱动包 mysql-connector-java-5.1.47.jar
先创建测试数据库
CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;
USE jdbcStudy;
CREATE TABLE users(
`id` INT PRIMARY KEY,
`name` VARCHAR(40),
`password` VARCHAR(60),
`email` VARCHAR(60),
`birthday` DATE
);
INSERT INTO users(id,`name`,`password`,email,birthday)
VALUES(1,'zhangsan','123456','[email protected]','1980-12-04'),
(2,'lishi','123456','[email protected]','1985-12-04'),
(3,'wangwu','123456','[email protected]','1981-12-04');
创建一个项目
导入数据库驱动,自己下载jar包导入也行 用maven导入也行(推荐) mysql-connector-java-5.1.47.jar
编写测试代码
package com.kuang.jdbc01;
import java.sql.*;
// 第一个JDBC程序
public class test01 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动
//2.用户信息和url
//"jdbc:mysql://localhost:3306/数据库名?支持中文编码&设置中文字符集=utf8&use使用安全的一个连接";
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
//用户名
String username = "root";
//密码
String password = "123456";
//3.连接成功 后,会返回 数据库对象
//驱动管理DriverManager 获得连接getConnection 代表数据库connection
Connection connection = DriverManager.getConnection(url, username, password);
//4.执行SQL的对象 用statement执行sql对象
Statement statement = connection.createStatement();
//5.执行SQL对象 去 执行SQL, 可能存在结果,查看返回结果
String sql = "SELECT * FROM users";
// 查询 sql
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
// DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动
// connection 代表数据库
//数据库设计自动提交
//事务提交
//事物回滚
connection.rollback();
connection.commit();
connection.setAutoCommit();
URL
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
//mysql -- 3306
// sTRING URL = 协议://主机地址+端口号/数据库名?参数1&参数2&参数3
// oralce --1521
// jdbc:oracle:thin:@localhost:1521:sid
Statement执行SQL的对象
String sql = "SELECT * FROM users"; //编写SQL
statement.executeQuery(); //查询操作 返回ResultSet(结果集)
statement.execute(); //执行任何sql
statement.executeUpdate(); //更新、插入、删除、都使用这个,返回一个受影响的行数
ResultSet 查询的结果集:封装了所有的查询结果
获得指定的数据类型
resultSet.getObject();// 在不知道列类型的情况下使用
//如果知道列的类型就使用指定的类型
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getTime();
...
遍历,指针
resultSet.beforeFirst();//移动到最前面
resultSet.afterLast();//移动到最后面
resultSet.next();//移动到下一个数据
resultSet.previous();//移动到下一行
resultSet.absolute(row);//指定到移动行
释放资源
resultSet.close();
statement.close();
connection.close();
//耗资源 ,用完关掉
jdbc中的 statement 对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可
Statement 对象的 executeUpdate 方法,用于向数据库发送增、删、改的sql语句,executeUpdate 执行完后,将会返回一个整数(即增删改语句导致了数据库几行数据发生了变化)。
Statement.executeQuery 方法用于向数据库发送查询语句,executeQuery 方法返回代表查询结果的ResultSet对象
CRUD操作-create
使用 executeUpdate (String sql)方法完成数据添加操作,示例操作:
Statement statement = connection.createStatement();
String sql = "insert into users(...)values(...)";
int num = statement.executeUpdate(sql);
if(num>0){
System.out.println("插入成功!!!");
}
CRUD操作-delete
使用 executeUpdate(String sql)方法完成数据删除操作,示例操作:
Statement statement = connection.createStatement();
String sql = "delete from users where id=1";
int sum = statement.executeUpdate(sql);
if(sum>0){
System.out.println("删除成功!!!");
}
CRUD操作-update
使用executeUpdate(String sql)方法完成数据修改操作,示例操作:
Statement statement = connection.createStatement();
String sql = "update users set name = '' where name = ''";
int sum = statement.executeUpdate(sql);
if(sum>0){
System.out.println("修改成功");
}
CRUD操作-read
使用 executeQuery(String sql)方法完成数据查询操作,实例操作:
Statement statement = connection.createStatement();
String sql = "select * from users where id=1";
ResultSet rs = statement.executeQuery(sql);
while(rs.next()){
//根据获取列的数据类型,分别调用rs的相应方法映射到java对象中
}
1、创建配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456
2、提取工具类
package com.kuang.jdbc02.utils;
import 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{
try{
//反射获取类的装载器,然后在获取资源(db.properties)
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
//属性
Properties properties = new Properties();
//加载获取到的属性
properties.load(in);
// 从文件把这4个信息读取出来
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();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
//释放资源
public static void release(Connection conn, Statement sta, ResultSet res) throws SQLException {
if (res!=null){
res.close();
}
if (sta!=null){
sta.close();
}
if (conn!=null){
conn.close();
}
}
}
3、编写增删改
增 insert
package com.kuang.jdbc02;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class TestInsert {
public static void main(String[] args) throws SQLException {
Connection connection = JdbcUtils.getConnection(); //通过工具类获取链接
Statement statement = connection.createStatement();//获得SQL的执行对象
String sql = "INSERT INTO users(id,`name`,`password`,`email`,`birthday`) VALUES(5,'fenqi','123456','[email protected]','1985-12-04')";
int i = statement.executeUpdate(sql);
if (i>0){
System.out.println("插入成功");
}
JdbcUtils.release(null,null,null); //释放资源
}
}
删 Delete
package com.kuang.jdbc02;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestSelete {
public static void main(String[] args) throws SQLException {
//通过工具类获取数据库连接
Connection connection = JdbcUtils.getConnection();
//获得SQL的执行对象
Statement statement = connection.createStatement();
//编写SQL语句
String sql = "select * from users";
//执行
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()){
System.out.println("id"+resultSet.getInt("id"));
System.out.println("name"+resultSet.getString("name"));
System.out.println("password"+resultSet.getString("password"));
System.out.println("email"+resultSet.getString("email"));
System.out.println("birthday"+resultSet.getDate("birthday"));
System.out.println("=================");
}
JdbcUtils.release(null,null,null);
}
}
改 Update
package com.kuang.jdbc02;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class TestUpdate {
public static void main(String[] args) throws SQLException {
//通过工具类获取连接
Connection connection = JdbcUtils.getConnection();
//获得SQL的执行对象
Statement statement = connection.createStatement();
//编写SQL语句
String sql = "update users set name='xiugai' where id = 1";
//执行
int i = statement.executeUpdate(sql);
if (i>0){
System.out.println("更新成功");
}
JdbcUtils.release(null,null,null);
}
}
查 Select
package com.kuang.jdbc02;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestSelete {
public static void main(String[] args) throws SQLException {
//通过工具类获取数据库连接
Connection connection = JdbcUtils.getConnection();
//获得SQL的执行对象
Statement statement = connection.createStatement();
//编写SQL语句
String sql = "select * from users";
//执行
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()){
System.out.println("id"+resultSet.getInt("id"));
System.out.println("name"+resultSet.getString("name"));
System.out.println("password"+resultSet.getString("password"));
System.out.println("email"+resultSet.getString("email"));
System.out.println("birthday"+resultSet.getDate("birthday"));
System.out.println("=================");
}
JdbcUtils.release(null,null,null);
}
}
sql存在漏洞,会被攻击导致数据泄露,SQL会被拼接or
package com.kuang.jdbc02;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class SQL注入 {
public static void main(String[] args) throws SQLException {
//正常登录
// login("lishi","123456");
login(" 'or' 1=1"," 'or' 1=1");
}
public static void login(String username,String password) throws SQLException {
//通过工具类获取数据库连接
Connection connection = JdbcUtils.getConnection();
//获得SQL的执行对象
Statement statement = connection.createStatement();
//编写SQL语句
String sql = "select * from users where `name` = '"+username+"' and password='"+password+"'";
//执行
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()){
System.out.println(resultSet.getString("name"));
System.out.println(resultSet.getString("password"));
System.out.println("======================");
}
}
}
PreparedStatement 可以防止SQL注入。效果更好
1、新增
package com.kuang.jdbc03;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.util.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestInsert {
public static void main(String[] args) throws SQLException {
//调用工具类获取数据库连接
Connection connection = JdbcUtils.getConnection();
//使用? 占位符代表参数
String sql = "insert into users(id,name,password,email,birthday)values(?,?,?,?,?)";
//预编译SQL,先写sql,然后不执行
PreparedStatement pr = connection.prepareStatement(sql);
//手动给参数赋值
pr.setInt(1,5); //第一个值为参数下标,第二个为参数 id
pr.setString(2,"王胜");
pr.setString(3,"123456");
pr.setString(4,"[email protected]");
// 注意点:sql.Date 数据库 java.sql.Date()
// util.Date Java new Date().getTime() 获得时间戳
pr.setDate(5,new java.sql.Date(new Date().getTime()));
//执行
int i = pr.executeUpdate();
if (i>0){
System.out.println("插入成功");
}
JdbcUtils.release(null,null,null);
}
}
2、删除
package com.kuang.jdbc03;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestDelete {
public static void main(String[] args) throws SQLException {
//通过工具类获取数据库的连接
Connection connection = JdbcUtils.getConnection();
//使用占位符? 代表参数
String sql = "delete from users where id = ?";
//预编译SQL 先写sql 然后不执行
PreparedStatement pre = connection.prepareStatement(sql);
//手动给参数赋值
pre.setInt(1,5);
//执行
int i = pre.executeUpdate();
if (i>0){
System.out.println("删除成功");
}
//释放资源
JdbcUtils.release(null,null,null);
}
}
3、更新
package com.kuang.jdbc03;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestUpdate {
public static void main(String[] args) throws SQLException {
//通过工具类获取数据库的连接
Connection connection = JdbcUtils.getConnection();
//使用占位符? 代表参数
String sql = "update users set name = ? where name = ?";
//预编译SQL 先写SQL 然后不执行
PreparedStatement pre = connection.prepareStatement(sql);
//手动给参数赋值
pre.setString(1,"zhangsan");
pre.setString(2,"xiugai");
//执行
int i = pre.executeUpdate();
if (i>0){
System.out.println("修改成功");
}
//关闭资源
JdbcUtils.release(null,null,null);
}
}
4、查询
package com.kuang.jdbc03;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TestSelete {
public static void main(String[] args) throws SQLException {
//通过工具类获取数据库的连接
Connection connection = JdbcUtils.getConnection();
//使用占位符? 代表参数
String sql = "select * from users where id=?";
//预编译SQL 先写sql 然后不执行
PreparedStatement pre = connection.prepareStatement(sql);
//手动传递参数
pre.setObject(1,2);
//执行
ResultSet resultSet = pre.executeQuery();
while (resultSet.next()){
System.out.println(resultSet.getInt("id"));
System.out.println(resultSet.getString("name"));
System.out.println(resultSet.getString("password"));
}
//释放资源
JdbcUtils.release(null,null,null);
}
}
5、防止Sql注入
package com.kuang.jdbc03;
import com.kuang.jdbc02.utils.JdbcUtils;
import java.sql.*;
public class SQL注入 {
public static void main(String[] args) throws SQLException {
//正常登录
// login("lishi","123456");
login("'' or 1=1","123456");
}
public static void login(String username,String password) throws SQLException {
//通过工具类获取数据库连接
Connection connection = JdbcUtils.getConnection();
//使用占位符? 传递参数
//PreparedStatement放置SQL注入的本质,把值传递进来的参数当作字符
//假设其中存在转义字符,比如说 ‘ 会被直接转义
String sql = "select * from users where name = ? and password = ?";
//预编译Sql 先写sql 然后不执行
PreparedStatement pre = connection.prepareStatement(sql);
//手动传递参数
pre.setString(1,username);
pre.setString(2,password);
//执行
ResultSet resultSet = pre.executeQuery();
while(resultSet.next()){
System.out.println(resultSet.getString("name"));
System.out.println(resultSet.getString("password"));
System.out.println("======================");
}
//释放资源
JdbcUtils.release(null,null,null);
}
}
找到右侧的Database 点击进去
点击 + 号 → 选择Data Source →选择MySql
然后输入用户名 和 密码 → 点击 Test Connection
成功的话会显示✔
不成功的话,要么是时区异常要么就是版本没选好
左侧NySQL点击进去
Class选择 com.musql.jdbc.Driver
Driver files 选择你对应的SQL版本 → 然后就可以点击Apply → 然后就去重新输入账号密码连接
时区的话就需要打开命令行的sql然后设计时区了
-- 查看时区,显示 SYSTEM 就是没有设置时区啦。
show variables like'%time_zone';
--设置时区 设置完需要重新连接sql重新查看
set global time_zone = '+8:00';
要么都成功,要么都失败
ACID原则
原子性:
要么全部完成,要么都不完成
一致性:
总数不变
隔离性:
多个进程互不干扰
持久性:
一旦提交不可逆,持久化到数据库
隔离性问题:
脏读:一个事务读取了另一个没有提交的事物
不可重复读:在同一个事物内,重复读取表中的数据,表数据发生了改变
虚读(幻读):在一个事务内,读取到了别人插入的数据,导致前后读出来结果不一致
代码实现
1、开启事务 conn.setAutoCommit(false);
2、一组业务执行完毕,提交事务
3、可以在catch语句中显示的定义 回滚语句,但默认失败就会回滚
package com.kuang.jdbc04;
import com.kuang.jdbc02.utils.JdbcUtils;
import com.sun.org.apache.xpath.internal.objects.XNull;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestTransation1 {
public static void main(String[] args) throws SQLException {
Connection conn = null;
PreparedStatement st = null;
try {
//通过工具类连接数据库
conn = JdbcUtils.getConnection();
//关闭数据库的自动提交,会自动开启事物
conn.setAutoCommit(false);
//编写SQL语句
String sql1 = "update account set miney = miney-100 where name = 'A'";
//预编译SQL
st = conn.prepareStatement(sql1);
st.executeUpdate();
//编写SQL语句
String sql2 = "update account set miney = miney+100 where name = 'B'";
//预编译SQL
st= conn.prepareStatement(sql2);
st.executeUpdate();
//业务完毕,提交事务
conn.commit();
System.out.println("成功");
}catch (Exception e){
try{
conn.rollback(); //如果失败则回滚事物
} catch (SQLException throwables)
{
throwables.printStackTrace();
}
e.printStackTrace();
}finally {
//释放资源
JdbcUtils.release(conn,st,null);
}
}
}
数据库连接 - 执行完毕 - 释放
连接-释放 十分浪费系统资源
池化技术:准备一些预先的资源,过来就连接预先准备好的
例子(银行)
比如去银行办理业务,开门-办理-关门 下一个 开门-办理-关门, 这是平常数据库的操作,很繁琐麻烦
使用连接池 开门-安排业务员-等待-办理-不使用了就彻底关门
常用连接数10个
最小连接数:10(按照常用连接数设计最小连接数)
最大连接数:(业务最高承载上线,超过了就需要等待排队)
等待超时:(超过等待的时间就不等了)
编写连接池,只需要实现一个接口 DataSource
开源数据源实现(拿来即用)
DBCP
C3P0
Druid:阿里巴巴
使用了这些数据库连接池之后我们在项目开发中就不需要编写连接数据库的代码了!
DBCP
需要用到的jar包
commons-dbcp-1.4、commons-pool-1.6
C3P0
需要用到的jar包
c3p0-0.9.5.5、mchange-commons-java-0.2.19
结论
无论使用什么数据源,本质还是一样的 ,DataSource接口不会变,方法就不会变