1数据库
1.1名词解释
DB:数据库Database,用于存放数据仓库
DBMS:数据库管理系统 DataBase Management System,管理数据库
table : 表,用于描述实体(对象)集合,需要提供行和列
1.2数据库分类
网状型数据库
层次型数据库
关系型数据库(Relationship DataBase Management System)
非关系型数据库:NoSql (not only sql)
1.3关系型数据库种类
DB2 : IBM公司,收费
Oracle : oracle公司,收费
MS sql server : 微软,收费,只适合window平台
Mysql : 免费
2 Mysql数据库
2.1介绍
2.2安装
其中MySQLInstanceConfig.exe可用于绿色版Mysql的设置.
mysql默认端口:3306
mysql将UTF-8编码称为:UTF8(只此一家)
mysql默认账号:root
2.3mysql启动和停止
手动方式(了解)
cmd> mysqld --console
使用window服务
服务开发方式:运行 “services.msc”
启动
cmd> net start mysql
停止
cmd> net stop mysql
2.4mysql登录
方式1(推荐):格式 “mysql -u账号 -hip地址 -p密码”
例: cmd> mysql -uroot -h192.168.18.98 -p1234
方式2:
例: cmd> mysql --user=root --host=192.168.18.98 --password=1234
2.5mysql常用命令
显示当前数据库服务器中的数据库列表
mysql> show databases;
显示当前所使用的数据库名称
mysql> select database();
显示当前数据库的状态
mysql> status;
使用数据库
mysql> use 要使用已存在的数据库名称;
显示数据库中的数据表
mysql>show tables;
显示当前数据库中某表的表结构 (DESCRIBE )
mysql> desc user;
显示所支持的字符集
show character set;
使用sql语句,注意:sql语句必须以分号(;)结尾
%mysql% 表示mysql的安装目录
%mysql%/data/mysql 表示mysql核心数据,切记不能删除
3 SQL语句
3.1介绍
sql:结构化查询语言(structured query language), 是操作和检索(查询)关系型数据库的标准语言.
?注:ANSI(American National Standards Institute,美国国家标准学会)制定的标准
各个数据库生产厂商,要遵循sql标准,同时又开发出自己数据特有的特性
3.2SQL分类
DDL,Data Definition Language,数据定义语言
管理表的结构和索引的结构 -- 【结构】
保留字:CREATE (create 创建)),DROP (drop 删除),ALTER (alter 修改)
DML,Data Manipulation Language,数据操作语言
用于添加,修改和删除表中的行(记录) -- 【数据】
保留字:INSERT (insert 录入),DELETE(delete 删除)和TRUNCATE ,UPDATE(update 更新)
DQL,Data Queries Language,数据查询语言
用以从表中获得数据 -- 【查询(检索)】
保留字:SELECT (select 查询),WHERE (where 条件),GROUP BY (group by 分组),having和order by(order by 排序)
DTL,Data Transaction Language,事务处理语言
确保被DML语句影响的表的所有行及时得以更新 -- 【事务】
保留字:BEGIN TRANSACTION (begin transaction 开启事务),COMMIT (commit 提交事务)和ROLLBACK(rollback 回滚事务)
DCL,Data Control Language,数据控制语言
授权用户或用户组操作和访问数据的权限 --【权限】
保留字:GRANT(grant 授权)或REVOKE (revoke 取消权限)
3.3DDL:数据定义语言
对数据库、表、列 进行结构的操作
3.3.1数据库操作
创建数据库
mysql> create database [IF NOT EXISTS] 数据库名称 [character set 字符集] ;
数据库默认使用 平台设置编码(my.ini文件)
删除数据库
mysql> drop database [IF EXISTS] 数据库名称;
修改数据库
mysql> alter database character set 修改数据库字符集;(不建议)
3.3.2表操作
创建表
mysql> create table 表名(字段描述1,字段描述2, … );
* 多个字段描述 使用逗号分隔
* 最后一个字段描述没有逗号
字段描述的格式:字段名 字段类型 [约束]
删除表 mysql> drop table 表名;
修改表 mysql> alter table 表名 rename [to] 新表名;
3.3.3列操作
添加列 mysql> alter table 表名 add [column] 字段描述
删除列 mysql> alter table 表名 drop [column] 字段名
修改列 mysql> alter table 表名 change [column] old_字段名 new_字段描述
3.4字段类型
3.4.1字符串
mysql类型 |
java类型 |
描述 |
char(n) |
java.lang.String |
char表示固定字符串,n表示字符个数。 例如:char(5) , 当前字段必须写5个字符. 如果录入的数据”abc”,右侧自动添加空格,结果“abc空空” |
varchar(n) |
java.lang.String |
varchar表示可变字符串,n表示字符个数. 例如:varchar(5),当前字段最多存放5个字符. 如果例如的数据”abc”,存放的就是”abc” |
3.4.2数字
3.4.3日期
注:
java.sql 包下面对象,之后都在dao层使用。
但一般使用java.util.Date 对象
java.util.Date 有三个子类:
java.sql.Date, java.sql.Time,java.sql.Timestamp
java.sql 包创建实例
Date date = new java.util.Date();
new java.sql.Timestamp(date.getTime());
3.4.4大数据
字节大数据: blob ,Binary Large Object
字符大数据: clob , Character Large Object
3.5 DML数据操作语言
对指定的表中的数据进行:添加、删除、修改
?录入数据
格式1: mysql> insert into 表名 values(字段对应的值,值2,值3,….);
格式2: mysql> insert into 表名(字段名1,字段名2,…) values(值1, 值2,….)
?修改数据
格式1: mysql> update 表名 set 字段=值,字段=值,….. #将表中所有的字段都进行修改
格式2: mysql> update 表名 set 字段=值,字段=值,…. where 条件 # 将符合条件的字段进行修改
?删除数据
格式1: mysql> delete from 表名; #清空表中的所有数据
格式2: mysql> delete from 表名 where 条件; #删除符合条件
3.6DQL数据查询语言
select distinct 字段|*
from 表名
where 条件
group by 分组字段
having 分组条件
order by 排序字段 asc|desc
3.6.1准备数据
create table t_user(
id varchar(22) primary key,
firstname varchar(22),
age smallint,
secondname varchar(22),
counts smallint
);
insert into `t_user`(`id`,`firstname`,`age`,`secondname`,`counts`)
values (‘u001‘,‘张‘,18,‘飞‘,60),
(‘u002‘,‘赵‘,20,‘云‘,58),
(‘u003‘,‘关‘,22,‘羽‘,80),
(‘u004‘,‘刘‘,25,‘备‘,98),
(‘u006‘,‘黄‘,18,‘盖‘,NULL),
(‘u005‘,‘王‘,12,‘子云‘,20),
(‘u007‘,‘诸葛‘,24,‘亮‘,100);
3.6.2没有条件查询
查询所有
mysql> select * from t_user;
查询部分信息
select secondname,age from t_user;
select id, firstname, age, secondname, counts from t_user; #等价于查询所有,推荐写法
查询用户编号、姓名,及格
select id,concat(firstname,secondname),counts-60 from t_user;
?修改上面查询显示字段名称,用"姓名"表示姓名,用"及格"表示及格
#字段的别名格式:字段名 [as] 别名
select id,concat(firstname,secondname) as 姓名,counts-60 及格 from t_user;
select id,concat(firstname,secondname) as ‘姓 名‘,counts-60 及格 from t_user; #引号可以使用
select id,concat(firstname,secondname) as `姓 名`,counts-60 及格 from t_user; #
3.6.3带有条件查询
查询分数等于60的学生
select * from t_user where counts=60; #如果是数字,可以使用单引号,一般不用。
查询姓"张"学生
select * from t_user where firstname = ‘张‘;
#注意:如果是字符串必须使用引号,建议是单引号
查询年龄大于18的学生
select * from t_user where age > 18;
显示分数在60-80的学生
select * from t_user where counts >=60 and counts <=80;
select * from t_user where counts between 60 and 80;
查询编号为u001和u002的学生
select * from t_user where id = ‘u001‘ or id = ‘u002‘;
select * from t_user where id in (‘u001‘, ‘u002‘);
查询年龄是18或20的学生
select * from t_user where age = 18 or age = 20;
select * from t_user where age in (18, 20);
查询名中含有"云"的学生
### like语句,模糊查询,不完全匹配查询
### 格式:字段 like 值
## 特殊符号:% _
% 匹配若干
‘%云‘,必须以“云”结尾
‘云%‘,必须以“云”开头
‘%云%‘,含有“云”
_ 匹配一个字符
# 查询名中含有"云"的学生
select * from t_user where secondname like ‘%云%‘;
# 查询名中第二字还有"云"的学生
select * from t_user where secondname like ‘_云%‘;
查询分数小于60 或 大于90分的学生
//这种情况下属于两边,不能用between and, 而只能是or, 何况or和and功能强大得多.
select * from t_user where counts < 60 or counts > 90;
查询分数等于60 或者 分数大于90并且年龄大于23
select * from t_user where counts = 60 or counts > 90 and age > 23;
#优先级问题: and 优先 or
select * from t_user where counts = 60 or (counts > 90 and age > 23);
查询没有考试的学生
select * from t_user where counts is null;
查询所有考试的学生
select * from t_user where counts is not null;
3.6.4聚合函数(同查询结果进行相应统计,显示一行一列数据)
count(): 用于计数,查询共有有多少条记录
select count(*) from t_user;
select count(id) from t_user; #统计指定字段值
select count(counts) from t_user; #且不进行null计数
select count(1) from t_user;
平均成绩 avg()
select avg(counts) from t_user; #不计算null
select sum(counts)/count(*) from t_user;#计算null,指的是所有人的平均分,包含没 参加考试的.
最高成绩 max()
select max(counts) from t_user;
最小年龄 min()
select min(age) from t_user;
班级总成绩 sum()
select sum(counts) from t_user;
查询所有的年龄数
select age from t_user;
### 去重复
select distinct age from t_user;
3.6.5排序
格式:select.... order by 排序字段名1 asc|desc, 字段2 asc|desc, ....;
asc:表示升序,默认值,可以不写
desc:表示降序
select age,money from person order by money desc,age asc;
*指先使用money进行降序的排列,如果数据重复,在按照age排序
10 18
10 20
10 23
9 20
3.6.6分组
#1添加班级字段(classes)
alter table t_user add column classes varchar(3);
update t_user set classes = ‘1‘ where id in (‘u001‘,‘u002‘,‘u003‘,‘u004‘);
update t_user set classes = ‘2‘ where id in (‘u005‘,‘u006‘,‘u007‘);
#2查询1班和2班的平均成绩
#2.1 查询所有平均成绩
select sum(counts)/count(*) from t_user;
#2.2 使用班级进行分组
### 分组格式:select .... group by 分组字段 having 分组条件;
select classes,sum(counts)/count(*) from t_user group by classes;
注意:如果使用分组,select查询字段处,只能使用“聚合函数”或“分组字段”
select id,classes,sum(counts)/count(*) from t_user group by classes; #此处id没有意义
#3查询班级平均成绩不及格的班级成员信息
3.1 查询班级平均成绩不及格(包含没参加考试的)
select classes as 班级, sum(counts)/count(*) as 平均分 from t_user group by 班级 having 平均分<60;
3.2 2班的班级成员信息
select * from t_user where classes = ‘2‘;
3.3 整合
新建表二:
create table t_user2(
name varchar(20) not null,
classes varchar(16) not null
);
再insert to数据
insert into t_user2 (name, classes) values
(‘作者‘, ‘1‘),
(‘小明‘, ‘2‘);
最终得到
### 多表操作 , 表名 [as] 别名
select * from A,B where A.classes = B.classes;
### 表的别名
select a.* from A as a,B as b where a.classes = b.classes;
例如
select * from t_user as A , t_user2 as B where A.classes=B.classes;
select A.* from t_user as A , t_user2 as B where A.classes=B.classes;
//应该是笛卡尔(直积)全部串起来 ,然后再select 元素的排列.
列举出那些班级平均分<60的所有学生
思路:将列表一 和 结果select修饰过后的表二拼起来即可.
法一:
select A.* from t_user as A, (select classes, sum(counts)/count(*) as avgcount
from t_user
group by classes
having avgcount < 60) as B
where A.classes = B.classes;
法二: 暂时没想到.不写了
4 cmd中文乱码处理
1,2,4 client/connection/results 此三项必须保持一致,且与事实符合
client:在服务器端设置的客户端的编码
connection:在服务器端设置的客户端与服务器的连接编码
results:在服务器端设置的服务器响应给客户端数据的编码
mysql 服务器端环境变量:character_set_database,设置数据库的编码,如果不出现乱码,设置的字符集支持中文即可,GBK或UTF-8或GB2312等
修改服务器端设置:
mysql> set names GBK:
5数据库备份与还原
备份
格式:mysqldump -u账号 -p密码 数据库名称 > 位置
cmd# mysqldump -uroot -p1234 day14 > d:/day14.sql
还原
格式: mysql -u账号 -p密码 数据库名称 < 位置
cmd# mysql -uroot -p1234 day14_bak < d:/day14.sql
注意:必须手动先创建数据库
6修改root账号的密码
手动启动mysql服务器,且进行无权限验证方式的启动
cmd> mysqld --console --skip-grant-tables
使用root账号登陆,不需要密码
cmd> mysql -uroot
修改账号的密码,mysql数据库,user表中
update user set password = password(‘1234‘) where host = ‘%‘;
#需要使用 password() 函数对密码进行加密
删除用户
语法: DROP USER ‘账号’@’host’
例如: mysql> drop user ‘root‘@‘%‘;
密码不正确
7约束
7.1主键约束: 被约束的字段的内容,非空不重复
关键字:primary key
一个表中只能由一个主键
但一个主键不表示只是一个字段,可以是联合主键
使用
方式1 : 声明字段时,同时声明主键
方式2 : 声明字段之后,在约束区域声明主键
constraint primary key (id)
方式2 可以声明 联合主键:多个字段组合在一起,是一个主键,内容合并在一起不重复。
方式3 : 在创建表之后,修改表结构,声明主键
alter table pk03 add [constraint] primary key (id);
7.2唯一约束(被约束的字段的内容,不能重复)
方式1 : 声明字段时,同时声明unique约束.
方式2 : 声明字段之后,在约束区域声明主键
constraint unique (id)
方式3 : 在创建表之后,修改表结构,声明主键
alter table pk03 add [constraint] unique (id);
7.3非空约束(被约束的字段的内容,不能为null)
关键字:not null
使用:在声明字段时,声明非空
7.4 Mysql特有字段:自动增长列
关键字:auto_increment
自动增长列:被约束字段的内容,可以自动累加, 所以录入数据时,可以不操作自动增长列
删除
delete from ai01; #清空表中的数据,但不重置“自动增长列”的累加数
truncate table ai01;#清空表中的数据,但重置“自动增长列”的累加数 (先删除表,再创建表)
7.5 外键约束
关键字:foreign key
外键:
1 在从表添加一个字段
2 类型:必须与主表主键的一致
名称:自定义,建议:user_id 或 uid
内容:从表外键的内容,必须是主表主键的引用
特点:
从表的外键不能添加(更新),主表主键不存在的内容
主表的主键不能删除(更新),从表外键已经引用的内容
//可以拿学籍注册举例
8.1一对多
实例:
国家(1) -- (*)城市
教室(1) -- (*)学生
用户(1) -- (*)书籍
需要使用主外键关系描述:一对多
#添加的格式:alter table 从表表名 add constraint [外键名称] foreign key (从表外键) references 主表表名 (主表主键);
#删除的格式:alter table 表名 drop foreign key 外键名称;
//所以最好以后每个key都要自己起名字.
通过案例学习
# 主表:user表
create table t_user(
id varchar(32),
username varchar(50),
password varchar(32) #MD5加密
);
### 主表的主键
alter table t_user add constraint primary key (id);
# 从表:book表
create table t_book(
id varchar(32),
title varchar(50),
author varchar(50),
user_id varchar(32) #外键,类型t_user.id一致
);
#添加外键
alter table t_book add constraint foreign key (user_id) references t_user (id);
#插入数据
insert into t_user(id,username,password) values(‘u001‘,‘jack‘,‘1234‘);
insert into t_user(id,username,password) values(‘u002‘,‘rose‘,‘1234‘);
insert into t_user(id,username,password) values(‘u003‘,‘tom‘,‘1234‘);
insert into t_book(id,title,author,user_id) values(‘b001‘,‘javaweb‘,‘任童‘,‘u001‘);
#成功,外键是主键引用
insert into t_book(id,title,author) values(‘b002‘,‘javaweb2‘,‘任童2‘);
#成功,user_id = null,外键可以为null,而且我发现可以插入多个null. 这不太好吧.
insert into t_book(id,title,author,user_id) values(‘b003‘,‘android‘,‘小华华‘,‘u001‘);
insert into t_book(id,title,author,user_id) values(‘b004‘,‘菊花是怎么成长的‘,‘强哥‘,‘u002‘);
#1 笛卡尔积,两个表的乘积集合
select * from t_user,t_book;
select count(*) from t_user,t_book; #12条
#2 隐式内连接
## 例如:查询某人借某了某书
select * from t_user,t_book where t_user.id = t_book.user_id;
select t_user.username,t_book.title from t_user,t_book where t_user.id = t_book.user_id;
#可以起表的别名哦.
下面这句话用隐式内连接实现(借阅人和借阅数据的列表)
select U.username, B.title from t_user as U , t_book as B
where U.id = B.user_id;
#3 内连接
## 格式:select ... from A inner join B on 条件
select U.username, B.title from t_user as U
inner join t_book as B
on U.id = B.user_id;
和隐式内连接一样
#4 外连接
# 左外连接:查询A表的所有内容,B表的内容是否显示,取决条件是否成立,成立显示,不成立显示null
### 格式:select ... from A left outer join B on 条件
### 例如:统计用户借阅书籍情况
select * from t_user u
left outer join t_book b on u.id = b.user_id;
# 右外连接:查询B表的所有内容,A表的内容是否显示,取决条件是否成立,成立显示,不成立显示null
### 格式:select ... from A right outer join B on 条件
### 例如:统计书籍被解决情况
select * from t_user u
right outer join t_book b on u.id = b.user_id;
8.2多对多
实例:
教师(*) -- (*)学生
人(*) -- (*)角色
角色(*) -- (*)权限
学生(*) -- (*)课程【】
##多对多 (many)
## 主表:学生表
create table m_student(
id varchar(32) primary key, #主键
`name` varchar(50),
age int
);
## 主表:课程表
create table m_course(
id varchar(32) primary key, #主键
content varchar(50),
teacher varchar(50)
);
## 从表:中间表,学生课程表
create table m_student_course(
student_id varchar(32), #学生表对应外键
course_id varchar(32) #课程表对应外键
);
##### 关系
### 中间表与学生表 :主外键关系
alter table m_student_course add constraint foreign key (student_id) references m_student (id);
### 中间表与课程表 :主外键关系
alter table m_student_course add constraint foreign key (course_id) references m_course (id);
### 联合主键
alter table m_student_course add constraint primary key (student_id,course_id);
### 外键删除
alter table 表名 drop foreign key 外键名称;
### 测试:
insert into m_student(id,name,age) values(‘s001‘,‘jack‘,18); #成功
insert into m_course(id,content,teacher) values(‘c001‘,‘java基础‘,‘自摸‘); #成功
insert into m_student_course(student_id,course_id) values(‘s001‘,‘c001‘);#成功,多对多关系通过中间数据维护
insert into m_student(id,name,age) values(‘s002‘,‘rose‘,21);
insert into m_course(id,content,teacher) values(‘c002‘,‘java web‘,‘梁少‘);
insert into m_course(id,content,teacher) values(‘c003‘,‘android‘,‘侃哥‘);
insert into m_student_course(student_id,course_id) values(‘s001‘,‘c002‘);
insert into m_student_course(student_id,course_id) values(‘s001‘,‘c003‘);
insert into m_student_course(student_id,course_id) values(‘s002‘,‘c002‘);
insert into m_student_course(student_id,course_id) values(‘s002‘,‘c003‘);
### 查询:某人学某课
##隐式内用多对多看来是高级连接操作吧
select s.name , c.content from m_student s , m_student_course sc , m_course c
where s.id = sc.student_id and sc.course_id = c.id;
##内连接,三个的内连接也是高级的内连接吧
select s.name ,c.content from m_student s
inner join m_student_course sc on s.id = sc.student_id
inner join m_course c on sc.course_id = c.id;
8.3一对一 (不讲)
SQL注入
select * from t_user where username = ‘jack‘ or 1=1 or 1=‘‘ and password = ‘12345678‘
select * from t_user where username = ‘\‘‘ and password = ‘‘
# 手动防止sql注入,将所有的单引号替换成 "\‘"
2预处理对象
2.1介绍
接口:javax.sql.PreparedStatement 预处理对象,预先处理sql语句,使用户的输入的内容,只是执行的参数,而不是sql语句语法的一部分。
2.2编写流程
1 提供一个已经处理过的sql语句,让预处理对象进行编译。 -- 将实际参数使用?替换
String sql = "select * from t_user where username = ? and password= ? ";
2 使用已经处理过的sql,获得预处理对象
PreparedStatement psmt = conn.prepareStatement(sql); //此处提供sql语句
3 设置实际参数
psmt.setXxx(1,"jack"); //给第一个?设置实际参数
psmt.setString(2,"1234"); //给第二个?设置实际参数
4 执行sql语句
int r = psmt.executeUpdate(); //注意:此处不提供sql语句
ResultSet rs = psmt.exeucteQuery();
2.3应用场景
①防sql注入:
sql注入:用户输入的内容,是sql语句语法的一部分
②大数据:blob字节大对象、clob 字符大对象
数据库可以保存:图片、视频、小说等
注意:图片、视频等不会保存数据库,如果需要图片,则完成文件上传,将图片等保存服务器端,将服务器端保存的路径,保存数据库中。
③批处理 : 批量的处理sql语句
Statement 批处理 : 一次性可以执行多条sql语句,需要编译多次。
* 应用场景:系统初始化 (创建表,创建数据等)
* api
添加sql语句,st.addBatch(sql) --添加sql语句
批量处理sql语句,int[] st.executeBatch()
清除缓存: st.clearBatch();
PreparedStatement 批处理 : 执行一条sql语句,编译一次,执行sql语句的参数不同。
* 应用场景:表数据初始化
* api
添加批量参数:psmt.addBatch() --添加实际参数,执行之前,需要执行psmt.setXxx()设置实际参数
执行批处理:int[] psmt.executeBatch()
清除缓存:pstm.clearBatch();
3事务 transaction
3.1介绍
事务:业务中存在一组操作,要么全部成功,要么全部不成功。
例如:转账
3.2事务特性 ACID
原子性:一个事务不可分割。【整体】
一致性:事务的前后数据一致。【数据】
隔离性:多个事务并发性。【并发】
持久性:事务操作之后不可改变状态。【不可变】--如果事务提交了,将不可改变。
3.3mysql事务的操作
开启事务:start transaction
提交事务:commit
回滚事务:rollback
3.4数据的隔离问题
脏读:一个事务读到另一个事务没有提交的数据。
不可重复读:一个事务读到另一个事务提交的数据。(update 更新)
幻读(虚读):一个事务读到另一个事务提交的数据。(insert 录入)
3.5数据隔离级别
数据的隔离级别,解决不用隔离问题。提供4种隔离级别
read uncommitted ,读未提交:一个事务读到另一个事务没有提交的数据。存在问题:3个
read committed ,读已提交:一个事务读到另一个事务调剂的数据。存在问题2个:不可重复读、幻读。解决问题:脏读
repeatable read,可重复读:一个事务中读到数据重复的。存在问题1个:幻读。解决问题:脏读、不可重复读
serializable ,串行化:单事务。没有问题,解决问题:脏读、不可重复读、幻读。
对比:
* 性能:read uncommitted > read committed > repeatable read > serializable
* 安全:read uncommitted < read committed < repeatable read < serializable
mysql 默认隔离级别:repeatable read
Oracle默认隔离级别:read committed
Connection提供常量:
TRANSACTION_READ_COMMITTED,指示不可以发生脏读的常量;不可重复读和虚读可以发生。
TRANSACTION_READ_UNCOMMITTED ,指示可以发生脏读 (dirty read)、不可重复读和虚读 (phantom read) 的常量。
TRANSACTION_REPEATABLE_READ ,指示不可以发生脏读和不可重复读的常量;虚读可以发生。
TRANSACTION_SERIALIZABLE,指示不可以发生脏读、不可重复读和虚读的常量。
3.6隔离级别演示
3.6.1准备工作
查询数据库隔离级别:mysql> show variables like ‘%iso%‘;
mysql 系统环境变量查询:mysql> select @@tx_isolation;
设置数据库的隔离级别:
mysql> set session transaction isolation level 隔离级别
3.6.2读未提交
A 隔离级别:读未提交 read uncommitted
AB开始事务
A查询
B更新
A再查询 -- 查询到,A读到B没有提交的数据
B 回滚
A再查询 -- B更新之前的数据。
3.6.3读已提交
A 隔离级别:读已提交 read committed
AB开启事务
A查询
B更新
A再查询 -- 没有查询到,解决:脏读
B提交
A再查询 -- 查询到,存在问题:不可重复读
3.6.4可重复读
A隔离级别:可重复读 repeatable read
AB开启事务
A查询
B更新
A再查询 -- 没有查询到,解决:脏读
B提交
A再查询 -- 没有查询到,解决:不可重复读
A提交或回滚
A 再查询 -- 查询到,新事物
3.6.5串行化
A隔离级别:串行化 serializable
AB开启事务
A查询
B更新 -- 等待 (A提交或回滚B继续操作;B等待超时)
3.7lost update 丢失更新
丢失更新:两个事务同时操作,A事务更新的数据,被B事务更新的数据覆盖了,导致A事务更新的数据丢失
解决
* 乐观锁:丢失更新肯定不会发生。提供版本字段,进行数据的有效操作。
* 悲观锁:丢失更新肯定会发生。采用mysql数据库的锁机制
* 读锁:共享锁,一个表可以存在多个读锁
mysql> select .... lock in share mode;
* 写锁:排他锁,一个表只能由一个写锁。(独占)
mysql> select .... for update;
注意:mysql的锁必须在事务中
所有的update语句都使用写锁
4事务案例
4.1JDBC事务
一个连接Connection 就表示一个事务
api
conn.setAutoCommit(false); //开启事务 -- 将自动提交关闭了
conn.commit(); //提交事务
conn.rollback(); //回滚事务
mysql事务默认情况自动提交
4.2JDBC事务管理--service层()
4.2.1模板1:(必须学会)
Connection conn = null;
try{
//0 获得连接
conn = ....;
//1 开启事务
conn.setAutoCommit(false);
A步骤
B步骤
C步骤
D步骤
//2 在不报错的情况下, 提交事务
conn.commit();
} catch(e){
//3 回滚事务
conn.rollback();
} finally{
//释放资源
conn.close();
}
4.2.2模板2:
# AB是必选项(A和B是一个整体),CD可选项(C和D一个整体)。
#需要通过Savepoint 保存点进行部分内容管理
Connection conn = null;
Savepoint savepoint = null;
try{
//0 获得连接
conn = ....;
//1 开启事务
conn.setAutoCommit(false);
A步骤
B步骤
// 设置一个保持点
savepoint = conn.setSavepoint();
C步骤
D步骤
//2 提交事务
conn.commit();
} catch(e){
if(savepoint == null){
//AB 其中之一存在异常,回滚AB选项
conn.rollback();
} else {
//CD存在异常
// * 将CD进行回滚
conn.rollback(savepoint); //回滚到保存点之前,也就是AB之后
// * 提交AB
conn.commit();
}
} finally{
//释放资源
conn.close();
}
4.3案例:转账 transfer,详见案例1,2.
5连接池
java.lang中类 ThreadLocal<T>, 详见案例3.