JavaEE:企业级Java开发 Web
前端(页面:展示,数据!)
后台(连接点:连接数据库JDBC,链接前端(控制,控制视图跳转,和给前端传递数据)
数据库(存数据,txt,Excel,word)
只会写代码,学好数据库,基本混饭吃;
操作系统,数据结构与算法! 当一个不错的程序猿!
离散数学,数字电路,体系结构,编译原理。+实战经验,高级程序猿 优秀的程序猿
数据库(DB,DataBase)
概念:数据仓库,软件,安装在操作系统( window ,linux, mac、…)之上! SQL,可以存储大量的数据。500万!
作用:存储数据,管理数据
关系型数据库:(SQL)
非关系型数据库:(NoSQL) Not Only
DBMS(数据库管理系统)
官网下载地址
下载压缩版
解压到自己想要安装到的目录,本人解压到的是G:\JavaSE\mysql-5.7.38
添加环境变量
我的电脑->属性->高级->环境变量
选择PATH,在其后面添加:你的mysql安装文件下面的bin文件夹
在G:\JavaSE\mysql-5.7.38下新建my.ini文件
编辑my.ini文件,注意替换路径位置
[mysqld]
#目录换成自己安装的目录
basedir=G:\JavaSE\mysql-5.7.38\
#data文件夹会自动生成,不需要手动建文件夹
datadir=G:\JavaSE\mysql-5.7.38\data\
#端口号
port=3306
#跳过密码验证
skip-grant-tables
#设置默认字符集编码
#character-set-server=utf8
启动管理员模式下的CMD,并将路径切换至mysql 下的bin目录
cd /d G:\JavaSE\mysql-5.7.38\bin
然后输入mysqld -install (安装mysql)
mysqld -install
再输入下面代码,初始化数据文件
mysqld --initialize-insecure --user=mysql
然后再次启动mysql
net start mysql
然后用命令mysql -u root -p进入mysql管理界面(密码可为空)
mysql -u root -p
看到Enter password:时,不要输入任何内容,直接回车
进入界面后更改root密码
update mysql.user set authentication_string=password('123456') where user='root' and Host = 'localhost';
最后输入下面代码刷新权限
flush privileges;
修改my.ini文件skip-grant-tables前加一个#号
退出mysql
exit
重启mysql即可正常使用
#关闭
net stop mysql
#启动
net start mysql
连接上测试
mysql -u root -p
#出现Enter password:,就输入之前设置的密码
出现以下结果就安装好了
#停止mysql服务
net stop mysql
#卸载mysql
mysql remove
#清空服务
sc delete mysql
SQLyog下载
安装好后运行输入名称,和证书密钥(注册码序列号)
名称:英文随便命名
证书密钥:
8d8120df-a5c3-4989-8f47-5afc79c56e7c
每一个sqlyog的执行操作,本质就是对应了一个sql,可以在软件的历史记录中查看
字段: id,name,age
命令行连接
#关闭
net stop mysql
#启动
net start mysql
mysql -u root -p123456 -- 连接数据库
update mysql.user set authentication_string=password('123456') where user='root' and Host = 'localhost'; -- 修改用户密码
flush privileges; -- 刷新权限
---------------------
-- 所有的语句都使用;结尾
show databases; -- 查询所有的数据库
-- mysql> 中写:
use school -- 切换数据库 use 数据库名
Database changed
show tables; -- 查看数据库中所有的表
describe student; -- 显示student数据库中所有的表的信息
create database westos; -- 创建一个数据库
exit; -- 退出连接
-- 单行注释(SQL的本来的注释)
/* (sql的多行注释)
hello
*/
数据库xxx语言 CRUD增删改查! CV程序员 API程序员 CRUD程序员(业务)
操作数据库 > 操作数据库中的表 > 操作数据库中表的数据
了解:操作一次就行
精通:背下来
创建名为westos的数据库,[]里的代码可以省略
CREATE DATABASE [IF NOT EXISTS] westos;
删除westos数据库
DROP DATABASE [IF EXISTS] westos
使用school数据库
-- ``在 tab 键的上面,如果你的表名或者字段名是一个特殊字符,就需要带``
USE `school`
SHOW DATABASES -- 查看所有的数据库
数值
字符串
时间日期
java.util.Date
null
Unsigned:
zerofill:
自增:
非空 NUII not null
默认:
拓展:
/* 每一个表,都必须存在以下五个字段!未来做项目用的,表示一个记录存在意义!
id 主键
`version` 乐观锁
is_delete 伪删除
gmt_create 创建时间
gmt_update 修改时间
*/
-- 目标:创建一个school数据库
-- 创建学生表(列,字段) 使用sQL创建
-- 学号int 登录密码varchar(20) 姓名,性别varchar(2),出生日期(datatime),家庭住址,email
-- 注意点,使用英文(),表的名称 和 字段尽量使用 `` 括起来
-- AUTO_INCREMENT 自增
-- 字符串使用单引号括起来!
-- 所有的语句后面加,(英文的),最后一个不用加
-- PRIMARY KEY 主键,一股一个表只有一个唯一的主键!
CREATE TABLE IF NOT EXISTS `student`(
`id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
`name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名',
`pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码',
`sex` VARCHAR(2) NOT NULL DEFAULT '女' COMMENT '性别',
`birthday` DATETIME DEFAULT NULL COMMENT '出生日期',
`address` VARCHAR(100) DEFAULT NULL COMMENT '家庭住址',
`email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
格式
CREATE TABLE [IF NOT EXISTS] `表名`(
`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
......
`字段名` 列类型 [属性] [索引] [注释]
)[表类型][字符集设置][注释]
常用命令
SHOW CREATE DATABASE school -- 查看创建数据库的语句
SHOW CREATE TABLE student -- 查看student数据表的定义语句
DESC student -- 显示表的结构
-- 关于数据库引擎
/*
INNODB 默认使用~
MYISAM 早些年使用的
*/
MYISAM | INNODB | |
---|---|---|
事务支持 | 不支持 | 支持 |
数据行锁定 | 不支持 | 支持 |
外键约束 | 不支持 | 支持 |
全文索引 | 支持 | 不支持 |
表空间的大小 | 较小 | 较大,约为MYISAM的2倍 |
常规使用操作:
在物理空间存在的位置
所有的数据库文件都存在data目录下
本质还是文件的存储!
MySQL引擎在物理文件上的区别:
设置数据库表的字符集编码
CHARSET=utf8
不设置的话,会是mysql默认的字符集编码~(不支持中文! )
MySQL的默认编码是Latin1,不支持中文
在my.ini中配置默认的编码
character-set-server=utf8
-- 修改表名: 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 表名 MODIFY 旧字段名 新字段名
ALTER TABLE teacher1 MODIFY age VARCHAR(11) -- 修改约束
ALTER TABLE teacher1 CHANGE age age1 INT(2) -- 字段重名名
-- 删除表的字段: ALTER TABLE 表名 DROP 字段名
ALTER TABLE teacher1 DROP age1
-- 删除表(如果表存在再册删除): DROP TABLE IF EXISTS 表名
DROP TABLE IF EXISTS teacher1
所有的创建和删除操作尽量加上判断,以免报错~
注意点:
方式一、在创建表的时候,增加约束(麻烦,比较复杂)
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
-- 学生表的 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 '密码',
`sex` VARCHAR(2) NOT NULL DEFAULT '女' COMMENT '性别',
`birthday` DATETIME DEFAULT NULL COMMENT '出生日期',
`gradeid` INT(10) NOT NULL COMMENT '学生的年级',
`address` VARCHAR(100) DEFAULT NULL COMMENT '家庭住址',
`email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY(`id`),
KEY `FK_gradeid` (`gradeid`),
CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade`(`gradeid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
删除有外键关系的表的时候,必须要先删除引用别人的表(从表),再删除被引用的表(主表)
方式二∶创建表成功后,添加外键约束
CREATE TABLE `grade`(
`gradeid` INT(10) NOT NULL AUTO_INCREMENT COMMENT '年级id',
`gradename` VARCHAR(50) NOT NULL COMMENT '年级名称',
PRIMARY KEY(`gradeid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
-- 学生表的 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 '密码',
`sex` VARCHAR(2) NOT NULL DEFAULT '女' COMMENT '性别',
`birthday` DATETIME DEFAULT NULL COMMENT '出生日期',
`gradeid` INT(10) NOT NULL COMMENT '学生的年级',
`address` VARCHAR(100) DEFAULT NULL COMMENT '家庭住址',
`email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
-- 创建表的时候没有外键关系
ALTER TABLE `student` ADD CONSTRAINT `FK_gradeid` FOREIGN KEY(`gradeid`) REFERENCES `grade`(`gradeid`);
-- ALTER TABLE 表 ADD CONSTRAINT 约束名 FOREIGN KEY(作为外键的列) REFERENCES 哪个表(哪个字段);
以上的操作都是物理外键,数据库级别的外键,我们不建议使用!(避免数据库过多造成困扰,这里了解即可~)
最佳实践
数据库意义:数据存储,数据管理
DML语言:数据操作语言
insert
-- 插入语句(添加)
-- insert into 表名([字段名1,字段名2,字段名3])values('值1'),('值2'),('值3'),(...)
INSERT INTO `grade`(`gradename`)VALUES('大三')
-- 由于主键自增我们可以省略 (如果不写表的字段,他就会一一匹配)
-- 一般写插入语句,我们一定要数据利和字段一一对应!
-- 插入多个字段
INSERT INTO `grade`(`gradename`) VALUES('大二'),('大一')
INSERT INTO `student`(`name`) VALUES('张三')
INSERT INTO `student`(`name`,`pwd`,`sex`) VALUES('张三','123','男')
INSERT INTO `student`(`name`,`pwd`,`sex`) VALUES('张三','123','男'),('李四','1234','女'),('王五','12345','男')
INSERT INTO `student` VALUES(6,'张三','123','男','2000-01-01',1,'重庆','email')
语法:insert into 表名([字段名1,字段名2,字段名3])values('值1'),('值2'),('值3'),(...)
注意事项:
VALUES(),(),...
update 修改谁 (条件) set 原来的值 = 新值
-- 修改学员名字,带了条件
UPDATE `student` SET `name`='潆勖' WHERE id=1;
-- 不指定条件的情况下,会改动所有表!
UPDATE `student` SET `name`='鲁班7号'
-- 语法:
-- UPDATE 表名 set colnum_name = value[,colnum_name = value,...] where [条件]
条件:where 子句 运算符 id等于某个值,大于某个值,在某个区间内修改…
操作符会返回 布尔值
操作符 | 含义 | 范围 | 结果 |
---|---|---|---|
= | 等于 | 5=6 | false |
<>或!= | 不等于 | 5<>6 | true |
> | 大于 | ||
< | 小于 | ||
<= | 小于等于 | ||
>= | 大于等于 | ||
between … and … | 区间,在某个范围内 | between 2 and 5 | [2,5] |
and | 和,&& | 5>1 and 1>2 | false |
or | 或,|| | 5>1 or1>2 | true |
-- 通过多个条件定位数据
UPDATE `student` SET `name`='鲁班7号' WHERE `name`='潆勖' AND sex='女'
语法:UPDATE 表名 set colnum_name = value[,colnum_name = value,...] where [条件]
注意:
UPDATE `student` SET `birthday`=CURRENT_TIME WHERE `name`='潆勖' AND sex='女'
delete 命令
语法:delete from 表名 [where 条件]
-- 删除数据(避免这样写,会全部删除)
DELETE FROM `student`
-- 删除指定数据
DELETE FROM `student` WHERE id=1
truncate 命令
作用:完全清空一个数据库表,表的结构和索引约束不会变
-- 清空 student 表
TRUNCATE `student`
delete 和 truncate 区别
-- 测试 delete 和 truncate 区别
CREATE TABLE `test`(
`id` INT(4) NOT NULL AUTO_INCREMENT,
`coll` VARCHAR(20) NOT NULL,
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `test`(`coll`) VALUES('1'),('2'),('3')
DELETE FROM `test` -- 不会影响自增
TRUNCATE TABLE `test` -- 自增会归零
了解即可:DELETE删除的问题,重启数据库,现象
(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}];
-- 指定查询的记录从哪条至哪条
注意 : [ ] 括号代表可选的 , { }括号代表必选得
school.sql文件:
CREATE DATABASE IF NOT EXISTS `school`;
-- 创建一个school数据库
USE `school`;-- 用school数据库
-- 创建年级表
DROP TABLE IF EXISTS `grade`;
CREATE TABLE `grade`(
`gradeid` INT(11) NOT NULL AUTO_INCREMENT COMMENT '年级编号',
`gradename` VARCHAR(50) NOT NULL COMMENT '年级名称',
PRIMARY KEY (`gradeid`)
) ENGINE=INNODB AUTO_INCREMENT = 6 DEFAULT CHARSET = utf8
-- 插入年级数据
INSERT INTO `grade` (`gradeid`,`gradename`) VALUES(1,'大一'),(2,'大二'),(3,'大三'),(4,'大四'),(5,'预科班');
-- 创建成绩表
DROP TABLE IF EXISTS `result`;
CREATE TABLE `result`(
`studentno` INT(4) NOT NULL COMMENT '学号',
`subjectno` INT(4) NOT NULL COMMENT '课程编号',
`examdate` DATETIME NOT NULL COMMENT '考试日期',
`studentresult` INT (4) NOT NULL COMMENT '考试成绩',
KEY `subjectno` (`subjectno`)
)ENGINE = INNODB DEFAULT CHARSET = utf8
-- 插入成绩数据 这里仅插入了一组,其余自行添加
INSERT INTO `result`(`studentno`,`subjectno`,`examdate`,`studentresult`)
VALUES
(1000,1,'2013-11-11 16:00:00',85),
(1000,2,'2013-11-12 16:00:00',70),
(1000,3,'2013-11-11 09:00:00',68),
(1000,4,'2013-11-13 16:00:00',98),
(1000,5,'2013-11-14 16:00:00',58);
-- 创建学生表
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`(
`studentno` INT(4) NOT NULL COMMENT '学号',
`loginpwd` VARCHAR(20) DEFAULT NULL,
`studentname` VARCHAR(20) DEFAULT NULL COMMENT '学生姓名',
`sex` TINYINT(1) DEFAULT NULL COMMENT '性别,0或1',
`gradeid` INT(11) DEFAULT NULL COMMENT '年级编号',
`phone` VARCHAR(50) NOT NULL COMMENT '联系电话,允许为空',
`address` VARCHAR(255) NOT NULL COMMENT '地址,允许为空',
`borndate` DATETIME DEFAULT NULL COMMENT '出生时间',
`email` VARCHAR (50) NOT NULL COMMENT '邮箱账号允许为空',
`identitycard` VARCHAR(18) DEFAULT NULL COMMENT '身份证号',
PRIMARY KEY (`studentno`),
UNIQUE KEY `identitycard`(`identitycard`),
KEY `email` (`email`)
)ENGINE=MYISAM DEFAULT CHARSET=utf8
-- 插入学生数据 其余自行添加 这里只添加了2行
INSERT INTO `student` (`studentno`,`loginpwd`,`studentname`,`sex`,`gradeid`,`phone`,`address`,`borndate`,`email`,`identitycard`)
VALUES
(1000,'123456','张伟',0,2,'13800001234','北京朝阳','1980-1-1','[email protected]','123456198001011234'),
(1001,'123456','赵强',1,3,'13800002222','广东深圳','1990-1-1','[email protected]','123456199001011233');
(1002,'123456','张三',1,3,'13800002223','广东深圳','1990-1-1','[email protected]','123456199001011235');
(1003,'123456','张三丰',0,2,'13800002225','广东深圳','1990-1-1','[email protected]','123456199001011236');
-- 创建科目表
DROP TABLE IF EXISTS `subject`;
CREATE TABLE `subject`(
`subjectno`INT(11) NOT NULL AUTO_INCREMENT COMMENT '课程编号',
`subjectname` VARCHAR(50) DEFAULT NULL COMMENT '课程名称',
`classhour` INT(4) DEFAULT NULL COMMENT '学时',
`gradeid` INT(4) DEFAULT NULL COMMENT '年级编号',
PRIMARY KEY (`subjectno`)
)ENGINE = INNODB AUTO_INCREMENT = 18 DEFAULT CHARSET = utf8
-- 插入科目数据
INSERT INTO `subject`(`subjectno`,`subjectname`,`classhour`,`gradeid`)VALUES
(1,'高等数学-1',110,1),
(2,'高等数学-2',110,2),
(3,'高等数学-3',100,3),
(4,'高等数学-4',130,4),
(5,'C语言-1',110,1),
(6,'C语言-2',110,2),
(7,'C语言-3',100,3),
(8,'C语言-4',130,4),
(9,'Java程序设计-1',110,1),
(10,'Java程序设计-2',110,2),
(11,'Java程序设计-3',100,3),
(12,'Java程序设计-4',130,4),
(13,'数据库结构-1',110,1),
(14,'数据库结构-2',110,2),
(15,'数据库结构-3',100,3),
(16,'数据库结构-4',130,4),
(17,'C#基础',130,1);
-- 查询全部的学生 SELECT 字段 FROM 表
SELECT * FROM student
SELECT * FROM result
-- 查询指定字段
SELECT `studentno`,`studentname` FROM student
-- 别名,给结果起一个名字 As 可以给字段起别名,也可以给表起别名
SELECT `studentno` AS 学号,`studentname` AS 学生姓名 FROM student AS s
-- 函数 Concat (a,b)
SELECT CONCAT('姓名:',studentname) AS 新名字 FROM student
语法:SELECT 字段1,字段2,... FROM 表
有的时候,列名字不是那么的见名知意。我们起别名 AS
字段名 as 别名 表名 as 别名
去重 distinct
作用:去除SELECT查询出来的结果中重复的数据,重复的数据只显示一条
-- 查询一下有哪些同学参加了考试,成绩
SELECT * FROM result -- 查询全部的考试成绩
SELECT `studentno` FROM result -- 查询有哪些同学参加了考试
SELECT DISTINCT `studentno` FROM result -- 发现重复数据,去重
数据库的列
SELECT VERSION() -- 查询系统版本(函数)
SELECT 100*3-1 AS 计算结果 -- 用来计算(表达式)
SELECT @@auto_increment_increment -- 查询自增的步长(变量)
-- 学员考试成绩+1分查看
SELECT `studentno`,`studentresult`+1 AS '提分后' FROM result
数据库中的表达式:文本值,列,Null,函数,计算表达式,系统变量…
select 表达式
from 表
作用:检索数据中**符合条件
**的值
搜索的条件由一个或者多个表达式组成!结果 布尔值
逻辑运算符
运算符 | 语法 | 描述 |
---|---|---|
and && | a and b a&&b | 逻辑与,两个都为真,结果为真 |
or || | a or b a||b | 逻辑或,其中一个为真,则结果为真 |
Not ! | not a ! a | 逻辑非,真为假,假为真! |
尽量使用英文字母
-- ============== where ===================
SELECT `studentno`,`studentresult` FROM result
-- 查询考试成绩在95~100分之间
SELECT `studentno`,`studentresult` FROM result
WHERE studentresult>=95 AND studentresult<=100
-- and &&
SELECT `studentno`,`studentresult` FROM result
WHERE studentresult>=95 && studentresult<=100
-- 模糊查询(区间)
SELECT `studentno`,`studentresult` FROM result
WHERE studentresult BETWEEN 95 AND 100
-- 除了1000号学生之外的同学的成绩
SELECT `studentno`,`studentresult` FROM result
WHERE studentno!=1000
-- != not
SELECT `studentno`,`studentresult` FROM result
WHERE NOT studentno=1000
模糊查询:比较运算符
运算符 | 语法 | 描述 |
---|---|---|
IS BULL | 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 `studentno`,`studentname` FROM `student`
WHERE studentname LIKE '张%'
-- 查询姓张的同学,名字后面只有一个字的
SELECT `studentno`,`studentname` FROM `student`
WHERE studentname LIKE '张_'
-- 查询姓张的同学,名字后面只有两个字的
SELECT `studentno`,`studentname` FROM `student`
WHERE studentname LIKE '张__'
-- 查询名字中间有嘉字的同学 %三%
SELECT `studentno`,`studentname` FROM `student`
WHERE studentname LIKE '%三%'
-- ========= in (具体的一个或者多个值) ===========
-- 查询 1001,1002,1003号学员
SELECT `studentno`,`studentname` FROM `student`
WHERE studentno IN (1001,1002,1003)
-- 查询在北京的学生
SELECT `studentno`,`studentname` FROM `student`
WHERE `address` IN ('北京朝阳','广东深圳')
-- =====null not null====
-- 查询地址为空的学生nul1 ''
SELECT `studentno`,`studentname` FROM `student`
WHERE `address`='' OR `address` IS NULL
-- 查询有出生日期的同学 不为空
SELECT `studentno`,`studentname` FROM `student`
WHERE `borndate` IS NOT NULL
-- 查询没有出生日期的同学 为空
SELECT `studentno`,`studentname` FROM `student`
WHERE `borndate` IS NULL
JOIN 对比
七种Join:
-- join (连接的表) on (判断的条件) 连接查询
-- where 等值查询
-- Inner Join
SELECT s.studentno,`studentname`,`subjectno`,`studentresult`
FROM student s
INNER JOIN result r
ON s.studentno=r.studentno
-- Right Join
SELECT s.studentno,`studentname`,`subjectno`,`studentresult`
FROM student s
RIGHT JOIN result r
ON s.studentno=r.studentno
-- Left Join
SELECT s.studentno,`studentname`,`subjectno`,`studentresult`
FROM student s
LEFT JOIN result r
ON s.studentno=r.studentno
操作 | 描述 |
---|---|
Inner Join | 如果表中至少有一个匹配,就返回行 |
left Join | 会从左表中返回所有的值,即使右表中没有匹配 |
right Join | 会从右表中返回所有的值,即使左表中没有匹配 |
-- ========= 联表查询 join ===========
-- 查询参加了考试的同学(学号,姓名,科目编号,分数)
SELECT * FROM student
SELECT * FROM result
/*
1.分析需求,分析查询的字段来自哪些表 (连接查询)
2.确定使用哪种连接查询? 7种
确定交叉点(这两个表中哪个数据是相同的)
判断的条件 : 学生表的中 studentno =成绩表 studentno
*/
-- join (连接的表) on (判断的条件) 连接查询
-- where 等值查询
-- Inner Join
SELECT s.studentno,`studentname`,`subjectno`,`studentresult`
FROM student s
INNER JOIN result r
ON s.studentno=r.studentno
-- Right Join
SELECT s.studentno,`studentname`,`subjectno`,`studentresult`
FROM student s
RIGHT JOIN result r
ON s.studentno=r.studentno
-- Left Join
SELECT s.studentno,`studentname`,`subjectno`,`studentresult`
FROM student s
LEFT JOIN result r
ON s.studentno=r.studentno
-- 查询缺考的同学
SELECT s.studentno,`studentname`,`subjectno`,`studentresult`
FROM student s
LEFT JOIN result r
ON s.studentno=r.studentno
WHERE studentresult IS NULL
-- 思考题(查询了参加考试的同学信息:学号,学生姓名,科目名,分数)
/*
1.分析需求,分析查询的字段来自哪些表 student、result、subject (连接查询)
2.确定使用哪种连接查询? 7种
确定交叉点(这两个表中哪个数据是相同的)
判断的条件 : 学生表的中 studentno =成绩表 studentno
*/
SELECT s.studentno,`studentname`,`subjectname`,`studentresult`
FROM student s
RIGHT JOIN result r
ON s.studentno=r.studentno
INNER JOIN `subject` sub
ON r.subjectno=sub.subjectno
-- 我要查询哪些数据 select ...
-- 从那几个表中查 FROM 表 XXX Join 连接的表 on 交叉条件
-- 假设存在一种多张表查询,慢慢来,先查询两张表然后再慢慢增加
-- FROM a left join b 以a为准
-- FROM a right join b 以b为准
自连接 (了解)
自己的表和自己的表连接,核心:一张表拆为两张一样的表即可
-- =========== 自连接 ===============
CREATE TABLE `category`(
`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 `category` (`categoryid`, `pid`, `categoryname`)
VALUES ('2','1','信息技术'),
('3','1','软件开发'),
('4','3','数据库'),
('5','1','美术设计'),
('6','3','web开发'),
('7','5','ps技术'),
('8','2','办公信息')
父类:
categoryid | categoryname |
---|---|
2 | 信息技术 |
3 | 软件开发 |
5 | 美术设计 |
子类:
pid | categoryid | categoryname |
---|---|---|
3 | 4 | 数据库 |
2 | 8 | 办公信息 |
3 | 6 | web开发 |
5 | 7 | ps技术 |
操作:查询父类对应的子类关系
父类 | 子类 |
---|---|
信息技术 | 办公信息 |
软件开发 | 数据库 |
软件开发 | web开发 |
美术设计 | ps技术 |
-- 查询父子信息: 把一张表看为两个一模一样的表
SELECT a.`categoryname` AS '',b.`categoryname` AS ''
FROM category AS a,category AS b
WHERE a.`categoryid`=b.`pid`
排序
-- 排序 : 升序ASC ,降序DESC
-- ORDER BY通过那个字段排序,怎么排
-- 查询的结果根据成绩降序排序
SELECT s.studentno,studentname,subjectname,studentresult
FROM student s
INNER JOIN result r
ON r.studentno = s.studentno
INNER JOIN `subject` sub
ON r.subjectno = sub.subjectno
WHERE subjectname LIKE '高等数学%'
ORDER BY studentresult DESC
分页
-- 100万
-- 为什么要分页?
-- 缓解数据库压力,给人的体验更好,瀑布流
-- 分页,每页只显示三条数据
-- 语法: limit 当前页,页面的大小
-- 网页应用: 当前,总的页数,页面的大小
-- LIMIT 0,3 l~3
-- LIMIT 1,3 2~4
SELECT s.studentno,studentname,subjectname,studentresult
FROM student s
INNER JOIN result r
ON r.studentno = s.studentno
INNER JOIN `subject` sub
ON r.subjectno = sub.subjectno
WHERE subjectname LIKE '高等数学%'
ORDER BY studentresult DESC
LIMIT 0,3
/*
推导:
第一页 : limit 0,5
第二页 : limit 5,5
第三页 : limit 10,5
......
第N页 : limit (pageNo-1)*pageSzie,pageSzie
[pageNo:当前页码,pageSize:单页面显示条数]
[(pageNo-1)*pageSzie:起始值]
[数据总数/页面大小 = 总页数]
*/
语法:limit(查询起始下标,pagesize)
where (这个值是计算出来的)
本质:在where语句中嵌套一个子查询语句
-- ============== where ====================
-- 查询 高等数学-1 的所有考试结果(学号,科目编号,成绩),并且成绩降序排列
-- 方法一:使用连接查询
SELECT studentno,r.subjectno,studentresult
FROM result r
INNER JOIN `subject` sub
ON r.`SubjectNo`=sub.`SubjectNo`
WHERE subjectname = '高等数学-1'
ORDER BY studentresult DESC;
-- 方法二:使用子查询(执行顺序:由里及外)
SELECT studentno,subjectno,studentresult
FROM result
WHERE subjectno=(
SELECT subjectno FROM `subject`
WHERE subjectname = '高等数学-1'
)
ORDER BY studentresult DESC;
-- 查询课程为 高等数学-2 且分数不小于70分的学生的学号和姓名
-- 方法一:使用连接查询
SELECT DISTINCT s.studentno,studentname
FROM student s
INNER JOIN result r
ON s.`StudentNo` = r.`StudentNo`
INNER JOIN `subject` sub
ON sub.`SubjectNo` = r.`SubjectNo`
WHERE subjectname = '高等数学-2' AND StudentResult>=70
-- 方法二:使用连接查询+子查询
SELECT r.studentno,studentname
FROM student s
INNER JOIN result r
ON s.`StudentNo`=r.`StudentNo`
WHERE StudentResult>=70 AND subjectno=(
SELECT subjectno FROM `subject`
WHERE subjectname = '高等数学-2'
)
-- 方法三:使用子查询(由里及外)
-- 分步写简单sql语句,然后将其嵌套起来
SELECT studentno,studentname FROM student WHERE studentno IN(
SELECT studentno FROM result WHERE StudentResult>=70 AND subjectno=(
SELECT subjectno FROM `subject` WHERE subjectname = '高等数学-2'
)
)
-- 查询不同课程的平均分,最高分,最低分
-- 核心: (根据不同的课程进行分组)
SELECT subjectname,AVG(studentresult) AS 平均分,MAX(StudentResult) AS 最高分,MIN(StudentResult) AS 最低分
FROM result AS r
INNER JOIN `subject` AS s
ON r.subjectno = s.subjectno
GROUP BY r.subjectno
HAVING 平均分>80;
/*
where写在group by前面.
要是放在分组后面的筛选
要使用HAVING..
因为having是从前面筛选的字段再筛选,而where是从数据表中的>字段直接进行的筛选的
*/
官网:https://dev.mysql.com/doc/refman/5.7/en/built-in-function-reference.html
-- =========== 常用函数 ===================
-- 数学运算
SELECT ABS(-8); /*绝对值*/
SELECT CEILING(9.4); /*向上取整*/
SELECT FLOOR(9.4); /*向下取整*/
SELECT RAND(); /*随机数,返回一个0-1之间的随机数*/
SELECT SIGN(0); /*符号函数: 负数返回-1,正数返回1,0返回0*/
-- 字符串函数
SELECT CHAR_LENGTH('神说坚持就能成功'); /*返回字符串包含的字符数*/
SELECT CONCAT('我','爱','程序'); /*合并字符串,参数可以有多个*/
SELECT INSERT('我爱编程helloworld',1,2,'超级热爱'); /*替换字符串,从某个位置开始替换某个长度*/
SELECT LOWER('yingxu'); /*小写*/
SELECT UPPER('yingxu'); /*大写*/
SELECT INSTR('yingxu','n');/*返回第一次出现的子串的索引*/
SELECT LEFT('hello,world',5); /*从左边截取*/
SELECT RIGHT('hello,world',5); /*从右边截取*/
SELECT REPLACE('神说坚持就能成功','坚持','努力'); /*替换字符串*/
SELECT SUBSTR('神说坚持就能成功了',4,6); /*截取字符串,开始和长度*/
SELECT REVERSE('神说坚持就能成功'); /*反转*/
-- 查询姓张的同学,改成張
SELECT REPLACE(studentname,'张','張') AS 新名字
FROM student WHERE studentname LIKE '张%';
-- 时间和日期函数 (记住)
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() | 最小值 |
… | … |
-- ========== 聚合函数 ===============
-- 都能够统计表中的数据 ( 想查询一个表中有多少个记录,就使用这个count() )
/*无主键 推荐*/
SELECT COUNT(studentname) FROM student; -- COUNT(指定列),会忽略所有的 null值
SELECT COUNT(*) FROM student; -- COUNT(*),不会忽略 null 值
/*有主键 推荐*/
SELECT COUNT(1) FROM student; -- COUNT(1),不会忽略所有的 null值
SELECT SUM(StudentResult) AS 总和 FROM result;
SELECT AVG(StudentResult) AS 平均分 FROM result;
SELECT MAX(StudentResult) AS 最高分 FROM result;
SELECT MIN(StudentResult) AS 最低分 FROM result;
MD5简介
MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,MD5的前身有MD2、MD3和MD4。
实现数据加密
新建一个表 testmd5:
-- ============测试MD5 加密==================
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 VALUES(1,'zhangsan','123456'),(2,'yingxu','123456'),(3,'yingxu','123456')
-- 加密
UPDATE testmd5 SET pwd = MD5(pwd) WHERE id = 1;
UPDATE testmd5 SET pwd = MD5(pwd); -- 加密全部的密码
-- 插入的时候加密
INSERT INTO testmd5 VALUES(4,'kyingxu2',MD5('123456'))
-- 如何校验:将用户传递进来的密码,进行md5加密,然后比对加密后的值
SELECT * FROM testmd5 WHERE `name`='kyingxu2' AND pwd=MD5('123456');
什么是事务
要么都成功,要么都失败
事务原则:ACID原则 原子性,一致性,隔离性,持久性 (脏读,幻读…)
原子性(Atomic)
一致性(Consist)
隔离性(Isolated)
持久性(Durable)
执行事务
-- =========== 事务 ===============
-- mysql是默认开启事务自动提交的
SET autocommit = 0; /*关闭*/
SET autocommit = 1; /*开启(默认的)*/
-- 手动处理事务
SET autocommit = 0 -- 关闭自动条件
-- 事务开启
START TRANSACTION -- 标记一个事务的开始,从这个之后的 sql 都在同一个事务内
-- 提交:持久化(成功!)
COMMIT
-- 回滚:回到的原来的样子(失败!)
ROLLBACK
-- 事务结束
SET autocommit = 1 -- 开启自动提交
-- 保存点(相当于存档)
SAVEPOINT 保存点名称 -- 设置一个事务保存点
ROLLBACK TO SAVEPOINT 保存点名称 -- 回滚到保存点
RELEASE SAVEPOINT 保存点名称 -- 删除保存点
模拟场景
/*
转账
A在线买一款价格为500元商品,网上银行转账.
A的银行卡余额为2000,然后给商家B支付500.
商家B一开始的银行卡余额为10000
创建数据库shop和创建表account并插入2条数据
*/
CREATE DATABASE `shop`CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `shop`;
CREATE TABLE `account` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(32) 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';
UPDATE account SET money=money+500 WHERE `name`='B';
COMMIT; -- 提交事务
# rollback; -- 回滚
SET autocommit = 1; -- 恢复自动提交
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。
提取句子主干,就可以得到索引的本质:索引是数据结构。
在一个表中,主键索引只能有一个,唯一索引可以有多个
基础语法:
-- 索引使用
-- 1、在创建表的时候给字段增加索引
-- 2、创建完毕后,增加索引
-- 显示所有的索引信息
SHOW INDEX FROM student
-- 增加一个全文索引 (索引名) 列名
ALTER TABLE school.`student` ADD FULLTEXT INDEX `studentname`(`studentname`);
-- EXPLAIN 分析sq1执行的状况
EXPLAIN SELECT * FROM student; -- 非全文索引
EXPLAIN SELECT * FROM student WHERE MATCH(studentname) AGAINST('张');
建表app_user:
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用户表'
-- 插入100万条数据
DROP FUNCTION IF EXISTS mock_data;
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` = '用户9999';
EXPLAIN SELECT * FROM app_user WHERE `name` = '用户9999'; -- 0.458 sec
-- 创建索引
-- id _ 表名 _ 字段名
-- CREATE INDEX 索引名 ON 表(字段);
CREATE INDEX id_app_user_name ON app_user(`name`);
SELECT * FROM app_user WHERE `name` = '用户9999';
EXPLAIN SELECT * FROM app_user WHERE `name` = '用户9999'; -- 0.001 sec
索引在小数据量的时候,用户不大,但是在大数据的时候,区别十分明显~
索引的数据结构
-- 我们可以在创建上述索引的时候,为其指定索引类型,分两类
hash 类型的索引:查询单条快,范围查询慢
btree 类型的索引:b+树,层数越多,数据量指数级增长(我们就用它,因为innodb默认支持它)
-- 不同的存储引擎支持的索引类型也不一样
InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
MyISAM 不支持事务,支持表级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
Memory 不支持事务,支持表级别锁定,支持 B-tree、Hash 等索引,不支持 Full-text 索引;
NDB 支持事务,支持行级别锁定,支持 Hash 索引,不支持 B-tree、Full-text 等索引;
Archive 不支持事务,支持表级别锁定,不支持 B-tree、Hash、Full-text 等索引;
阅读:MySQL索引背后的数据结构及算法原理
SQLyog 可视化管理
SQL命令操作
用户表:mysql.user
本质:读这张表进行增删改查
-- 创建用户 CREATE USER 用户名 IDENTIFIED BY '密码'
CREATE USER yingxu IDENTIFIED BY '123456'
-- 修改密码 (修改当前用户密码) SET PASSWORD = PASSWORD('密码')
SET PASSWORD = PASSWORD('123')
-- 修改密码 (修改指定用户密码) SET PASSWORD FOR 用户名 = PASSWORD('密码')
SET PASSWORD FOR yingxu = PASSWORD('123')
-- 重命名 RENAME USER 原名字 TO 新名字
RENAME USER yingxu TO yingxu2
-- 用户授权 ALL PRIVILEGES 全部的权限 ,库.表
-- ALL PRIVILEGES 除了给别人授权,其他都能够干
GRANT ALL PRIVILEGES ON *.* TO yingxu2
-- 查看权限
SHOW GRANTS FOR yingxu2 -- 查看指定用户的权限
SHOW GRANTS FOR root@localhost
-- ROOT用户权限:GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-- 撤消权限 REVOKE 哪些权限,在哪个库撤销,给谁撤销
REVOKE ALL PRIVILEGES ON *.* FROM yingxu2
-- 删除用户 DROP USER 用户名
DROP USER kuangshen2
数据库备份必要性:
MySQL数据库备份方法:
# cmd导出
# 导出一张表
# mysqldump -u用户名 -p密码 库名 表名 > 文件名(G:/a.sql)
mysqldump -uroot -p123456 school student >G:/a.sql
# 导出多张表
# mysqldump -u用户名 -p密码 库名 表1 表2 表3 > 文件名(G:/a.sql)
mysqldump -uroot -p123456 school student result >G:/a.sql
# 导出所有表
# mysqldump -u用户名 -p密码 库名 > 文件名(G:/a.sql)
mysqldump -uroot -p123456 school >G:/a.sql
# 导出一个库
# mysqldump -u用户名 -p密码 -B 库名 > 文件名(G:/a.sql)
mysqldump -uroot -p123456 -B school >G:/a.sql
# 导入
# 1.在登录mysql的情况下
# 2.切换到指定的数据库 use 库名
# 3.source 备份文件
source G:/a.sql
# 在不登录的情况下
# mysql -u用户名 -p密码 库名 < 备份文件
什么时候用:
假设你要备份数据库,防止数据丢失。
把数据库个朋友! sql文件给别人即可!
当数据库比较复杂时我们需要设计数据库
糟糕的数据库设计 :
良好的数据库设计 :
软件项目开发周期中数据库设计 :
设计数据库步骤:
收集信息,分析需求
标识实体(把需求落地到每个字段)[Entity]
标识实体之间的关系[Relationship]
问题 : 为什么需要数据规范化?
不合规范的表设计会导致的问题:
信息重复
更新异常
插入异常
删除异常
三大范式(了解)
第一范式 (1NF)
第二范式(2NF)
前提:满足第一范式
每张表只描述一件事情
第三范式(3NF)
(规范数据库的设计)
规范性 和 性能的问题
关联查询的表不得超过三张表
程序会通过数据库驱动,和数据库打交道!
SUN公司为了简化开发人员的(对数据库的统一)操作,提供了一个(Java操作数据库的)规范,俗称JDBC
这些规范的实现由具体的厂商去做~
对于开发人员来说,我们只需要掌握JDBC接口的操作即可!
学习JDBC需要:
创建测试数据库
CREATE DATABASE `jdbcStudy` CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `jdbcStudy`;
CREATE TABLE `users`(
`id` INT PRIMARY KEY,
`NAME` VARCHAR(40),
`PASSWORD` VARCHAR(40),
`email` VARCHAR(60),
birthday DATE
);
INSERT INTO `users`(`id`,`NAME`,`PASSWORD`,`email`,`birthday`)
VALUES(1,'zhangsan','123456','[email protected]','1980-12-04'),
(2,'lisi','123456','[email protected]','1981-12-04'),
(3,'wangwu','123456','[email protected]','1979-12-04')
1、创建一个普通项目
2、导入数据库驱动
3、编写测试代码
//1.加载驱动
// MySQL8.0以上版本:com.mysql.cj.jdbc.Driver
Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动
//2.用户信息和url
// jdbc:mysql://主机:3306/数据库名?useUnicode=true&characterEncoding=utf8&useSSL=false
// 出现Communications link failure错误时useSSL=true改为false
// MySQL8.0以上版本加时区:severTimezone=UTC (UTC要比中国时区早8个小时) 或者 severTimezone=Asia/Shanghai 或者 severTimezone=Hongkong
String url="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false";
String username="root";
String password="123456";
//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("birth="+resultSet.getObject("birthday"));
System.out.println("========================================");
}
//6、释放连接
resultSet.close();
statement.close();
connection.close();
步骤总结:
DriverManager
// MySQL8.0以上版本:com.mysql.cj.jdbc.Driver
// DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动
Connection connection = DriverManager.getConnection(url, username, password);
// connection 代表数据库
// 数据库设置自动提交
// 事务提交
// 事务回滚
connection.rollback();
connection.commit();
connection.setAutoCommit();
URL
// 出现Communications link failure错误时useSSL=true改为false
// MySQL8.0以上版本加时区:severTimezone=UTC (UTC要比中国时区早8个小时) 或者 severTimezone=Asia/Shanghai 或者 severTimezone=Hongkong
String url="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false";
// mysql -- 默认端口:3306
// jdbc:mysql://主机地址:端口号/数据库名?参数1&参数2&参数3
// oralce -- 默认端口:1521
// jdbc:oralce:thin:@localhost:1521:sid
Statement 执行SQL的对象 PrepareStatement 执行SQL的对象
String sql="SELECT * FROM users";//编写SQL
statement.executeQuery();//查询操作返回 ResultSet
statement.execute();// 执行任何SQL
statement.executeUpdate();//更新、插入、删除。都是用这个,返回一个受影响的行数
statement.executeBatch();// 执行多个SQL
ResultSet 查询的结果集:封装了所有的查询结果
resultSet.getObject();// 在不知道列类型的情况下使用
// 如果知道列的类型就使用指定的类型
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getDouble();
resultSet.getDate();
......
遍历,指针
resultSet.beforeFirst();// 移动到最前面
resultSet.afterLast();// 移动到最后面
resultSet.next();// 移动到下一个数据
resultSet.previous();// 移动到前一行
resultSet.absolute(row);// 移动到指定行
释放资源
//6、释放连接
resultSet.close();
statement.close();
connection.close();// 耗资源,用完关掉!
Jdbc中的statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可。
Statement对象的executeUpdate方法,用于向数据库发送增、删、改的sqli语句,executeUpdate执行完后,将会返回一个整数(即增删改语句导致了数据库几行数据发生了变化)。
Statement.executeQuery方法用于向数据库发送查询语句,executeQuery方法返回代表查询结果的ResultSet对象。
CRUD操作-create
使用executeUpdate(String sql)方法完成数据添加操作,示例操作:
Statement st=conn.createStatement();
String sql="INSERT INTO user(...) VALUES(....)";
int num = st.executeUpdate(sql);
if (num>0){
System.out.println("插入成功");
}
CRUD操作-delete
使用executeUpdate(String sql)方法完成数据删除操作,示例操作:
Statement st=conn.createStatement();
String sql="DELETE FROM user WHERE id=1";
int num = st.executeUpdate(sql);
if (num>0){
System.out.println("删除成功");
}
CRUD操作-update
使用executeUpdate(String sql)方法完成数据修改操作,示例操作:
Statement st=conn.createStatement();
String sql="UPDATE user SET `NAME`='',`email`='' WHERE id=1";
int num = st.executeUpdate(sql);
if (num>0){
System.out.println("修改成功");
}
CRUD操作-read
使用executeQuery(String sql)方法完成数据查询操作,示例操作:
Statement st=conn.createStatement();
String sql="SELECT * FROM user WHERE id=1";
ResultSet rs = st.executeQuery(sql);
while (rs.next()){
//根据获取列的数据类型,分别调用rs的相应方法映射到java对象中
}
代码实现
db.properties文本:
# MySQL8.0以上版本:com.mysql.cj.jdbc.Driver
driver=com.mysql.jdbc.Driver
# 出现Communications link failure错误时useSSL=true改为false
# MySQL8.0以上版本加时区:severTimezone=UTC (UTC要比中国时区早8个小时) 或者 severTimezone=Asia/Shanghai 或者 severTimezone=Hongkong
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false
username=root
password=123456
JdbcUtils工具类:
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 {
InputStream in = JdbcUtils.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");
//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 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();
}
}
}
}
executeUpdate
增:
public class TestInsert {
public static void main(String[] args) {
Connection conn =null;
Statement st=null;
ResultSet rs=null;
try {
conn = JdbcUtils.getConnection();//获取数据库连接
st=conn.createStatement();//获得SQL的执行对象
String sql="INSERT INTO users(id,`NAME`,`PASSWORD`,`email`,`birthday`)" +
"VALUES(4,'yingxu','123456','[email protected]','2022-5-8')";
int i = st.executeUpdate(sql);
if (i>0){
System.out.println("插入成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
删:
public class TestDelete {
public static void main(String[] args) {
Connection conn =null;
Statement st=null;
ResultSet rs=null;
try {
conn = JdbcUtils.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 {
JdbcUtils.release(conn,st,rs);
}
}
}
改:
public class TestUpdate {
public static void main(String[] args) {
Connection conn =null;
Statement st=null;
ResultSet rs=null;
try {
conn = JdbcUtils.getConnection();//获取数据库连接
st=conn.createStatement();//获得SQL的执行对象
String sql="UPDATE users SET `NAME`='yingxu',`email`='[email protected]' WHERE id=1";
int i = st.executeUpdate(sql);
if (i>0){
System.out.println("修改成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
executeQuery
public class TestSelect {
public static void main(String[] args) {
Connection conn =null;
Statement st=null;
ResultSet rs=null;
try {
conn = JdbcUtils.getConnection();//获取数据库连接
st=conn.createStatement();//获得SQL的执行对象
String sql="select * from users where id='1'";
rs = st.executeQuery(sql);//查询完毕会返回一个结果集
while (rs.next()){
System.out.println("id="+rs.getInt("id"));
System.out.println("name="+rs.getString("NAME"));
System.out.println("password="+rs.getString("PASSWORD"));
System.out.println("email="+rs.getString("email"));
System.out.println("birth="+rs.getDate("birthday"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
sql存在漏洞,会被攻击导致数据泄露,SQL会被拼接or
public class SQL注入 {
public static void main(String[] args) {
//login("yingxu","123456");//正常登录
login("'or ' 1=1","'or ' 1=1");//SQL注入
}
//登录业务
public static void login(String username,String password){
Connection conn =null;
Statement st=null;
ResultSet rs=null;
try {
conn = JdbcUtils.getConnection();//获取数据库连接
st=conn.createStatement();//获得SQL的执行对象
String sql="select * from users where `NAME`='"+username+"' AND `PASSWORD`='"+password+"'";
rs = st.executeQuery(sql);//查询完毕会返回一个结果集
while (rs.next()){
System.out.println("id="+rs.getInt("id"));
System.out.println("name="+rs.getString("NAME"));
System.out.println("password="+rs.getString("PASSWORD"));
System.out.println("email="+rs.getString("email"));
System.out.println("birth="+rs.getDate("birthday"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
PreparedStatement 可以防止SQL注入。效率更好!
public class TestInsert {
public static void main(String[] args) {
Connection conn =null;
PreparedStatement st =null;
try {
conn = JdbcUtils.getConnection();
//区别
//使用? 占位符代替参数
String sql="INSERT INTO users(id,`NAME`,`PASSWORD`,`email`,`birthday`) VALUES(?,?,?,?,?)";
st = conn.prepareStatement(sql);//预编译SQL,先写sqL,然后不执行
//手动给参数赋值
st.setInt(1,4);//id
st.setString(2,"yingxu");
st.setString(3,"123");
st.setString(4,"[email protected]");
//注意点: sql.Date 数据库 java.sql.Date
// util.Date Java java.util.Date().getTime() 获得时间戳
st.setDate(5,new Date(new java.util.Date().getTime()));
//执行
int i=st.executeUpdate();
if (i>0){
System.out.println("插入成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,null);
}
}
}
public class TestDelete {
public static void main(String[] args) {
Connection conn =null;
PreparedStatement st =null;
try {
conn = JdbcUtils.getConnection();
String sql="delete from users where id=?";
st = conn.prepareStatement(sql);
st.setInt(1,4);
int i = st.executeUpdate();
if (i>0){
System.out.println("删除成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,null);
}
}
}
public class TestUpdate {
public static void main(String[] args) {
Connection conn =null;
PreparedStatement st =null;
try {
conn = JdbcUtils.getConnection();
String sql="update users set NAME=? where id=?";
st = conn.prepareStatement(sql);
st.setString(1,"yingxu2");
st.setInt(2,1);
int i = st.executeUpdate();
if (i>0){
System.out.println("修改成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,null);
}
}
}
public class TestSelect {
public static void main(String[] args) {
Connection conn =null;
PreparedStatement st =null;
ResultSet rs =null;
try {
conn = JdbcUtils.getConnection();
String sql="select * from users where id=?";
st = conn.prepareStatement(sql);
st.setInt(1,1);
rs = st.executeQuery();
while (rs.next()){
System.out.println("name="+rs.getString("NAME"));
System.out.println("password="+rs.getString("PASSWORD"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
public class SQL注入 {
public static void main(String[] args) {
//login("yingxu2","123456");//正常登录
login("' ' or 1=1","' ' or 1=1");//SQL注入
}
//登录业务
public static void login(String username,String password){
Connection conn =null;
PreparedStatement st=null;
ResultSet rs=null;
try {
conn = JdbcUtils.getConnection();//获取数据库连接
String sql="select * from users where `NAME`=? AND `PASSWORD`=?";
st=conn.prepareStatement(sql);//获得SQL的执行对象
st.setString(1,username);
st.setString(2,password);
rs = st.executeQuery();//查询完毕会返回一个结果集
while (rs.next()){
System.out.println("id="+rs.getInt("id"));
System.out.println("name="+rs.getString("NAME"));
System.out.println("password="+rs.getString("PASSWORD"));
System.out.println("email="+rs.getString("email"));
System.out.println("birth="+rs.getDate("birthday"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,st,rs);
}
}
}
连接成功后,可以选择数据库
查看:
更新数据:
连接失败查看原因:
要么都成功,要么都失败
ACID原则
原子性:要么全部完成,要么都不完成
一致性:总数不变
隔离性:多个进程互不干扰
持久性:一旦提交不可逆,持久化到数据库了
隔离性的问题:
代码实现
1、开启事务 conn.setAutoCommit(false);
2、一组业务执行完毕,提交事务
3、可以在catch语句中显示的定义回滚语句,但默认失败就会回滚
表:
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(40),
money FLOAT
);
/*插入测试数据*/
INSERT INTO account(NAME,money) VALUES('A',1000);
INSERT INTO account(NAME,money) VALUES('B',1000);
INSERT INTO account(NAME,money) VALUES('C',1000);
代码:
public class TestTransaction {
public static void main(String[] args) {
Connection conn =null;
PreparedStatement st=null;
ResultSet rs=null;
try {
conn = JdbcUtils.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 {
JdbcUtils.release(conn,st,null);
}
}
}
数据库连接 — 执行完毕 — 释放
连接 – 释放 十分浪费系统资源
池化技术:准备一些预先的资源,过来就连接预先准备好的
------ 开门–业务员:等待 – 服务—
常用连接数10个
最小连接数:10
最大连接数:15 业务最高承载上限
超过最大连接数:排队等待
等待超时:100ms
编写连接池,实现一个接口 DataSource
开源数据源实现 ( 拿来即用 )
DBCP
C3P0
Druid:阿里巴巴的
使用了这些数据库连接池之后,我们在项目开发中就不需要编写连接数据库的代码了!
DBCP
下载:
dbcpconfig.properties文件:
#连接设置(每一个名称不能改动)
driverClassName=com.mysql.jdbc.Driver
#jdbcstudy为数据库名
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false
username=root
password=123456
#
initialSize=10
#最大连接数量
maxActive=50
#
maxIdle=20
#
minIdle=5
#
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=UTF8
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
代码:
public class JdbcUtils_DBCP {
private static BasicDataSource dataSource =null;
static {
try {
InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties properties = new Properties();
properties.load(in);
//创建数据源 工厂模式-->创建
dataSource = BasicDataSourceFactory.createDataSource(properties);
}catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();//从数据源中获取连接
}
//释放连接资源
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();
}
}
}
}
public class TestDBCP {
public static void main(String[] args) {
Connection conn =null;
PreparedStatement st =null;
try {
conn = JdbcUtils_DBCP.getConnection();
//区别
//使用? 占位符代替参数
String sql="INSERT INTO users(id,`NAME`,`PASSWORD`,`email`,`birthday`) VALUES(?,?,?,?,?)";
st = conn.prepareStatement(sql);//预编译SQL,先写sqL,然后不执行
//手动给参数赋值
st.setInt(1,4);//id
st.setString(2,"yingxu");
st.setString(3,"123");
st.setString(4,"[email protected]");
//注意点: sql.Date 数据库 java.sql.Date
// util.Date Java java.util.Date().getTime() 获得时间戳
st.setDate(5,new Date(new java.util.Date().getTime()));
//执行
int i=st.executeUpdate();
if (i>0){
System.out.println("插入成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils_DBCP.release(conn,st,null);
}
}
}
C3P0
c3p0-config.xml
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driverproperty>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=falseproperty>
<property name="user">rootproperty>
<property name="password">123456property>
<property name="acquireIncrement">5property>
<property name="initialPoolSize">10property>
<property name="minPoolSize">5property>
<property name="maxPoolSize">20property>
default-config>
<named-config name="MySQL">
<property name="driverClass">com.mysql.jdbc.Driverproperty>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=falseproperty>
<property name="user">rootproperty>
<property name="password">123456property>
<property name="acquireIncrement">5property>
<property name="initialPoolSize">10property>
<property name="minPoolSize">5property>
<property name="maxPoolSize">20property>
named-config>
c3p0-config>
代码:
public class JdbcUtils_C3P0 {
private static ComboPooledDataSource dataSource =null;
static {
try {
//代码版配置(不建议使用)
// dataSource = new ComboPooledDataSource();
// dataSource.setDriverClass("com.mysql.jdbc.Driver");
// dataSource.setUser("root");
// dataSource.setPassword("123456");
// dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false");
//
// dataSource.setMaxPoolSize(5);
// dataSource.setMinPoolSize(20);
//创建数据源 工厂模式-->创建
dataSource = new ComboPooledDataSource("MySQL");//配置文件写法
}catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();//从数据源中获取连接
}
//释放连接资源
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();
}
}
}
}
public class TestC3P0 {
public static void main(String[] args) {
Connection conn =null;
PreparedStatement st =null;
try {
conn = JdbcUtils_C3P0.getConnection();
//区别
//使用? 占位符代替参数
String sql="INSERT INTO users(id,`NAME`,`PASSWORD`,`email`,`birthday`) VALUES(?,?,?,?,?)";
st = conn.prepareStatement(sql);//预编译SQL,先写sqL,然后不执行
//手动给参数赋值
st.setInt(1,5);//id
st.setString(2,"yingxu");
st.setString(3,"123");
st.setString(4,"[email protected]");
//注意点: sql.Date 数据库 java.sql.Date
// util.Date Java java.util.Date().getTime() 获得时间戳
st.setDate(5,new Date(new java.util.Date().getTime()));
//执行
int i=st.executeUpdate();
if (i>0){
System.out.println("插入成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils_C3P0.release(conn,st,null);
}
}
}
结论
无论使用什么数据源,本质还是一样的,DataSource接口不会变,方法就不会变
apache(阿帕奇)