MySQL学习笔记

了解MySQL

MySQL:关系型数据库管理系统

数据库管理系统包含的最基本的成员:

​ 1:若干个数据库组成

​ 2:管理软件

​ 3:使用人员(DBA,以及普通人员)

数据库:什么是数据库。

​ 所谓的数据库,就是一个存储数据的仓库,数据存储在仓库中,我们就可以通过管理软件提供的方式来对数据进行快速的crud。

目前数据库主要分为两大类:

​ 1:关系型数据库 sql server MySQL Oracle…等

​ 2:非关系型数据库 Redis es…等

如何区分一个数据库是否是关系型还是非关系型,关系型数据库这个名字意味着他跟一些东西有关系,这个东西就是sql规范,而所有的关系型数据库都必须要遵守sql规范。

SQL:结构化查询语句

​ SQL是一个为了实现特殊目的的编程语言,其主要目的就是针对关系型数据库的各种操作,以及数据库中数据的增删改查,SQL语言诞生于1974年,1980年美国的国家标准局数据库委员会将SQL定义成了关系型数据库语言的标准,当美国宣布不久之后,ISO(国际标准化组织)宣布将SQL定义为关系型数据库的国际标准。

SQL主要分为六大类:

​ 1:DQL(数据查询语言,主要是查询数据)

​ 关键字:SELECT(查) WHERE(筛选过滤)

​ 2:DML(数据操作语言,主要是增删改)

​ 关键字:INSERT(增加) UPDATE(修改) DELETE(删除)

​ 3:TCL(事务控制语言)

​ DCL(数据控制语言,权限操作)

​ 关键字:GRANT(赋予用户权限) REVOKE(回收权限)

5:DDL(数据定义语言)

​ 关键字:CREATE(创建) DROP(删除) ALTER(修改)

6:CCL(指针控制语言)

​ Oracle – Oracle(收费) mysql(免费)

# 关于约束分类的一些争议
#	观点1:	约束分为列级约束与表级约束	列级约束:自增,默认值,非空,检查	表级约束:主键,外键,唯一
# 观点2:	约束分为行级与列级					行级约束:自增,默认值,非空,检查	列级约束:主键,外键,唯一
# 观点3: 约束分为行级与表级					行级约束:自增,默认值,非空,检查	表级约束:主键,外键,唯一

day29

MySQL>>>>数据库>>>>表>>>>列>>>>行

MySQL:是一个数据库管理系统。每个MySQL都能有很多数据库。

数据库:就是一个数据仓库,每个数据库都可以有很多表。

表:里面存放的是我们的数据,每个表都有任意个列。

列:用来指定了存放的数据类型,以及数据的长度,每个列可能有任意行。

行:单元格,里面存放了我们需要存储的数据。

如何用Java的目光看待数据库?

将表看成Java的类,表中的列看成类中的属性,每一行数据都对应着Java的对象。

创建用户

create user 'zx' identified by '123456';
drop user 'zx';

登录用户

mysql -uzx -p

查看当前登录用户

select user();

用户添加回收权限

grant create,insert,select, on aaa.* to 'zx';
revoke create,insert,select on aaa.* from 'zx';

用户编辑表需要先进数据库

use aaa;

创建删除数据库

create database aaa;
drop database aaa;

切换数据库

use aaa;

展示所有数据库

show databases;

展示数据库中的表

show tables;

查看表的建表语句

show CREATE table stu;

查看表的信息

desc user

创建表

create table m(id int,name varchar(20));

表里插入数据

insert into m values(1,'张三');    
insert into user (id,name,n) values(2,'张三','1998-08-28 12:09:08');
insert user select * from user;#数据翻倍(一般用于测试大量数据时使用)

表里查看数据

select * from m;
#显示所有的职位,使用distinct可以把重复的数据去重
select distinct job from emp;

表里更新数据

update m set name='bbb' where id=1;
update user set name='沙和尚',hiberdate='2000-09-09' where id=1 and sex='男';
delete from user where id=3 or (id=2 and name='孙悟空' and sex is null) or (id=1 and name='沙和尚' and sex='男');

表里删除数据

delete from m;
delete from user where id=2;
truncate table stu;#直接删除表中的所有数据,不能加条件,效果和delete from user;一样,但是效率更高

delete与truncate的区别

​ 1:delete后面可以跟条件进行删选,而truncate后面不能有任何条件
​ 2:delete操作在日志中有记录,可以使用回滚,而truncate在日志中没有记录,不能使用回滚
​ 3:delete是删除数据而已,而truncate是相当于删除了表,重新建立了一张表,原表还在,truncate效率更高

删除表

drop table user;

赋值与判断相等null

null的使用,判断某个值为空时不可以使用=null 要使用is null  判断不为空为is not null

#null值不能进行计算,所有的值与null 计算结果都为null
#使用ifnull(a,b)如果第一个值为null则使用第二个值,否则使用第一个值
select ename,(sal*15+comm*12) from emp;
select ename,(sal*15+ifnull(comm,0)*12) from emp;

#null默认等于0,但是0默认不等于空
select * from emp where comm!=0 or comm is not null;#结果为comm=0的也满足条件
#null值不能进行计算,所有的值与null 计算结果都为null
select ename,(sal*15+comm*12) from emp;

varchar与char

#varchar     用来表示字符串,字符串在数据库中需要指定长度,而varchar是可变字符串,例如varchar(20)虽然指定了这一列能存储20个字符,但是其占用空间是根据实际占用情况来看的,如果数据只占用了两个字符的话那实际占用空间就是2个字符,当最高占用不能超过20个字符,varchar最高能存储65532字节的数据
#char      用来表示字符串,不可变(例如sex(20),这里指定了sex每一行数据都最多能存储20个字符,但是如果只存放了一个字符,实际占用空间还是20个字符的空间

timestamp与data

data    数据类型,用来表示日期,没有具体到时分秒
timestamp   虽然会以年月日,时分秒来表示,但是本质上是时间戳。

bit

bit      只能存储数字0和1,对应Java布尔

(约束)DDL(数据定义语言)

约束:用来规定以及限制数据的内容。
#1:非空约束(not null)  约束列的值不能为空
#2: 唯一约束(unique)    列的值,在本列之内要唯一
#3:主键约束(primary key)非空+唯一,表示这一列的值既不能是null也不能重复。一般情况下第一列都是主键, auto_increment 自增长,只有主键有自增长。
#4:外键约束              跟父子表有关系,约束了字表的增加和父表的删除。
#自增,一般情况下在mysql中我盟的主键如果是int类型的话,可以使用自增长来让mysql自动维护主键的值。如果主键有自增长,插入时主键写null即可,mysql会自动填入主键。(有争议:当插入失败时主键也会自增,会造成主键下一次添加时会和上一个的主键值相差大于1)
#我盟可以使用default关键字来给列设置默认值,然后在插入数据的时候使用默认值进行插入。

CREATE table user(
id int primary key auto_increment,   #id是主键,自增长
name VARCHAR(20) unique,             #name 唯一约束,要求值不能重复
sex varchar(20) not null default '女',            #sex 要求非空约束,
age int,
check(age between 18 and 25)        #AGE检查约束,值必须在18到25之间,在mysql中无效
);
insert into user values(null,'张三',default,88);
insert into user values(1,'李四','男',88);
insert into user values(null,'hjk','男',88);
select * from user;

外键约束

#外键约束一般外键约束都在字表之内,由字表中的一列指向父类的主键foreign key(class_id) references class(id)
#增加的时候,由于class_id这一列绑定了class表的主键,所以class_id的值必须要在父表中存在,否则无法插入
##如果父表的主键值在字表中出现了,那么是不能直接删除的,需要先删除字表的数据,然后再删除父表。

CREATE table class(
id int PRIMARY Key,
name VARCHAR(20)
);
CREATE TABLE stu(
id int PRIMARY KEY,
name varchar(20),
class_id int,
FOREIGN key(class_id) references class(id)
);
INSERT INTO class VALUES(1,'中级一般');
INSERT into class VALUES(2,'中级二班');
-- INSERT into stu VALUES(1,'张三',3);#3在class中不存在,会出错
INSERT into stu VALUES(1,'张三',2);
SELECT * from class;
SELECT * from stu;
-- DELETE FROM class WHERE class_id=2;#无法删除父类,因为其绑定的stu对象存在
DELETE FROM stu WHERE class_id=2;
DELETE FROM class WHERE id=2;

alter修改

#增加列 number
alter table user add number int;
#修改列的类型
alter table user modify number VARCHAR(20);
#修改列的名称
alter table user change number num VARCHAR(20);#修改列明时要重新修改类型
#删除列
alter table user drop num;
#修改表名
alter table user rename to u;
#删除约束,删除自增长
ALTER TABLE U MODIFY id int;
#添加主键自增长
alter table u modify id int auto_increment;
#删除主键,主键如果有自增长时,需要先删除自增之后才能删除主键。主键在删除后会保留一个非空约束,只有主键有自增长,
alter table u DROP PRIMARY KEY;
#增加主键
alter table u add PRIMARY KEY(id);
#删除非空约束
alter TABLE u MODIFY sex VARCHAR(20);
#增加非空约束
alter TABLE u MODIFY sex VARCHAR(20) not null;
#删除唯一约束
alter table u drop key name;
#增加唯一约束
alter table u add constraint unique(name);
#删除外键约束(要根据约束的名称来删除)
alter table stu DROP foreign key class_id;#要使用外键
alter table stu DROP foreign key stu_ibfk_1;
#添加外键约束
alter TABLE stu add CONSTRAINT stu_class_id foreign key(class_id) REFERENCES class(id);

day30

查询指定行

select * from student limit 1,3;

in

#查询工资不在3000~1000之间的员工的所有信息
select * from emp where !(sal>1000 and sal<3000);
select * from emp where not (sal>1000 and sal<3000);

#使用in关键字进项条件匹配,in中可以写多个值,只要符合里面任意一个值都能匹配成功
select * from emp where sal in(800,1600,1250,2975,2450,2850);
#查询工资不是。。。或者部门不是。。的员工的所有信息
select * from emp where sal not in(5000,2450,1250) or (deptno not in(10,20));

ifnull

#使用ifnull(a,b)如果第一个值为null则使用第二个值,否则使用第一个值
select ename,(sal*15+ifnull(comm,0)*12) as '年薪' from emp;
#年薪为15薪,年底时决定给奖金小于300的员工提升为每人每月奖金500元,求每个人年收入
select ename,sal*15+if(ifnull(comm,0)<300,500,comm)*12 from emp;

升序降序

#按照年收入对员工进行升序排序,排序时order by后面的别名不能加单引号,默认排序是升序asc 降序为desc
select ename,sal*15+if(ifnull(comm,0)<300,500,comm)*12 '年薪' from emp order by 年薪 asc;
select ename,sal*15+if(ifnull(comm,0)<300,500,comm)*12 '年薪' from emp order by 年薪 desc;
#查询员工的所有信息m,按照工资降序排序.第一个值相同,按照第二个列进行排序
select * from emp order by sal desc limit 2,4;
select * from emp order by sal desc,ename asc;

求绝对值

select abs(-456);

取余

select 11%2;
select mod(11,2);

向上取整

select ceiling(55.34);

四舍五入

select round(85.45,1);

截取小数

select truncate(2001.154,0);
select truncate(2001.154,1);
select truncate(2001.154,2);

ascii编码值

select ascii('A');

字符串等

#字符串的长度
select length('rgerthrthgsrtgerthyhj');
#查询名字长度是5的员工emp表
select * from emp where LENGTH(ename)=5;
#字符串拼接
select concat('hello','jaava','hello','zhangsan');
#返回前面字符串在后面字符串中出现的位置,注意从1开始计算,如果没有就返回0
select locate('ja','hello java');
#返回后面字符串在前面字符串中出现的位置,注意从1开始计算,如果没有就返回0
select instr('hello java','ja');
#从左侧进行字符串截取
select left ('hello',2);
#从右侧进行字符串截取,空格也是字符串
select right('hello ',2);
#从中间截取,从第几位开始到截取几位,注意中间没有逗号
select substr('hello java' FROM 2 FOR 1);
select substr('hello java',2 );#从第二个开始截取剩余的全部
#去除字符串的空格
select trim('      oihohosg     edtgrt     dfgr     ') '我的';
select ltrim('      oihohosg     edtgrt     dfgr     ') '我的';#去除左侧空字符串
select rtrim('      oihohosg     edtgrt     dfgr     ') '我的';#去除右侧空字符串
#字符串替换,前面是字符串,中间为需要替换的字符串,后面是替换后的字符串
select replace('      oihohosg     edtgrt     dfgr     ',' ','');
#字符串翻倍
select repeat('又热有麻将房',50);
#字符串反转
select reverse('今天天气不错子');
#字符串替换,通过位置进行替换,从第二个位置开始,一个字符串替换成dfgh
select insert('ABCDEFGHI',2,1,'dfgh');
select insert('ABCDEFGHI',2,0,'dfgh');
#在字符串sdkjfhkjhfwfoiqewirfjioqweoqwr在第一个0后面插入狗生没有了希望
select insert('sdkjfhkjhfwfoiqewirfjioqweoqwr',14,0,'狗生没有了希望');
select insert('sdkjfhkjhfwfoiqewirfjioqweoqwr',locate('o','sdkjfhkjhfwfoiqewirfjioqweoqwr')+1,0,'狗生没有了希望')
select insert('sdkjfhkjhfwfoiqewirfjioqweoqwr',position('o' in 'sdkjfhkjhfwfoiqewirfjioqweoqwr')+1,0,'狗生没有了希望')
#字符串变成小写
lcase(str);

时间

#获得当前时间
select now();
#获得年月日
select curdate();
#获取时分秒
select current_time();
#返回星期的索引,星期六返回7,星期天返回1
select dayofweek(now());
select dayofmonth(now());#这个月过了多少天
select dayofyear(now());
#求出年份
select year(now());
#求出月份
select month(now());
#求出天
select day(now());
#求出时
select hour(now());
#求出分
select minute(now());
#求出秒
select second(now());
select day('2020-09-09');
#求出星期几所对应的英文名称
select dayname(now());
#求出月份的名称
select monthname(now());
#求出季度,春天为1,冬天为4
select quarter(now());
#根据参数决定求年月日时分秒
select extract(day from now());
select extract(hour from now());
select extract(minute from now());
#在时间上加上对应的 年月日时分秒
select date_add(now(),interval 30 year);
#在时间上减去对应的年月日时分秒
select date_sub(now(),interval 30 year);
#看如今距离2000-08-28多久了(前面日期减掉后面的日期)
select datediff(now(),'2000-08-28');
#日期转为天数
select to_days(now());
select DATE_ADD(CURDATE(),INTERVAL - day(CURDATE())+ 1 day);#本月第一天
select DATE_ADD(CURDATE()-day(CURDATE()) + 1,INTERVAL  1 MONTH);#下月第一天
select CURDATE()-10;#把日期变为20211208后减一,若减的数为10,则结果为20211199
select NOW()-1;#秒数减一
select to_days(curdate()-1);#天数减一后,转为天数(前提是减后格式符合日期标准格式)
select DATE_ADD(DATE_ADD(CURDATE(),INTERVAL - day(CURDATE())+ 1 day),INTERVAL  1 MONTH);#下个月的第一天
select to_days(DATE_ADD(CURDATE()-day(CURDATE()) + 1,INTERVAL  1 MONTH))-to_days(CURDATE());

当前数据库名称

select database();

查看当前的编码格式

select charset('多');

加密函数

select md5(1123); 
select password(123);

统计平均最大等

select avg(sal) from emp;
select max(sal) from emp;
select min(sal) from emp;
#求数据的总数
select count(*) from emp;
select sum(sal) from emp; 里面可以有Boolean式,为true时自加一

day33

Switch类似方法

select case when <> then <> else <> end;

select case 1
when 1 then '这是1'
when 2 then '这是2'
when 3 then '这是3'
else '大于三'
end;

select username,IF(is_male=1,'男','女') from user
select username,case is_male when 1 then '男' when 0 then '女' end from user;

SQL执行顺序

#1:from      确定表
#2:on join   确定表(多表联查)
#3:where    筛选行,将不符合要求的数据进行筛选掉
#4:group by  对筛选之后的数据进行分组统计(列的别名在这一步之后才能使用)
#5:having    对分组之后的数据进行筛选
#6:select    确定列
#7:distinct 去除重读数据
#8:order by 对数据排序
#9:limit    分页显示数据

多表查询

#多表查询主要分为一下几种
#1:交叉连接
#2:自然链接
#3:内连接
#4:外连接
#5: 全连接
#6:交并不差
#7:(自连接)
#笛卡尔积:两个表的数据产生了乘积相当于x*y 
#笛卡尔积会让数据变得极为庞大,所以我们编写SQL时候应该尽量避免它
#隐式交叉链接(产生笛卡尔积)
select * from emp,dept
#显式交叉链接(产生笛卡尔积)
select * from emp cross join dept
#自然链接 将两张表中等值的列自动排列到一起,并放在第一列显式,过滤到笛卡尔积显示给我们
select * from emp natural join dept
#隐式内连接,过滤掉笛卡尔积
select * from emp,dept where emp.deptno=dept.deptno
#显式内连接(使用inner join代替,然后使用on来做表关系的连接)
select * from emp inner join dept on emp.deptno=dept.deptno
#内连接问题,虽然避免了笛卡尔积的并且常用,但是不符合条件的数据是无法显示的,这时需要使用到外连接
#左右连接
select * from emp left join dept on emp.deptno=dept.deptno
select * from emp right join dept on emp.deptno=dept.deptno
#全连接
select * from emp full join dept
#交 mysql无法使用
select * from emp 
interval 
select * from emp where deptno=20
#并
select * from emp
union all 
select * from emp where deptno=20
#补 使用下面的结果集对上一句SQL的结果进行补充,不会产生重复数据
select * from emp where deptno=20
union
select * from emp
#差 mysql不存在,使用上面的结果集,减掉下面的结果集,然后对剩余的数据进行补充
select * from emp
minus
select * from emp where deptno=20
#自连接
#求每个员工上级领导姓名
select e.ename,m.ename from emp e left join emp m on e.mgr=m.empno
#求每个部门工资最高的人员的姓名
select emp.ename 姓名,emp.deptno 部门编号 from 
(select max(sal) m,deptno from emp group by deptno) e,emp
where e.m=emp.sal and e.deptno=emp.deptno;

select * from 
(select max(sal) m,deptno from emp group by deptno) e right join emp
on e.m=emp.sal and e.deptno=emp.deptno;
#获取比普通员工最高薪水还要高的经理人的名称
select ename from
(select max(sal) m from emp where empno not in(select mgr from emp)) e,
(select ename,sal from emp where mgr in(select empno from emp)) m
where m.sal > e.m

day35

事务

A表
事务: TCL(事务控制语言) 事务实际上就是指我们要做的事情,那在sql语句中事务就代表
了要执行的sql语句。
张三给李四转账虽然是一件事情,但是对应到我们sql却需要两条,分别是1:修改张三的余额,
2:修改李四的余额,此时如果我们执行第一步之后系统出现问题,导致没有给李四增加上钱,
就会出现严重bug,此时我们就需要想办法将两条sql变成一件事情,这样的话两条sql要成功就
一起成功,要失败就一起失败,这就是为什么需要事务的原因。
在关系型数据库中默认每条sql语句都是一个单独的事务,当前我们也可以取消掉默认,让多条sql
语句变成一个事务。
事务的主要两个关键字:
COMMIT			提交事务,在开启事务的时候只有提交事务数据库才会发生改变。
ROLLBACK		回滚事务,回滚事务可以让数据库恢复到执行sql之前的状态。
			事务被提交后便不可以使用回滚恢复数据
因为在mysql中默认每条sql语句都是单独的事务,所以在mysql中事务默认是自动提交的,在事务
开启的情况下只有提交事务才会对数据库执行更改的操作。
事务的四大特性
1: 原子性	在过去的最小单位,代表了不可进行分割,表示一个事务是一个原子性的整体,不能
对其进行分割,要么一起成功,要么一起失败。
2: 一致性	数据从一个状态转变成了另外一个状态(数据安全性,举例:张三1000元,李四1000
元,那么张三给李四转钱,无论怎么转最后数据库应该还是有2000元)。
3: 隔离性	为了防止多个事务之间,互相干扰。
	1: 读未提交	READ UNCOMMITTED	是指,用户A没有提交的数据,被用户B读取到了(脏读,不可重复读,幻读)。
	2: 读已提交	READ COMMITTED		可以避免脏读,会引发不可重复读。
	3: 可重复读	REPEATABLE READ		可以避免脏读,不可重复读,会引发幻读。(mysql默认的隔离级别)
	4: 串行化		SERIALIZABLE			相当于Java的单线程,在操控表的过程中,其余任何人都不能操作。串行化不会引发任何问题。
	不可重复读:是指用户A在两次读取数据的中间,数据被用户B修改了,导致A两次读取的数据不一致。
	幻读:是指用户A在连续操作数据的过程中,B增加了一条新的数据,影响到了A。	 
4: 持久性	将改变的数据持久化到可掉电设备(硬盘)中

CREATE TABLE u(
id int,
name varchar(20),
money int
);

INSERT into u values(1,'张三','1000');
INSERT into u values(2,'李四','1000');
# 张三给李四转钱,转500
UPDATE u SET MONEY=MONEY-500 WHERE ID=1;
这里出现了异常
UPDATE u SET MONEY=MONEY+500 WHERE ID=2;
# 上面的例子中,由于事务是mysql自动控制的,每条sql都是单独的事务,所以36出现bug不影响
# 35行sql的执行。

# 手动控制事务start transaction
START TRANSACTION;
UPDATE u SET MONEY=MONEY-500 WHERE ID=1;
这里出现了异常
UPDATE u SET MONEY=MONEY+500 WHERE ID=2;
COMMIT;				
ROLLBACK;
SELECT * FROM U

****************************查看事务的隔离级别************************************
SELECT @@TX_ISOLATION
# 设置数据库的隔离级别读未提交,观察脏读
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 
START TRANSACTION
SELECT * FROM U;
COMMIT;
# 设置隔离级别读已提交,解决脏读
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
SELECT * FROM U;
COMMIT;
# 观察不可重复度
START TRANSACTION;
SELECT * FROM U;
SELECT * FROM U;
COMMIT;
# 设置隔离级别可重复读,解决不可重复读
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT * FROM U;
SELECT * FROM U;
COMMIT;
# 观察幻读的出现,表面看起来可重复度,解决了幻读,但是真的解决了么?
START TRANSACTION;
SELECT * FROM U;
#SELECT * FROM U;
UPDATE U SET money=1000;
COMMIT;
# 解决幻读
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
SELECT * FROM U;
#SELECT * FROM U;
UPDATE U SET money=2000;
COMMIT;

B表
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
INSERT INTO U VALUES(3,'王五',50000);
COMMIT;
ROLLBACK;

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; 
START TRANSACTION;
INSERT INTO U VALUES(3,'王五',50000);
ROLLBACK;
SELECT @@TX_ISOLATION;
# 观察不可重复度
START TRANSACTION;
UPDATE U SET MONEY=1 WHERE ID=2;
COMMIT;
# 设置隔离级别可重复读,解决不可重复读
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
UPDATE U SET MONEY=1 WHERE ID=1;
COMMIT;
# 观察幻读
START TRANSACTION;
INSERT INTO U VALUES(3,'王五',50000);
COMMIT;

# 解决幻读
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
SELECT * FROM U;
COMMIT;

for update悲观排它表行锁… select * from emp where id=’’ for update;

lock in share mode;共享锁(s锁,读锁);

笑嘻嘻的,这个人很乐观
小黄,小蓝 共享单车,	
锁:锁的主要目的就是为了保护东西,那数据库的锁主要目的就是为了保护数据,主要是为了防止多用户共同操控数据的时候,出现数据混乱。
乐观锁,悲观锁,共享锁,排它锁,表锁,行锁。
乐观锁:非常乐观的一个人,总是认为在自己操控数据的时间之内,肯定不可能有其他人来操控数据,乐观锁需要我们通过数据库设计来实现,一般我们使用乐观锁的话,
会在数据库之内加入一列叫版本号值是数字,或者是时间戳,然后在每次去修改数据库之前,都先查看当前版本号是多少,在修改的时候需要去对比版本号是否一致,如果
一致则修改成功,否则就是修改失败。

CREATE TABLE S(
ID INT,
NAME VARCHAR(20),
MONEY INT,
VERSION INT DEFAULT 1);

INSERT INTO S VALUES(1,'张三',1000,DEFAULT);
SELECT * FROM S;
# 乐观锁去修改数据时候的操作: 将id是1的人加1000块钱。
# 1: 先查出ID 1的人版本号多少
SELECT VERSION FROM S WHERE ID=1;
# 2:  将id是1的人加1000块钱。
UPDATE S SET MONEY=MONEY+1000,VERSION=VERSION+1 WHERE ID =1 AND VERSION=1;

# 悲观锁在修改数据的时候,总是认为会有人跟我抢车位,那如果对方跟我抢车位,我就要像个办法先占住
# 这个车位,共享锁和排它锁都是悲观锁的实现,数据库对悲观锁提供有关键字。

# 排它锁(X锁,写锁) 假设事务A对数据库对象o加了X锁之后,在此期间任何其他的事务都不能对o加锁。
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 
START TRANSACTION;
SELECT * FROM EMP FOR UPDATE;
insert into emp (empno) values(1000);
COMMIT;

# 共享锁(S锁,读锁)	假如事务A对数据库对象o加了S锁之后,就可以对其进行查询,但是不能更新数据,并
#	且此时,其他的事务可以继续对数据库对象加S锁,但是不能加X锁。
START TRANSACTION;
SELECT * FROM EMP lock in SHARE MODE;
insert into emp (empno) values(2000);
COMMIT;

上一篇:Activity的启动流程三--基于API30分析


下一篇:SpringAOP[2]-@Transaction原理