首页 > 代码库 > 00 MySQL

00 MySQL

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 分组),havingorder 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.Stringchar表示固定字符串,n表示字符个数。
例如:char(5) , 当前字段必须写5个字符.
如果录入的数据”abc”,右侧自动添加空格,结果“abc空空”
varchar(n)java.lang.Stringvarchar表示可变字符串,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.

00 MySQL