MySQL

数据库概述

什么是数据库?

所谓的数据库就是指存储和治理数据的仓库

扩大内容1:数据库有哪些分类?(理解)

晚期: 档次式数据库、网络型数据库当初:关系型数据库、非关系型数据库

什么是关系型数据库?

底层以二维表的模式保留数据的库就是关系型数据库

扩大内容2:常见的关系型数据库有哪些?(理解)

  • Sql Server:微软提供,免费,实用于一些中型或大型的我的项目中,在java中的应用占比不高(.NET中应用的较多)
  • Oracle:甲骨文公司提供,免费,实用于一些大型或者超大型的我的项目中,在java中的应用占比十分高
  • mysql:瑞典MySQLAB公司提供,收费开源,实用于一些小型或者中型的我的项目中,在Java中的应用占比拟高(玲珑轻量)mariadb其实就是MySQL的一个分支,用法和MySQL齐全一样。
  • DB2:IBM公司提供,免费,在一些银行、金融等行业中应用较多。在java中的应用占比也不高。
  • Sqlite:迷你数据库,嵌入式设施中(安卓、苹果手机、pad)
  • ...

数据库相干概念

1、什么是数据库服务器

数据库服务器就是一个软件(比方mysql软件)将数据库软件装置在电脑上,以后电脑就是一个数据库服务器。就能够对外提供存取数据的服务

在一个数据库服务器中能够创立多个数据库(dataBases),每一个数据库都是一个独自的仓库。

2、什么是数据库

数据库就是存储和治理数据的仓库,通常状况下,一个网站的中的所有数据会寄存在一个数据库中。例如:

jd.com db_jd(数据库)
taobao.com db_taobao(数据库)
...

3、什么是表

一个数据库中能够创立多张表,每张表用于存储一类信息(数据库),例如:

jd.com中的用户数据 tb_user(表)

jd.com中的商品数据 tb_product(表)

jd.com中的订单数据 tb_order(表)

...

4、什么表记录

一张表中能够蕴含多行表记录,每一行表记录用于存储某一个具体的数据

学生编号

姓名

年龄

1001

刘沛霞

35

1002

陈子枢

18

。。。

。。。

。。。

什么是SQL语言?

SQL是一门用于操作关系型数据库的通用的语言(应用SQL能够操作所有的关系型数据库)

应用SQL能够操作数据库、表、表记录

(1)创立数据库、删除数据库、批改数据库、查询数据库

(2)创立表、删除表、批改表、查问表

(3)新增表记录、删除表记录、批改表记录、查问表记录

应用SQL也能够操作存储过程/视图/索引等。

提醒:SQL是一个规范通用的操作关系型数据库的语言(普通话),每个数据库厂商为了加强本人数据库的性能,都提供了反对本人数据库的语言,称之为数据库的方言。方言不通用!

连贯mysql服务器

通过命令行工具能够登录MySQL客户端,连贯MySQL服务器,从而拜访服务器中的数据。

1、连贯mysql服务器:

mysql -uroot -p明码

-u:前面的root是用户名,这里应用的是超级管理员root;

-p:(小写的p)前面的root是明码,这是在装置MySQL时就曾经指定的明码;

2、连贯mysql服务器并指定IP和端口:

mysql -uroot -proot -h127.0.0.1 -P3306

-h:前面给出的127.0.0.1是服务器主机名或ip地址,能够省略的,默认连贯本机;

-P:(大写的P)前面的3306是连贯端口,能够省略,默认连贯3306端口;

3、退出客户端命令:quit或exit或 q

4、FAQ:常见问题:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetse23a06d3921d5d68c48d6e51024aae8a.jpg?lastModify=1616081589)

解决办法:复制mysql装置目录下的bin目录的门路,将bin目录的门路增加到path环境变量中!!

能够在cmd中通过 echo %path% 查看path环境变量的值。

扩大内容3:

(1)在cmd中连贯mysql服务器之后,能够应用 #、/**/、-- 等符号增加正文,例如:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets7765164a8afaa8d26d9b31ff3356bc33.png?lastModify=1616081589)

(2)在cmd中连贯mysql服务器之后,在书写SQL语句时,能够通过 c 勾销以后语句的执行。例如:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets4c6edbd263d6c870ac745e34d4b207d0.png?lastModify=1616081589)

数据库及表操作

创立、删除、查看数据库

提醒: (1)SQL语句对大小写不敏感。举荐关键字应用大写,自定义的名称(库名,表名,列名、别名等)应用小写。

SHOW DATABASES; -- 查看以后数据库服务器中的所有库
CREATE DATABASE mydb1; -- 创立mydb1库

(2)并且在自定义名称时,针对多个单词不要应用驼峰命名,而是应用下划线连贯。(例如:tab_name,而不是 tabName )

-- 01.查看mysql服务器中所有数据库

show databases; -- 查看所有库

show datebases; -- 谬误写法,是data,不是date

-- 02.进入某一数据库(进入数据库后,能力操作库中的表和表记录)

-- 语法:USE 库名;

use test; -- 进入test数据库
show tables; -- 查看test库中的所有表

-- 查看已进入的库(理解)

select database();

-- 03.查看以后数据库中的所有表

show tables; -- 查看test库中的所有表

-- 04.删除mydb1库

-- 语法:DROP DATABASE 库名;

drop database mydb1; -- 删除不存在的库,这种写法会报错!

-- 思考:当删除的库不存在时,如何防止谬误产生?

drop database if exists mydb1; -- 如果存在mydb1,则删除;

-- 05.从新创立mydb1库,指定编码为utf8

-- 语法:CREATE DATABASE 库名 CHARSET 编码;

-- 须要留神的是,mysql中不反对横杠(-),所以utf-8要写成utf8;

create database mydb1 charset utf8;
-- 建库时,最好指定编码,如果不指定,这个库的编码有可能是latin1

-- 如果不存在则创立mydb1;

create database if not exists mydb1 charset utf8;

-- 06.查看建库时的语句(理解, 并验证数据库库应用的编码)

-- 语法:SHOW CREATE DATABASE 库名;

show create database mydb1;

创立、删除、查看表

-- 07.进入mydb1库,删除stu学生表(如果存在)

-- 语法:DROP TABLE 表名;

use mydb1;
drop table if exists stu; -- 如果存在则删除

-- 08.创立stu学生表(编号[数值类型]、姓名、性别、出生年月、考试成绩[浮点型]),建表的语法:

CREATE TABLE 表名(
列名 数据类型,
列名 数据类型,
...

列名 数据类型

);

SQL语句:

create database if not exists mydb1;
use mydb1;
drop table if exists stu;
create table stu(

id int primary key auto_increment,name varchar(50), -- 50示意name这一列,最多能存50个字符gender varchar(10),birthday date,score double

);

-- 09.查看stu学生表构造

-- 语法:desc 表名

desc stu;

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsimage-20201030000618981.png?lastModify=1616081589)

新增、批改、删除表记录

-- 10.往学生表(stu)中插入记录(数据)

-- 语法:INSERT INTO 表名(列名1,列名2,列名3...) VALUES(值1,值2,值3...);

insert into stu(id,name,gender,birthday,score) value(null,'tom','male','2000-1-1',85);
-- 如果插入的有中文数据,在cmd中,先 set names gbk; 再插入,能够避免中文乱码(或者中文数据插入失败)
insert into stu value(null,'孙尚香','female','2001-2-3',90);
insert into stu value(null,'后嗣','male','2002-3-4',78);

提醒:

(1)当为所有列插入值时,能够省写列名,但值的个数和程序必须和申明时列的个数和程序保持一致!
(2)SQL语句中的值为字符串或日期时,值的两边要加上单引号(有的版本的数据库双引号也能够,但举荐应用单引号)。
(3)(针对cmd窗口)在插入数据之前,先设置编码:set names gbk;
或者用以下命令连贯mysql服务器:
mysql --default-character-set=gbk -uroot -proot
等价于:
mysql -uroot -proot
set names gbk;

-- 11.查问stu表所有学生的信息

-- 语法:SELECT 列名 | * FROM 表名

select from stu; -- 号是通配符,示意查问所有列

-- 12.批改stu表中所有学生的问题,加10分专长分

-- 批改语法: UPDATE 表名 SET 列=值,列=值,列=值...[WHERE子句];

update stu set score=score+10;
-- score+=10 谬误写法,mysql不反对+=

-- 13.批改stu表中编号为1的学生问题,将问题改为83分。

update stu set score=83 where id=1;

提醒:where子句用于对记录进行筛选过滤,保留符合条件的记录,将不符合条件的记录剔除。

-- 14.删除stu表中所有的记录

-- 删除记录语法: DELETE FROM 表名 [where子句]

delete from stu; -- 删除所有记录

-- 仅删除符合条件的(例如,删除id大于1的记录)

delete from stu where id>1;

查问表记录

-- 筹备数据: 以下练习将应用db10库中的表及表记录,请先进入db10数据库!!!

根底查问

SELECT 语句用于从表中选取数据。后果被存储在一个后果表中(称为后果集)。

语法:SELECT 列名称 | * FROM 表名

提醒:(1) *(星号)为通配符,示意查问所有列。

(2)但应用 *(星号)有时会把不必要的列也查出来了,并且效率不如间接指定列名

-- 15.查问emp表中的所有员工,显示姓名,薪资,奖金

select from emp; -- 示意查问emp表中的所有列
select name,sal,bonus from emp; -- 查问指定的列

-- 16.查问emp表中的所有部门和职位

select dept,job from emp;

思考:如果查问的后果中,存在大量反复的记录,如何剔除重复记录,只保留一条?

-- 在select之后、列名之前,应用DISTINCT 剔除反复的记录

select distinct dept,job from emp;

WHERE子句查问

WHERE子句查问语法:SELECT 列名称 | * FROM 表名称 WHERE 列 运算符 值

WHERE子句前面跟的是条件,条件能够有多个,多个条件之间用连接词(or | and)进行连贯

上面的运算符可在 WHERE 子句中应用:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets7b2b5598fb3af3f96d552b5d9779d892.png?lastModify=1616081589)

-- 17.查问emp表中【薪资大于3000】的所有员工,显示员工姓名、薪资

select name, sal from emp where sal>3000;

-- 18.查问emp表中【总薪资(薪资+奖金)大于3500】的所有员工,显示员工姓名、总薪资

select name, sal+bonus from emp
where sal+bonus > 3500;

-- ifnull(列名, 值)函数: 判断指定的列是否蕴含null值,如果有null值,用第二个值替换null值

select name, sal+ifnull(bonus,0) from emp
where sal+ifnull(bonus,0) > 3500;

-- 留神查看下面查问后果中的表头,如何将表头中的 sal+bonus 批改为 "总薪资"

-- 应用as能够为表头指定别名(格局:列名 as 别名)

select name as 姓名, sal+ifnull(bonus,0) as 总薪资 from emp
where sal+ifnull(bonus,0) > 3500;

-- 另外as能够省略

select name 姓名, sal+ifnull(bonus,0) 总薪资 from emp
where sal+ifnull(bonus,0) > 3500;

-- 19.查问emp表中【薪资在3000和4500之间】的员工,显示员工姓名和薪资

select name,sal from emp
where sal>=3000 and sal<=4500;
-- and:必须同时满足and两边的条件

-- 提醒: between...and... 在...和...之间

select name,sal from emp
where sal between 3000 and 4500;

-- 20.查问emp表中【薪资为 1400、1600、1800】的员工,显示员工姓名和薪资

select name,sal from emp
where sal=1400 or sal=1600 or sal=1800;
-- or: 或,只有满足or两边中的任何一个条件即可!

-- 或者应用 in

select name,sal from emp
where sal in(1400,1600,1800);
-- in:只有sal是in前面括号中的任何一个值,就算符合条件!

-- 21.查问薪资不为1400、1600、1800的员工,显示员工姓名和薪资

select name,sal from emp
where not(sal=1400 or sal=1600 or sal=1800);
-- ---------------------------------------
select name,sal from emp
where sal not in(1400,1600,1800);
-- not示意对条件进行取反

-- 22.(本人实现) 查问emp表中【薪资大于4000和薪资小于2000】的员工,显示员工姓名、薪资。

select name,sal from emp
where sal>4000 or sal<2000;

-- 23.(本人实现) 查问emp表中薪资大于3000并且奖金小于600的员工,显示员工姓名、薪资、奖金。

select name,sal,bonus from emp
where sal>3000 and bonus<600;

-- 解决null值

select name,sal,bonus from emp
where sal>3000 and ifnull(bonus,0)<600;

-- 24.查问没有部门的员工(即部门列为null值)

select * from emp
where dept is null; -- 查问dept列为null的

-- 思考:如何查问有部门的员工(即部门列不为null值)

select * from emp
where not(dept is null); -- 查问dept列不为null的
-- 或
select * from emp
where dept is not null; -- 查问dept列不为null的

含糊查问

LIKE 操作符用于在 WHERE 子句中搜寻列中的指定模式。

能够和通配符(%、_)配合应用,其中"%"示意0或多个任意的字符,"_"示意一个任意的字符

语法:SELECT 列 | * FROM 表名 WHERE 列名 LIKE 值

示例:

-- 25.查问emp表中姓名中以"刘"字结尾的员工,显示员工姓名。

select name from emp
where name like '刘%';
-- %示意匹配0个、1个或多个任意字符

-- 26.查问emp表中姓名中蕴含"涛"字的员工,显示员工姓名。

select name from emp
where name like '%涛%';
-- %涛%, 示意匹配某一列蕴含涛的记录,'涛'可能在结尾、结尾或两头某一个地位

-- 27.查问emp表中姓名以"刘"结尾,并且姓名为两个字的员工,显示员工姓名。

-- 以'刘'结尾,姓名为两个字的员工
select name from emp
where name like '刘_'; -- _(下划线)示意任意一个字符
-- 以'刘'结尾,姓名为三个字的员工
select name from emp
where name like '刘__';

多行函数查问

多行函数也叫做聚合(汇集)函数,依据某一列或所有列进行统计。

常见的多行函数有:

多行函数

作用

COUNT( 列名 | * )

统计后果集中指定列的记录的行数。

MAX( 列名 )

统计后果集中某一列值中的最大值

MIN( 列名 )

统计后果集中某一列值中的最小值

SUM( 列名 )

统计后果集中某一列所有值的和

AVG( 列名 )

统计后果集中某一列值的平均值

提醒:(1)多行函数不能用在where子句中

(2)多行函数和是否分组无关,分组与否会间接影响多行函数的执行后果。

(3)多行函数在统计时会对null值进行过滤,间接将null值抛弃,不参加统计。

-- 28.统计emp表中薪资大于3000的员工个数

-- 查问薪资大于3000的员工有哪些?
select * from emp where sal>3000;
-- count:用于统计行数
select count(*) from emp where sal>3000; -- 7
select count(id) from emp where sal>3000; -- 7
select count(bonus) from emp where sal>3000; -- 6
-- 因为bonus中有null值,在统计时,null值间接别抛弃,不参加统计

-- 29.求emp表中的最高薪资

-- emp表中的最高薪资(max函数)
select max(sal) from emp;
-- emp表中的最低薪资(min函数)
select min(sal) from emp;

-- 求emp表中薪资最高的员工
select name,max(sal) from emp;
-- 下面的后果,max(sal)是求薪资中的最大值,而name只是name列中的第一个姓名,因而name和max(sal)很可能是不对应的。因而后果是错的!

-- 30.统计emp表中所有员工的薪资总和(不蕴含奖金)

select sum(sal) from emp; -- 求薪资这一列所有值的和
select sum(bonus) from emp; -- 求奖金这一列所有值的和

-- 31.统计emp表员工的均匀薪资(不蕴含奖金)

select avg(sal) from emp; -- 薪资平均值(总薪资/12)
select avg(bonus) from emp; -- 奖金平均值(总奖金/11),有null值

多行函数须要留神的问题:

  • 多行函数和是否分组无关,如果查问后果中的数据没有通过分组,默认整个查问后果是一个组,多行函数就会默认统计以后这一个组的数据。产生的后果只有一个。
  • 如果查问后果中的数据通过分组(分的组不止一个),多行函数会依据分的组进行统计,有多少个组,就会统计出多少个后果。

例如:统计emp表中的人数

-- 如果没有分组,默认整个查问后果是一个组
select count(*) from emp; -- 12

后果返回的就是emp表中的所有人数

再例如:依据性别对所有员工进行分组,再统计每组的人数,显示性别和对应人数

-- 依据性别分组,最终分为两个组,统计两个组的人数(10、2)
select gender,count(*) from emp group by gender;

分组查问

GROUP BY 语句依据一个或多个列对后果集进行分组。

在分组的列上咱们能够应用 COUNT,SUM,AVG,MAX,MIN等函数。

语法:SELECT 列 | * FROM 表名 [WHERE子句] GROUP BY 列;

-- 32.对emp表,依照部门对员工进行分组,查看分组后成果。

-- 依照部门分组(3个组)
select * from emp group by dept;
-- 对下面3个组的人数进行统计
select dept,count(*) from emp group by dept;

-- 33.对emp表依照职位进行分组,并统计每个职位的人数,显示职位和对应人数

-- 依照职位分组(3个组)
select * from emp group by job;
-- 对下面3个组的人数进行统计
select job,count(*) from emp group by job;

-- 34.对emp表依照部门进行分组,求每个部门的最高薪资(不蕴含奖金),显示部门名称和最高薪资

-- 依据部门进行分组(3个组)
select * from emp group by dept;
-- 求每个部门(每个组)的最高薪资
select dept,max(sal) from emp group by dept;

排序查问

应用 ORDER BY 子句将后果集中记录依据指定的列排序后再返回

语法:SELECT 列名 FROM 表名 ORDER BY 列名 [ASC|DESC]

ASC(默认)升序,即从低到高;DESC 降序,即从高到低。

-- 35.对所有员工的薪资进行升序(从低到高)排序,显示员工姓名、薪资。

-- 依照薪资进行排序,默认是升序(asc),即从低到高
select name,sal from emp order by sal;
select name,sal from emp order by sal asc;

-- 36.对emp表中所有员工的奖金进行降序(从高到低)排序,显示员工姓名、奖金。

-- 依照奖金进行降序排序
select name,bonus from emp order by bonus desc;

分页查问

在mysql中,通过limit进行分页查问,查问公式为:

limit (页码-1)*每页显示记录数, 每页显示记录数

-- 37.查问emp表中的所有记录,分页显示:每页显示3条记录,返回所有页的数据。

-- 分页查问:每页显示3条,返回第1页数据:
select * from emp limit 0, 3;
-- 分页查问:每页显示3条,返回第2页数据:
select * from emp limit 3, 3;
-- 分页查问:每页显示3条,返回第3页数据:
select * from emp limit 6, 3;
-- 分页查问:每页显示3条,返回第4页数据:
select * from emp limit 9, 3;

-- 38.求emp表中薪资最高的前3名员工的信息,显示姓名和薪资

-- 依照薪资对员工信息进行排序,降序排序
select name,sal from emp order by sal desc;
-- 在下面查问的根底上,分页查问,每页显示3条,查问第一页
select name,sal from emp order by sal desc limit 0, 3;

其余函数

函数名

解释阐明

curdate()

获取以后日期,格局是:年月日

curtime()

获取以后工夫 ,格局是:时分秒

sysdate()/now()

获取以后日期+工夫,格局是:年月日 时分秒

year(date)

返回date中的年份

month(date)

返回date中的月份

day(date)

返回date中的天数

hour(date)

返回date中的小时

minute(date)

返回date中的分钟

second(date)

返回date中的秒

CONCAT(s1,s2..)

将s1,s2 等多个字符串合并为一个字符串

CONCAT_WS(x,s1,s2..)

同CONCAT(s1,s2,..)函数,然而每个字符串之间要加上x,x是分隔符

-- 39.查问emp表中所有【在1993和1995年之间出世】的员工,显示姓名、出生日期。

select name,birthday from emp
where birthday between 1993 and 1995;
-- birthday(日期) 和 1993、1995不能比拟,因为类型不同

-- 解决方案1:将1993、1995转换成日期后,再和birthday进行比拟
select name,birthday from emp
where birthday between '1993-1-1' and '1995-12-31';

-- 解决方案2:将birthday中的年份取出,和1993、1995进行比拟
select name,birthday from emp
where year(birthday) between 1993 and 1995;

-- 40.查问emp表中本月过生日的所有员工

-- 求本月过生日的员工
select * from emp
where month(now())=month(birthday);
-- 求下个月过生日的员工
select * from emp
where month(now())+1=month(birthday); -- 问题:如果以后是12月份

select * from emp
where ( month(now())+1 )%12=month(birthday); -- 问题:如果以后是11月份
-- 最终版本:求下个月过生日的员工
select * from emp
where ( month(now())+1 )%12=month(birthday)%12;

-- 41.查问emp表中员工的姓名和薪资(薪资格局为: xxx(元) 、 xxx/元)

select name,concat(sal,'(元)') from emp;
select name,concat(sal,'/元') from emp;
select name,concat_ws('/',sal,'元') from emp;

mysql的数据类型

MySQL反对多种类型,大抵能够分为三类:数值、字符串(字符)类型和日期/工夫。

数值类型

MySQL中反对多种整型,其实很大水平上是雷同的,只是存储值的大小范畴不同而已,上面的表显示了罕用的数值类型的存储和范畴。

类型

大小

用处

tinyint

1 byte(绝对于java中的byte)

小整数值

smallint

2 bytes(绝对于java中的short)

大整数值

int

4 bytes(绝对于java中的int)

大整数值

bigint

8 bytes(绝对于java中的long)

极大整数值

float

4 bytes(绝对于java中的float)

单精度浮点数值

double

8 bytes(绝对于java中的double)

双精度浮点数值

decimal

-

存储准确的小数值

decimal数据类型用于在数据库中存储准确的数值

decimal用法:column_name  decimal(P,D);
1)P是示意无效数的精度。 P范畴为1〜65。
2)D是示意小数点后的位数。 D的范畴是0~30。MySQL要求D小于或等于(<=)P
例如:amount DECIMAL(6,2); 示意amount列最多能够存储6位数字,小数位数为2位,因而,amount列的范畴是从-9999.99到9999.99

字符串类型

mysql中的字符串类型有很多,例如:CHAR、VARCHAR、TEXT、BLOB、MEDIUMTEXT、MEDIUMBLOB、LONGTEXT、LONGBLOB等

上面说几个较为常见的字符串类型:

类型

大小

用处

char

0~255(字符)

定长字符串

varchar

0~65535(字节)

变长字符串

mediumtext

0~16 777 215(字节,约16M)

大文本/长文本

mediumblob

0~16 777 215(字节,约16M)

二进制

longtext

0-4 294 967 295(字节,约4G)

极大文本

longblob

0-4 294 967 295(字节,约4G)

极大二进制

1、char(n) 定长字符串,最长255个字符。n示意字符数,例如:

-- 创立user表,指定用户名为char类型,字符长度不超过10 个字符

create table user(
  username char(10),
  ...
);

所谓的定长,是当插入的数据的长度小于指定的长度时,残余的空间会用空格填充。(这样会节约空间)

char类型往往用于存储长度固定的数据,这样不会节约空间,并且在存储数据的效率上比varchar类型略高一些。

2、varchar(n) 变长字符串,最长不超过65535个字节,n示意字符数,个别超过255个字符,会应用text类型,例如:

iso8859-1码表:一个字符占用1个字节,1*n < 65535, n最多等于 65535
utf8码表:一个中文汉字占用3个字节,3*n < 65535,n最多等于 65535/3
GBK码表:一个中文汉字占用2个字节,2*n < 65535,n最多等于 65535/2

-- 创立user表,指定用户名为varchar类型,长度不超过10个字符

create table user(
username varchar(10)
);

所谓的不定长,是当插入的数据的长度小于指定的长度时,残余的空间能够留给别的数据应用。(节俭空间)

总结:长度固定的数据,用char类型,这样既不会节约空间,效率也比拟高

如果长度不固定,应用varchar类型,这样不会节约空间。

日期类型

示意工夫值的日期和工夫类型为date、time、datetime、timestamp和year。

类型

大小(bytes)

范畴

格局

用处

date

3

1000-01-01/9999-12-31

YYYY-MM-DD

日期

time

3

'-838:59:59'/'838:59:59'

HH:MM:SS

工夫

datetime

8

1000-01-01 00:00:00/9999-12-31 23:59:59

YYYY-MM-DD HH:MM:SS

日期和工夫

timestamp

4

1970-01-01 00:00:00/2038

YYYYMMDD HHMMSS

日期和工夫

year

1

1901/2155

YYYY

年份

timestamp:工夫戳,理论存储的是一个从1970-1-1到该 日期工夫值 的工夫毫秒值

mysql的字段束缚

字段束缚/列束缚 --> 束缚: 限度

主键束缚

主键束缚:如果为一个列增加了主键束缚,那么这个列就是主键,主键的特点是惟一且不能为空。

主键的作用: 作为一个惟一标识,惟一的示意一条表记录(作用相似于人的身份证号,能够惟一的示意一个人一样。)

增加主键束缚,例如将id设置为主键:

create table stu(
id int primary key,
...
);

如果主键是数值类型,为了不便插入主键(并且保障插入数据时,主键不会因为反复而报错),能够设置一个主键自增策略。

create table stu(
id int primary key auto_increment,
...
);

主键自增策略是指:设置了自增策略的主键,能够在插入记录时,不给id赋值,只须要设置一个null值,数据库会主动为id调配一个值(AUTO_INCREMENT变量,默认从1开始,前面顺次+1),这样既能够保障id是惟一的,也省去了设置id的麻烦。

将id主键设置为自增:

create table stu(
id int primary key auto_increment,
...
);

非空束缚

非空束缚:如果为一个列增加了非空束缚,那么这个列的值就不能为空,但能够反复。

增加非空束缚,例如为password增加非空束缚:

create table user(
password varchar(50) not null,
...
);

惟一束缚

惟一束缚:如果为一个列增加了惟一束缚,那么这个列的值就必须是惟一的(即不能反复),但能够为空。

增加惟一束缚,例如为username增加惟一束缚及非空束缚:

create table user(
username varchar(50) unique not null,
...
);

外键束缚

外键其实就是用于告诉数据库两张表数据之间对应关系的这样一个列。

这样数据库就会帮咱们保护两张表中数据之间的关系。

(1) 创立表的同时增加外键

create table emp(
id int,
name varchar(50),
dept_id int,
foreign key(dept_id) references dept(id)
);

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets6b56c23ba5cf6a63d9f9e8a8b5fd8467.png?lastModify=1616081589)

(1)如果是要示意两张表的数据之间存在对应关系,只须要在其中的一张表中增加一个列,保留另外一张表的主键,就能够保留两张表数据之间的关系。

然而增加的这个列(dept_id)对于数据库来说就是一个一般列,数据库不会晓得两张表存在任何关系,因而数据库也不会帮咱们保护这层关系。

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsimage-20200530160646152.png?lastModify=1616081589)

(2)如果将dept_id列设置为外键,等同于告诉数据库,部门表和员工表之间存在对应关系,dept_id列中的数据要参考部门的主键,数据库一旦晓得部门和员工表之间存在关系,就会帮咱们保护这层关系。

思考:如果在创立表时没有指定外键,那么前期该如何指定外键?以及如何删除外键?

表关系

常见的表关系分为以下三种:

一对多(多对一)·、一对一、多对多

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets1e4a36daf517c88d356cbc3c8dbd3ea8.png?lastModify=1616081589)

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets392ee4d4c0f8fdd86adc4b9abc3ad6f5.png?lastModify=1616081589)

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsimage-20201030000709386.png?lastModify=1616081589)

多表查问

-- 筹备数据: 以下练习将应用db30库中的表及表记录,请先进入db30数据库!!!

连贯查问

-- 42.查问部门和部门对应的员工信息

select * from dept,emp;

下面的查问中存在大量谬误的数据,个别咱们不会间接应用这种查问。

笛卡尔积查问:所谓笛卡尔积查问就是指,查问两张表,其中一张表有m条记录,另一张表有n条记录,查问的后果是m*n条。

尽管笛卡尔积查问中蕴含大量谬误数据,但咱们能够通过where子句将谬误数据剔除,保留下来的就是正确数据。

-- 通过where子句将笛卡尔积查问后果中谬误的数据剔除,保留正确数据!
select * from dept,emp
where emp.dept_id = dept.id;
-- 因为id在两张表中都存在,所以为了辨别,必须在列名后面加上[表名.]

通过where子句将笛卡尔积查问中的谬误数据剔除,保留正确的数据,这就是连贯查问!

下面的查问能够换成上面的查问:

select * from dept inner join emp
on emp.dept_id=dept.id;
-- 内连贯查问,后果和下面的连贯查问雷同!

左外连贯查问

-- 43.查问【所有部门】及部门对应的员工,如果某个部门下没有员工,员工显示为null

-- 这是连贯查问,后果中蕴含的都是[有员工的部门]和[有部门的员工]
select * from dept, emp
where dept_id=dept.id;
-- 如果要查问其中一张表(比方部门表)的所有记录,另一张表只显示对应的记录
select * from dept left join emp
on dept_id=dept.id;
-- ------------------------------
select * from emp right join dept
on dept_id=dept.id;

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsimage-20201030000731608.png?lastModify=1616081589)

左外连贯查问:能够将右边表中的所有记录都查问进去,左边表只显示和右边绝对应的数据,如果右边表中某些记录在左边没有对应的数据,左边显示为null即可。

右外连贯查问

-- 44.查问【所有员工】及员工所属的部门,如果某个员工没有所属部门,部门显示为null即可

-- 查问的后果中都是有员工的部门和有部门的员工
select * from dept, emp
where emp.dept_id=dept.id;
-- 如果要查问所有员工及员工对应的部门
select * from dept right join emp
on emp.dept_id=dept.id;
-- 或者应用左外连贯查问
select * from emp left join dept
on emp.dept_id=dept.id;

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsimage-20201030000757358.png?lastModify=1616081589)

右外连贯查问:能够将左边表中的所有记录都查问进去,右边表只显示和左边绝对应的数据,如果左边表中某些记录在右边没有对应的数据,能够显示为null。

扩大:如果想将两张表中的所有数据都查问进去(左外+右外并去除重复记录),能够应用全外连贯查问,然而mysql又不反对全外连贯查问。

select * from dept left join emp on emp.dept_id=dept.id
union
select * from dept right join emp on emp.dept_id=dept.id;

能够应用union将左外连贯查问的后果和右外连贯查问的后果合并在一起,并去除反复的记录。例如:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsd0c3a3d4ad4f48d4779d1d40cac31755.png?lastModify=1616081589)

须要留神的是:union能够将两条SQL语句执行的后果合并,然而有前提:

(1)两条SQL语句查问的后果列数必须统一

(2)两条SQL语句查问的后果列名、程序也必须统一

并且union默认就会将两个查问中反复的记录去除(如果不心愿去除重复记录,能够应用union all)

子查问练习

-- 筹备数据:以下练习将应用db40库中的表及表记录,请先进入db40数据库!!!

-- 45.列出薪资比'王海涛'的薪资高的所有员工,显示姓名、薪资

-- 1)查问出'王海涛'的薪资是多少
select sal from emp where name='王海涛'; -- 2450
-- 2)查问比'王海涛'薪资高的员工有哪些?
select name,sal from emp where sal > (select sal from emp where name='王海涛');

-- 46.列出与'刘沛霞'从事雷同职位的所有员工,显示姓名、职位。

-- 1)查问出'刘沛霞'从事的职位
select job from emp where name='刘沛霞'; -- 推销员
-- 2)和'刘沛霞'从事雷同职位的员工有哪些?
select name,job from emp
where job=(select job from emp where name='刘沛霞');

多表查问练习

-- 47.列出在'培优部'任职的员工,假设不晓得'培优部'的部门编号,显示部门名称,员工名称。

-- 1)连贯查问部门表和员工表, 显示部门和部门对应的员工
select dept.name,emp.name from dept, emp
where emp.dept_id = dept.id;
-- 表别名: 能够为表名起一个别名,一旦起了表别名,就肯定要用别名代替表名
select d.name,e.name from dept d, emp e
where e.dept_id = d.id;
-- 2)只查问部门名称为'培优部'的员工
select d.name,e.name from dept d, emp e
where e.dept_id = d.id and d.name='培优部';

-- 48.(自查问)列出所有员工及其间接下级,显示员工姓名、下级编号,下级姓名

/* emp e1(员工表), emp e2(下级表)
查问的表: emp e1, emp e2
显示的列: e1.name, e1.topid, e2.name
连贯条件: e1.topid = e2.id;
*/
select e1.name, e1.topid, e2.name
from emp e1, emp e2
where e1.topid = e2.id;

-- 49.列出最低薪资大于1500的各种职位,显示职位和该职位的最低薪资

-- 1)求出每个职位的最低薪资(依照职位进行分组,求每个组/职位的最低薪资)
select job,min(sal) from emp group by job;
-- 2)求出最低薪资大于1500的职位有哪些?
select job,min(sal) from emp
/ where xxx /
group by job
having min(sal)>1500;

补充内容:where和having子句的区别:

相同点: 都是对查问的后果进行筛选过滤
不同点:
(1)where是在分组之前对数据进行筛选过滤; 而having是在分组之后对数据进行筛选过滤
(2)where子句中不能应用多行数据, 也不能应用列别名, 但能够应用表别名; 而having中能够应用多行函数, 也能够应用列别名和表别名;

-- 50.列出在每个部门就任的员工数量、平均工资。显示部门编号、员工数量,均匀薪资。

-- 依据部门编号(dept_id)进行分组
select dept_id,count(*),avg(sal) from emp group by dept_id;

-- 51.列出受雇日期早于间接下级的所有员工,显示员工编号、员工姓名、部门名称。

/* emp e1(员工表), emp e2(下级表), dept d
查问的表:emp e1,emp e2,dept d
显示的列:e1.id,e1.name,d.name
连贯条件:e1.topid=e2.id e1.dept_id=d.id
筛选条件: e1.hdate<e2.hdate
*/
select e1.id,e1.name,d.name
from emp e1,emp e2,dept d
where e1.topid=e2.id and e1.dept_id=d.id
and e1.hdate<e2.hdate;

-- 补充52:查问员工表中薪资最高的员工信息

-- 查问emp表中的最高薪资
select max(sal) from emp;
-- 查问emp表中的最高薪资及对应的员工姓名
select name,max(sal) from emp; -- 谬误,name和最高薪资不对应

-- 形式1:排序+分页,依据薪资降序排序,分页查问,取第一条
select * from emp order by sal desc limit 0,1;
-- 形式2:子查问,求emp表中的最高薪资,再依据这个薪资找对应的员工
-- 求emp表中的最高薪资是多少
select max(sal) from emp; -- 5000
-- 求emp表中最高薪资对应的员工
select * from emp where sal=(select max(sal) from emp);

扩大内容

数据库备份与复原

B站视频链接:https://www.bilibili.com/video/BV1ez4y19776

备份数据库

在cmd窗口中(未登录的状态下),能够通过如下命令对指定的数据库进行备份:

mysqldump -u用户名 -p 数据库的名字 > 备份文件的地位

示例1: 对db40库中的数据(表,表记录)进行备份,备份到 d:/db40.sql文件中

mysqldump -uroot -p db40 > d:/db40.sql

键入明码,如果没有提醒,即示意备份胜利!

也能够一次性备份所有库,例如:

对mysql服务器中所有的数据库进行备份,备份到 d:/all.sql文件中

mysqldump -uroot -p --all-database > d:/all.sql

键入明码,如果没有提醒谬误(正告信息不是谬误,能够疏忽),即示意备份胜利!

复原数据库

1、复原数据库形式一:

在cmd窗口中(未登录的状态下),能够通过如下命令对指定的数据库进行复原:

mysql -u用户名 -p 数据库的名字 < 备份文件的地位

示例:将d:/db40.sql文件中的数据恢复到db60库中

-- 在cmd窗口中(已登录的状态下),先创立db60库:

create database db60 charset utf8;

-- 在cmd窗口中(未登录的状态下)

mysql -uroot -p db60 < d:/db40.sql

2、复原数据库形式二:

在cmd窗口中(已登录的状态下),能够通过source执行指定地位的SQL文件:

source sql文件的地位

示例:将d:/db40.sql文件中的数据恢复到db80库中

-- 在cmd窗口中(已登录的状态下),先创立db80库,进入db80库:

create database db80 charset utf8;
use db80;

-- 再通过source执行指定地位下的sql文件:

source d:/db40.sql

Navicat软件的应用

B站视频链接:https://www.bilibili.com/video/BV1yA41147Vi/

Navicat Premium是一套带图形用户界面的数据库管理工具,让你从繁多应用程序中同时连贯MySQL、MariaDB、MongoDB、SQL Server、Oracle、PostgreSQL 和 SQLite数据库。应用Navicat能够疾速、轻松地创立、治理和保护数据库。

1、应用navicat连贯mysql服务器(应用cmd连贯mysql服务器)
2、查看所有库、进入数据库、创立数据库、删除数据库、批改数据库
3、创立表、查看表、批改表、删除表
4、新增表记录、查问表记录、批改表记录、删除表记录
5、应用navicat书写SQL语句操作数据库、表和表记录
...


现创立学生表:

use test; -- 进入test库
drop table if exists stu; -- 删除学生表(如果存在)
create table stu( -- 创立学生表

id int, -- 学生idname varchar(20), -- 学生姓名gender char(1), -- 学生性别birthday date -- 出生年月

);

批改表—新增列

语法:ALTER TABLE tabname ADD col_name datatype DEFAULT expr;

1、往stu表中增加score列,double类型

alter table stu add score double;

批改表—批改列

语法:ALTER TABLE tabname MODIFY (col_name datatype DEFAULT expr...);

1、批改id列,将id设置为主键

alter table stu modify id int primary key;

2、批改id列,将id主键设置为主动增长

alter table stu modify id int auto_increment;

批改表—删除列

语法:ALTER TABLE tabname DROP [COLUMN] col_name;

1、删除stu表中的score列

alter table stu drop score;

增加或删除主键及自增

思考:a) 在建表时,如何为id指定主键束缚和自增?

b) 建好的表,如何通过批改增加主键束缚和自增?

c) 如何删除表中的主键束缚和自增?

1、创立stu学生表,不增加主键自增, 查看表后果

use mydb1; -- 切换到mydb1库
drop table if exists stu; -- 删除stu学生表(如果存在)
create table stu( -- 重建stu学生表,没有主键自增
  id int,
  name varchar(20),
  gender char(1),
  birthday date
);
desc stu; -- 查看表构造

表构造如下: 没有主键束缚和自增。

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets4f7cf015ff7829f26d326b6f443628cc-1595871301917.png?lastModify=1616081589)

2、如果表没有创立,或者要删除重建,在创立时能够指定主键或主键自增

drop table if exists stu; -- 删除stu表
create table stu( -- 从新创立stu表时,指定主键自增
  id int primary key auto_increment,
  name varchar(20),
  gender char(1),
  birthday date
);
desc stu; -- 查看表构造

表构造如下: 曾经增加了主键束缚和自增。

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets1606d49b391f6e0d23b526912dbb22cf-1595871300159.png?lastModify=1616081589)

3、如果不想删除重建表,也能够通过批改表增加主键或主键自增

再次执行第1步,创立stu学生表,不增加主键自增,查看表后果

-- 例如: 将stu学生表中的id设置为主键和主动增长

alter table stu modify id int primary key auto_increment;
desc stu; -- 查看表构造

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets1606d49b391f6e0d23b526912dbb22cf.png?lastModify=1616081589)

如果只增加主键束缚,不设置自增

alter table stu modify id int primary key;

如果曾经增加主键束缚,仅仅设置自增,但需注意:

(1)如果没有设置主键,不可增加自增

(2)只有当主键是数值时,才能够增加自增

alter table stu modify id int auto_increment;

4、如果想删除主键自增

-- 删除主键自增时,要先删除自增

alter table stu modify id int;

-- 再删除主键束缚

alter table stu drop primary key;
desc stu; -- 查看表构造

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets4f7cf015ff7829f26d326b6f443628cc.png?lastModify=1616081589)

增加外键束缚

1、增加外键形式一:建表时增加外键

现有部门表如下:

-- 创立部门表

create table dept(
id int primary key auto_increment, -- 部门编号
name varchar(20) -- 部门名称
);

要求创立员工表,并在员工表中增加外键关联部门主键

-- 创立员工表

create table emp(
  id int primary key auto_increment, -- 员工编号
  name varchar(20), -- 员工姓名
  dept_id int, -- 部门编号
  foreign key(dept_id) references dept(id) -- 指定dept_id为外键
);

2、增加外键形式二:建表后增加外键

现有部门表和员工表:

-- 创立部门表

create table dept(
  id int primary key auto_increment, -- 部门编号
  name varchar(20) -- 部门名称
);

-- 创立员工表

create table emp(
  id int primary key auto_increment, -- 员工编号
  name varchar(20), -- 员工姓名
  dept_id int -- 部门编号
);

-- 如果表已存在,能够应用上面这种形式:

alter table emp add constraint fk_dept_id foreign key(dept_id) references dept(id);

其中 fk_dept_id (名字由本人定义),是指外键束缚名称,也能够将【constraint fk_dept_id】省略,MySQL会主动调配一个外键名称,未来能够通过该名称删除外键。

foreign key(dept_id)中的dept_id为外键

删除外键束缚

1、首先通过 “show create table 表名”语法,查问含有外键表的建表语句,例如:

show create table emp;

显示后果如下:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetse5ad1306d0985dadf8ffbbd3eff61174.png?lastModify=1616081589)

其中,emp_ibfk_1是在创立表时,数据库为外键束缚指定的一个名字,删除这个名字即可删除外键关系,例如:

alter table emp drop foreign key emp_ibfk_1;

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsimage-20201030000837137.png?lastModify=1616081589)

外键删除胜利!

增加外键束缚(多对多)

-- 现有学生(stu)表和老师(tea)表:

-- 创立学生表

create table stu(
  stu_id int primary key auto_increment, -- 学生编号
  name varchar(20) -- 学生姓名
);

-- 创立老师表

create table tea(
tea_id int primary key auto_increment, -- 老师编号
name varchar(20) -- 老师姓名
);

-- 增加第三方表(stu_tea)示意学生表和老师表关系

-- 创立学生和老师关系表

create table stu_tea(
  stu_id int, -- 学生编号
  tea_id int, -- 老师编号
  primary key(stu_id,tea_id), -- 设置联结主键
  foreign key(stu_id) references stu(stu_id), -- 增加外键
  foreign key(tea_id) references tea(tea_id) -- 增加外键
);

其中为了避免反复数据,将stu_id和tea_id设置为联结主键。

将stu_id设置为外键,参考stu表中的stu_id列

并将tea_id设置为外键,参考tea表中的tea_id列

级联更新、级联删除

-- 创立db20库、dept表、emp表并插入记录

-- 删除db20库(如果存在),并从新创立db20库

drop database if exists db20;
create database db20 charset utf8;
use db20;

-- 创立部门表, 要求id, name字段

create table dept(
id int primary key auto_increment, -- 部门编号
name varchar(20) -- 部门名称
);

-- 往部门表中插入记录

insert into dept values(null, '财务部');
insert into dept values(null, '人事部');
insert into dept values(null, '科技部');
insert into dept values(null, '销售部');

-- 创立员工表, 要求id, name, dept_id

create table emp(
  id int primary key auto_increment, -- 员工编号
  name varchar(20), -- 员工姓名
  dept_id int, -- 部门编号
  foreign key(dept_id) references dept(id) -- 指定外键
   on update cascade -- 级联更新
   on delete cascade -- 级联删除
);
insert into emp values(null, '张三', 1);
insert into emp values(null, '李四', 2);
insert into emp values(null, '老王', 3);
insert into emp values(null, '赵六', 4);
insert into emp values(null, '刘能', 4);

级联更新:主表(dept表)中的主键产生更新时(例如将销售部的id改为40),从表(emp表)中的记录的外键数据也会跟着该表(即赵六和刘能的部门编号也会更新为40)

级联删除:如果不增加级联删除,当删除部门表中的某一个部门时(例如删除4号部门),若该部门在员工表中有对应的员工(赵六和刘能),删除会失败!

若果增加了级联删除,当删除部门表中的某一个部门时,若该部门在员工表中有对应的员工,会在删除部门的同时,将员工表中对应的员工也删除!

where中不能应用列别名

SQL语句的书写程序:

select * | 列名 -- 确定要查问的列有哪些
from 表名 -- 确定查问哪张表
where 条件 -- 通过筛选过滤,剔除不符合条件的记录
group by 分组的列 -- 指定依据哪一列进行分组
having 条件 -- 通过条件对分组后的数据进行筛选过滤
order by 排序的列 -- 指定依据哪一列进行排序
limit (countPage-1)*rowCount, rowCount -- 指定返回第几页记录以及每页显示多少条

SQL语句的执行程序:

from 表名 -- 确定查问哪张表
where 条件 -- 通过筛选过滤,剔除不符合条件的记录
select * | 列名 列别名 -- 确定要查问的列有哪些,
group by 分组的列 -- 指定依据哪一列进行分组
having 条件 -- 通过条件对分组后的数据进行筛选过滤
order by 排序的列 -- 指定依据哪一列进行排序
limit (countPage-1)*rowCount, rowCount

对于where中不能应用列别名然而能够应用表别名?**

是因为,表别名是申明在from中,from先于where执行,先申明再应用没有问题,然而列别名是申明在select中,where先于select执行,如果先应用列别名,再申明,这样执行会报错!!

unit07-JDBC

学习指标:

  • 理解什么是JDBC?为什么要学习这门技术?
  • 把握通过JDBC连贯并拜访数据库
  • 把握PreparedStatement传输器的应用(SQL注入攻打)
  • 把握什么是连接池?为什么要应用连接池?
  • 把握C3P0连接池的用法
  • 数据库事务(放在前面讲,框架之前)

JDBC概述

什么是JDBC?为什么要学习JDBC?

JDBC(Java DataBase Connectivity) Java数据库连贯

其实就是利用Java语言/程序连贯并拜访数据库的一门技术

之前咱们能够通过CMD或者navicat等工具连贯数据库

但在企业开发中,更多的是通过程序(Java程序)连贯并拜访数据库,通过Java程序拜访数据库,就须要用到JDBC这门技术。

如何通过JDBC程序拜访数据库?

1、提出需要:

创立一个 jt_db 数据库,在库中创立一个account表,并插入三条记录,而后利用Java程序查问出account表中所有的记录,并将查问的后果打印在管制台上。

2、开发步骤:

(1)筹备数据, 创立jt_db库, 创立account表

drop database if exists jt_db;
create database jt_db charset utf8;
use jt_db;
create table account(

id int primary key auto_increment,name varchar(50),money double

);
insert into account values(null, 'tom', 1000);
insert into account values(null, 'andy', 1000);
insert into account values(null, 'tony', 1000);

如果曾经执行过课前材料中的"SQL脚本文件",此步骤能够跳过。

(2)创立JAVA工程:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsimage-20200318153229937.png?lastModify=1616081589)

(3)导入jar包——mysql驱动包:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsimage-20201030000404489.png?lastModify=1616081589)

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets6ba3042ef2947f314836128a5c19e2c2.png?lastModify=1616081589)

(4)创立类并实现JDBC程序(六个步骤)

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsdccd4a8163b4835b74e77f945d106683.png?lastModify=1616081589)

代码实现:

public static void main(String[] args) throws Exception {
   //1.注册数据库驱动
   Class.forName("com.mysql.jdbc.Driver");
   //2.获取数据库连贯
   Connection conn = DriverManager.getConnection(
       "jdbc:mysql://localhost:3306/jt_db?characterEncoding=utf-8",
       "root", "root");
   //3.获取传输器
   Statement stat = conn.createStatement();
   //4.发送SQL到服务器执行并返回执行后果
   String sql = "select * from account";
   ResultSet rs = stat.executeQuery( sql );
   //5.处理结果
   while( rs.next() ) {
       int id = rs.getInt("id");
       String name = rs.getString("name");
       double money = rs.getDouble("money");
       System.out.println(id+" : "+name+" : "+money);
  }
   //6.开释资源
   rs.close();
   stat.close();
   conn.close();
   System.out.println("TestJdbc.main()....");
}

3、执行后果:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsa37ab7234bb1beb988f4b9c01c8cee2f.png?lastModify=1616081589)

JDBC API总结

1、注册数据库驱动

Class.forName("com.mysql.jdbc.Driver");

所谓的注册驱动,就是让JDBC程序加载mysql驱动程序,并治理驱动

驱动程序实现了JDBC API定义的接口以及和数据库服务器交互的性能,加载驱动是为了方便使用这些性能。

2、获取连贯之数据库URL

Connection conn = DriverManager.getConnection(
   "jdbc:mysql://localhost:3306/jt_db?characterEncoding=utf-8",
   "root", "root" );

DriverManager.getConnection() 用于获取数据连贯,返回的Connection连贯对象是JDBC程序连贯数据库至关重要的一个对象。

参数2参数3别离是所连贯数据库的用户名和明码。

参数1:"jdbc:mysql://localhost:3306/jt_db" 是连贯数据库的URL,用于指定拜访哪一个地位上的数据库服务器及服务器中的哪一个数据库,其写法为:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets7edb0faa5fac5f29cd19411909c708cb.png?lastModify=1616081589)

当连贯本地数据库,并且端口为3306,能够简写为如下模式:

jdbc:mysql:///jt_db

3、Statement传输器对象

Statement stat = conn.createStatement();
该办法返回用于向数据库服务器发送sql语句的Statement传输器对象

该对象上提供了发送sql的办法:

executeQuery(String sql) --
用于向数据库发送查问类型的sql语句,返回一个ResultSet对象中
executeUpdate(String sql) --
用于向数据库发送更新(减少、删除、批改)类型的sql语句,返回一个int值,示意影响的记录行数

4、ResultSet后果集对象

ResultSet对象用于封装sql语句查问的后果,也是一个十分重要的对象。该对象上提供了遍历数据及获取数据的办法。

(1)遍历数据行的办法

next() – 使指向数据行的箭头向下挪动一行,并返回一个布尔类型的后果,true示意箭头指向了一行数据,false示意箭头没有指向任何数据(前面也没有数据了)

(2)获取数据的办法

getInt(int columnIndex)
getInt(String columnLable)
getString(int columnIndex)
getString(String columnLable)
getDouble(int columnIndex)
getDouble(String columnLable)
getObject(int columnIndex)
getObject(String columnLable)
...

5、开释资源

rs.close();
stat.close();
conn.close();

此处开释资源必须依照肯定的程序开释,越晚获取的越先敞开。所以先敞开rs对象,再敞开stat对象,最初敞开conn对象。

另,为了防止下面的程序抛出异样,开释资源的代码不会执行,应该把开释资源的代码放在finally块中.

try{
...
}catch(Exception e){
...
}finally{
   if (rs != null) {
       try {
      rs.close();
      } catch (SQLException e) {
           e.printStackTrace();
      } finally {
           rs = null;
      }
  }
   if (stat != null) {
       try {
      stat.close();
      } catch (SQLException e) {
           e.printStackTrace();
      } finally {
           stat = null;
      }
  }
   if (conn != null) {
       try {
      conn.close();
      } catch (SQLException e) {
           e.printStackTrace();
      } finally {
           conn = null;
      }
  }
}

增删改查

JDBC增删改查

1、新增:往account表中增加一个名称为john、money为3500的记录

/ 1、新增:往account表中增加一个名称为john、money为3500的记录 /
@Test
public void testInsert() {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
//注册驱动并获取连贯
conn = JdbcUtil.getConn();
//获取传输器
stat = conn.createStatement();
//发送sql语句到服务器执行,并返回执行后果
String sql = "insert into account values(null, 'john', 3500)";
int rows = stat.executeUpdate( sql );
//处理结果
System.out.println( "影响行数: "+rows );
} catch (Exception e) {
e.printStackTrace();
} finally {
//通过JdbcUtil工具类中的close办法开释资源
JdbcUtil.close(conn, stat, rs);
}
}

2、批改:将account表中名称为john的记录,money批改为1500

/ 2、批改:将account表中名称为john的记录,money批改为1500 /
@Test
public void testUpdate() {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
//注册驱动并获取连贯
conn = JdbcUtil.getConn();
//获取传输器
stat = conn.createStatement();
//发送sql语句到服务器执行,并返回执行后果
String sql = "update account set money=1500 where name='john'";
int rows = stat.executeUpdate( sql );
//处理结果
System.out.println( "影响行数: "+rows );
} catch (Exception e) {
e.printStackTrace();
} finally {
//通过JdbcUtil工具类中的close办法开释资源
JdbcUtil.close(conn, stat, rs);
}
}

3、查问(本人实现):查问account表中名称为john的记录

/ 3、查问:查问account表中id为1的记录 /
@Test
public void testFindById() {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
//注册驱动并获取连贯
conn = JdbcUtil.getConn();
//获取传输器
stat = conn.createStatement();
//执行sql语句,返回执行后果
String sql = "select * from account where id=1";
rs = stat.executeQuery( sql );
//处理结果
if( rs.next() ) {
int id = rs.getInt("id");
String name = rs.getString("name");
double money = rs.getDouble("money");
System.out.println( id+" : "+name+" : "+money);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn, stat, rs);
}
}

4、删除(本人实现):删除account表中名称为john的记录

/ 4、删除:删除account表中名称为john的记录 /
@Test
public void testDelete() {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
//注册驱动并获取连贯
conn = JdbcUtil.getConn();
//获取传输器
stat = conn.createStatement();
//发送sql语句到服务器执行,并返回执行后果
String sql = "delete from account where name='john'";
int rows = stat.executeUpdate( sql );
//处理结果
System.out.println( "影响行数: "+rows );
} catch (Exception e) {
e.printStackTrace();
} finally {
//通过JdbcUtil工具类中的close办法开释资源
JdbcUtil.close(conn, stat, rs);
}
}

单元测试补充

单元测试:不必创立新的类,也不必提供main函数,也不必创立类的实例,就能够间接执行一个办法

加了@Test注解的办法,能够通过单元测试(junit)框架测试该办法。底层会创立该办法所在类的实例,通过实例调用该办法。

@Test
public void testInsert() {
System.out.println("TestPreparedStatement.testInsert()");
}

可能应用@Test单元测试测试的办法必须满足如下几个条件:

(1)办法必须是公共的
(2)办法必须是非动态的
(3)办法必须是无返回值的
(4)办法必须是无参数的
(5)进行单元测试的办法或类,命名时不要命名为 Test/test

PreparedStatement

在下面的增删改查的操作中,应用的是Statement传输器对象,而在开发中咱们用的更多的传输器对象是PreparedStatement对象,PreparedStatement是Statement的子接口,比Statement更加平安,并且可能进步程序执行的效率。

Statement 父对象

PreparedStatement 子对象

模仿用户登录案例

(1)筹备数据

use jt_db;
create table user(
  id int primary key auto_increment,
  username varchar(50),
  password varchar(50)
);
insert into user values(null,'张三','123');
insert into user values(null,'李四','234');

(2)创立LoginUser 类,提供 main 办法 和 login 办法。

public static void main(String[] args) {
/* 1、提醒用户登录,提醒用户输出用户名并接管用户名

*  2、提醒用户输出明码并接管明码*  3、依据用户名和明码查问用户信息*/

// 1、提醒用户登录,提醒用户输出用户名并接管用户名
Scanner sc = new Scanner(System.in);
System.out.println( "请登录:" );
System.out.println( "请输出用户名:" );
String user = sc.nextLine();

// 2、提醒用户输出明码并接管明码
System.out.println( "请输出明码:" );
String pwd = sc.nextLine();

// 3、依据用户名和明码查问用户信息
login( user, pwd );
}
/**

  • 依据用户名和明码查问用户信息
  • @param user 用户名
  • @param pwd 明码

*/
private static void login(String user, String pwd) {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
//1.注册驱动并获取连贯
conn = JdbcUtil.getConn();
//2.获取传输器,执行sql并返回执行后果
stat = conn.createStatement();
String sql = "select * from user where username='"+user+"' and password='"+pwd+"'";
rs = stat.executeQuery(sql);
System.out.println( sql );
//3.处理结果
if( rs.next() ) { //有数据 -- 用户名明码都正确
System.out.println("祝贺您登录胜利!");
}else { //没数据 -- 用户名或明码不正确
System.out.println("登录失败, 用户名或明码不正确!");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.开释资源
JdbcUtil.close(conn, stat, rs);
}
}

执行时,输出:

请登录:
请输出用户名:
张飞'#'
请输出明码:

select * from user where username='张飞'#'' and password=''
祝贺您登录胜利了!

或输出

请登录:
请输出用户名:
张飞' or '1=1
请输出明码:

select * from user where username='张飞' or '1=1' and password=''
祝贺您登录胜利了!

或输出

请登录:
请输出用户名:

请输出明码:
' or '2=2
select * from user where username='' and password='' or '2=2'
祝贺您登录胜利了!

SQL注入攻打

通过下面的案例,咱们发现在执行时,不输出明码只输出用户名也能够登陆胜利。这就是SQL注入攻打。

SQL注入攻打产生的起因: 因为后盾执行的SQL语句是拼接而来的:

select * from user where username='"+user+"' and password='"+pwd+"'

其中的参数是用户提交过去的,如果用户在提交参数时,在参数中掺杂了一些SQL关键字(比方or)或者特殊符号(#、-- 、' 等),就可能会导致SQL语句语义的变动,从而执行一些意外的操作(用户名或明码不正确也能登录胜利)!

避免SQL注入攻打

如何避免SQL注入攻打?

(1)应用正则表达式对用户提交的参数进行校验。如果参数中有(# -- ' or等)这些符号就间接完结程序,告诉用户输出的参数不非法

(2)应用PreparedStatement对象来代替Statement对象。

上面通过第二种形式解决SQL注入攻打:增加loginByPreparedSatement办法,在办法中,应用PreparedStatement来代替Statement作为传输器对象应用,代码示例:

/**

  • 依据用户名和明码查问用户信息
  • @param user 用户名
  • @param pwd 明码

*/
private static void login(String user, String pwd) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1.注册驱动并获取连贯
conn = JdbcUtil.getConn();
//2.获取传输器,执行sql并返回执行后果
String sql = "select * from user where username=? and password=?";
ps = conn.prepareStatement( sql );
//设置SQL语句中的参数
ps.setString( 1 , user );
ps.setString( 2 , pwd );
//执行SQL语句
rs = ps.executeQuery();//这里不要再传输SQL语句

//3.处理结果
if( rs.next() ) { //有数据 -- 用户名明码都正确
System.out.println("祝贺您登录胜利!");
}else { //没数据 -- 用户名或明码不正确
System.out.println("登录失败, 用户名或明码不正确!");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.开释资源
JdbcUtil.close(conn, ps, rs);
}
}

再次执行程序,依照下面的操作登录。此时,曾经胜利的避免了SQL注入攻打问题了。

PreparedStatement对象是如何避免SQL注入攻打的:

应用PreparedStatement对象是先将SQL语句的骨架发送给服务器编译并确定下来,编译之后,SQL语句的骨架和语义就不会再被扭转了,再将SQL语句中的参数发送给服务器,即便参数中再蕴含SQL关键字或者特殊符号,也不会导致SQL语句的骨架或语义被扭转,只会被当作一般的文原本解决!


应用PreparedStatement对象能够避免SQL注入攻打

而且通过办法设置参数更加的不便且不易出错!

还能够从某些方面进步程序执行的效率!

数据库连接池

什么是连接池

常量池/线程池/连接池

池:指内存中的一片空间(容器,比方数组、汇合)

连接池:就是将连贯寄存在容器中,供整个程序共享,能够实现连贯的复用,缩小连贯创立和敞开的次数,从而进步程序执行的效率!

为什么要应用连接池

1、传统形式操作数据库

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsed6ceb2908a9c2a66c3336a651056ce0.png?lastModify=1616081589)

Connection conn = DriverManager.getConnection( url, user, pwd ); //创立连贯对象
......
conn.close(); //敞开连贯, 销毁连贯

在传统形式中,每次用户须要连贯拜访数据库时,都是创立一个连贯对象,基于这个连贯对象拜访数据库,用完连贯后,会将连贯敞开(conn.close())。

因为每次创立连贯和敞开连贯十分的耗时间而且耗资源,因而会导致程序执行的效率低下。

2、应用连接池操作数据库

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsb7566731966b48e3d918bbd205d30030.jpg?lastModify=1616081589)

能够在程序一启动时,就创立一批连贯放在一个连接池中(容器),当用户须要连贯时,就从连接池中获取一个连贯对象,用完连贯后,不要敞开,而是将连贯再还回连接池中,这样一来,用来用去都是池中的这一批连贯,实现了连贯的复用,缩小了连贯创立和敞开的次数,从而进步了程序执行的效率!

如何应用C3P0连接池

dbcp/c3p0/druid

所有的连接池都要实现一个接口——DataSource(数据源),因而连接池也被叫做数据源!

应用C3P0连接池开发步骤:

1、导入开发包

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsd9837a8227847f3e22aa28a9e5bf6aa5.png?lastModify=1616081589)

2、创立数据库连接池(对象)

ComboPooledDataSource cpds = new ComboPooledDataSource();

3、设置连贯数据库的根本信息

1)形式一:(不举荐) 间接将参数通过 pool.setXxx办法设置给c3p0程序

这种形式间接将参数写死在了程序中,前期一旦参数发生变化,就要批改程序,要从新编译我的项目、从新公布我的项目,十分麻烦。

//设置连贯数据库的根本信息
pool.setDriverClass( "com.mysql.jdbc.Driver" );
pool.setJdbcUrl( "jdbc:mysql:///jt_db?characterEncoding=utf-8" );
pool.setUser( "root" );
pool.setPassword( "root" );

2)形式二:将连贯参数提取到properties文件中(举荐)

文件必须放在src(源码根目录)目录下 !

文件名必须叫做 c3p0.properties !

在类目录下(开发时能够放在src或者相似的源码目录下),增加一个c3p0.properties文件,配置内容如下:

c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql:///jt_db?characterEncoding=utf-8
c3p0.user=root
c3p0.password=root

这种形式因为是c3p0到指定的地位下寻找指定名称的properties文件,所以文件的地位必须是放在src或其余源码根目录下,文件名必须是c3p0.properties。

3)形式三:将连贯参数提取到xml文件中(举荐)

文件必须放在src(源码根目录)目录下 !

文件名必须叫做 c3p0-config.xml

在类目录下(开发时能够放在src或者相似的源码目录下),增加一个c3p0-config.xml文件,配置内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
   <default-config>
       <property name="driverClass">com.mysql.jdbc.Driver</property>
       <property name="jdbcUrl">jdbc:mysql:///jt_db?characterEncoding=utf-8</property>
       <property name="user">root</property>
       <property name="password">root</property>
   </default-config>
</c3p0-config>

这种形式因为是c3p0到指定的地位下寻找指定名称的xml文件,所以文件的地位必须是放在src或其余源码根目录下,文件名必须是c3p0-config.xml。

4、从连接池中获取一个连贯对象并进行应用

Connection conn = pool.getConnection();

5、用完连贯后将连贯还回连接池中

conn.close()
/* 如果是本人创立的连贯对象,这个连贯对象没有通过任何的改变,调用

  • conn.close办法,是将连贯对象敞开
  • 如果是从连接池中获取的连贯对象,该连贯对象在返回时就曾经被连接池
  • 革新了,将连贯对象的close办法改为了还连贯到连接池中

*/

扩大:学生信息管理系统

通过JDBC实现学生信息管理系统

B站视频链接:https://www.bilibili.com/video/BV1ka4y1x7M6

筹备数据

建库建表语句如下:

-- 1、创立数据库jt_db数据库(如果不存在才创立)
create database if not exists jt_db charset utf8;
use jt_db; -- 抉择jt_db数据库
-- 2、在 jt_db 库中创立 stu 表(学生表)
drop table if exists stu;
create table stu(
  id int,
  name varchar(50),
  gender char(2),
  addr varchar(50),
  score double
);
-- 3、往 stu 表中, 插入记录
insert into stu values(1001,'张三','男', '北京', 86);

性能实现

运行程序控制台提醒如下:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets4b9ad722dca6065b01a5202f85e2d89c.png?lastModify=1616081589)

输出a:查问所有学生信息

输出b:增加学生信息

输出c:依据id批改学生信息

输出d:依据id删除学生信息

查问所有学生信息

在控制台中输出操作代码"a",成果如下:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets55a1ab054846de0d92525b6292f7b930.png?lastModify=1616081589)

增加学生信息

在控制台中输出操作代码"b",成果如下:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsc9f2e18f2a41668cd39ddaccd712290c.png?lastModify=1616081589)

依据id批改学生信息

在控制台中输出操作代码"c",成果如下:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets40de75c8a4f08fc4a4b581b358be6a87.png?lastModify=1616081589)

依据id删除学生信息

在控制台中输出操作代码"d",成果如下:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets9eda8bae1797eced35a1e4907009db83.png?lastModify=1616081589)

unit08-transaction

今日指标:

  • 理解事务的作用
  • 把握事务的四大个性(面试)
  • 理解事务的三个并发读问题
  • 把握mysql开启和完结事务
  • 理解事物的四个隔离级别

事务及四大个性

什么是事务

数据库事务(Database Transaction),是指作为单个逻辑工作单元执行的一系列操作,要么齐全地执行,要么齐全地不执行。

简略的说:事务就是将一堆的SQL语句(通常是增删改操作)绑定在一起执行,要么都执行胜利,要么都执行失败,即都执行胜利才算胜利,否则就会复原到这堆SQL执行之前的状态。

上面以银行转账为例,张三转100块到李四的账户,这至多须要两条SQL语句:

  • 给张三的账户减去100元;

    update 账户表 set money=money-100 where name='张三';

  • 给李四的账户加上100元。

    update 账户表 set money=money+100 where name='李四';

如果在第一条SQL语句执行胜利后,在执行第二条SQL语句之前,程序被中断了(可能是抛出了某个异样,也可能是其余什么起因),那么李四的账户没有加上100元,而张三却减去了100元,在现实生活中这必定是不容许的。

如果在转账过程中退出事务,则整个转账过程中执行的所有SQL语句会在一个事务中,而事务中的所有操作,要么全都胜利,要么全都失败,不可能存在胜利一半的状况。

也就是说给张三的账户减去100元如果胜利了,那么给李四的账户加上100元的操作也必须是胜利的;否则,给张三减去100元以及给李四加上100元都是失败的。

事务的四大个性

事务的四大个性(ACID)是:

1)原子性(Atomicity):一个事务(transaction)中的所有操作,要么全副实现,要么全副不实现,不会完结在两头某个环节。即便事务在执行过程中产生谬误,也会被回滚(Rollback)到事务开始前的状态,就像这个事务素来没有执行过一样。

即:事务中所有操作是不可再宰割的原子单位。事务中所有操作要么全副执行胜利,要么全副执行失败。

2)一致性(Consistency):在事务开始之前和事务完结当前,数据库的完整性没有被毁坏。这示意写入的材料必须完全符合所有的预设规定,这蕴含材料的精确度、串联性以及后续数据库能够自发性地实现预约的工作。

例如:转账业务,无论事务执行胜利与否,参加转账的两个账户金额之和在事务前后应该是放弃不变的。

张三:1000 1000-500=500 1000
李四:1000 1000+500=1500 1000

3)隔离性(Isolation):数据库容许多个并发事务同时对其数据进行读写和批改的能力,隔离性能够避免多个事务并发执行时因为穿插执行而导致数据的不统一。即:隔离性能够在事务并发时,让不同的事务隔离开来,一个事务看不到另一个事务正在进行中的状态。

例如:在A事务中,查看另一B事务(正在批改张三的账户金额)中张三的账户金额,要查看到B事务之前的张三的账户金额,要么查看到B事务之后张三的账户金额。

事务1: 查问A、B账户金额之和
事务2: A转账给B 500元

    A - 500 = 500    B + 500 = 1500

4)持久性(Durability):事务处理完结后,对数据的批改就是永恒的,即使系统故障也不会失落。因为事务一旦提交,事务中所有的数据操作都必须被长久化到数据库中,即便事务提交后,数据库马上解体,在数据库重启时,也必须能保障通过某种机制复原数据。

开启事务---A给B转账500元
A: 1000 - 500 = 500 (胜利了) -- 在日志中记录,事务胜利,A账户金额更新为500
B: 1000 + 500 = 1500 (胜利了) -- 在日志中记录,事务胜利,B账户金额更新为1500
完结事务---回滚/提交

MySQL中的事务

mysql中操作事务

在默认状况下,MySQL每执行一条SQL语句,都是一个独自的事务。因为底层在执行SQL语句之前会主动开启事务,在SQL语句执行完后,会主动提交事务!

如果须要在一个事务中蕴含多条SQL语句,那么须要手动开启事务和完结事务。

  • 开启事务:begin / start transaction;

    begin 或 start transaction 显式地开启一个事务;

  • 提交事务:commit;

    commit会提交事务,并使已对数据库进行的所有批改成为永久性的;

  • 回滚事务:rollback;

    rollback会回滚事务,即撤销正在进行的所有未提交的批改。数据也会复原到事务开始前的状态,就像这个事务素来没有执行过一样。

上面演示事务在转账例子中的利用:

筹备数据:

-- 1、创立数据库jt_db数据库(如果不存在才创立)
create database if not exists jt_db charset utf8;
use jt_db; -- 抉择jt_db数据库
-- 2、在 jt_db 库中创立 acc 表(银行账户表),要求有id(主键),name(姓名),money(账户金额)
drop table if exists acc;
create table acc(
  id int primary key auto_increment,
  name varchar(50),
  money double
);
-- 3、往 acc 表中, 插入2条记录
insert into acc values(null,'A',1000);
insert into acc values(null,'B',1000);
-- 查问acc表中的所有记录
select * from acc;

上面别离演示事务开启及执行一系列SQL之后,回滚事务、提交事务及中断操作的成果。

-- rollback(回滚事务)

-- 查问acc账户表中A和B的金额
select * from acc;
-- 开启事务
start transaction;
-- 开始转账,A账户减去100元
update acc set money=money-100 where name='A';
-- 查问acc账户表中A和B的金额
select * from acc;
-- B账户减少100元
update acc set money=money+100 where name='B';
-- 查问acc账户表中A和B的金额
select * from acc;
-- 回滚事务
rollback;
-- 再次查问acc账户表中A和B的金额
select * from acc;

-- commit(提交事务):将下面的操作再做一次,最初将rollback替换为commit,即提交事务

commit;

-- 中断操作:将下面的操作再做一次,最初将rollback替换为quit,即中断操作

quit;

jdbc中操作事务

在JDBC中实现转账例子

提醒:JDBC中默认是主动提交事务,所以须要敞开主动提交,改为手动提交事务

也就是说, 敞开了主动提交后, 事务就主动开启, 然而执行完后须要手动提交或者回滚!!

(1)执行上面的程序,程序执行没有异样,转账胜利!A账户减去100元,B账户减少100元。

(2)将第4步、5步两头的代码放开,再次执行程序,在转账过程中抛异样,转账失败!因为事务回滚,所以A和B账户金额不变。

public static void main(String[] args) throws SQLException {
  Connection conn = null;
  Statement stat = null;
  ResultSet rs = null;
  try {
      //1.获取连贯
      Class.forName("com.mysql.jdbc.Driver");
  //2.获取数据库连贯
  Connection conn = DriverManager.getConnection(
      "jdbc:mysql://localhost:3306/jt_db?characterEncoding=utf-8",
      "root", "root");
      //2.敞开JDBC主动提交事务(默认开启事务)
      conn.setAutoCommit(false);
      //3.获取传输器
      stat = conn.createStatement();
       / A给B转账100元 /
      //4.A账户减去100元
      String sql = "update acc set money=money-100 where name='A'";
      stat.executeUpdate(sql);
      //int i = 1/0; // 让程序抛出异样,中断转账操作
      //5.B账户加上100元
      sql = "update acc set money=money+100 where name='B'";
      stat.executeUpdate(sql);
      //6.手动提交事务
      conn.commit();
      System.out.println("执行胜利!提交事务...");
  } catch (Exception e) {
  e.printStackTrace();
  //一旦其中一个操作出错都将回滚,使两个操作都不胜利
  conn.rollback();
  System.out.println("执行失败!回滚事务...");
  } finally{
  rs.close();
  stat.close();
  conn.close();
  }
}