前言
目录
事务~
什么是事务~
事务的原子性~
事务相关的面试题~
脏读~
不可重复读~
幻读~
MySQL中事务的隔离级别~
MySQL中的JDBC编程~
JDBC~
学习使用JDBC编程~
引入依赖~
导入到项目中~
编写代码~
事务诞生的目的就是为了把若干个独立的操作给打包成一个整体~~
案例:
1.你要去银行取钱~
2.取完钱要去和女神约会~
如果你不取钱你就没办法和女神约会~你只有有钱了才能带女神约会~但是如果你取完钱了~女神突然不想和你约会了~那你不是钱就白取了吗~存在银行里吃几分的利息不香嘛~
所以这就相当于是两个独立的操作给打包成一个整体~~
要么就是把两个操作都执行完~
要么就一个操作也不执行~
不需要存在~只执行了第一步~不执行第二步的中间状态~~
这种情况下就可以使用事务~
在SQL中~有的复杂的任务需要多个SQL来执行的时候~也同样需要打包在一起~
前一个SQL是为了给后一个SQL提供支持~如果后一个SQL不执行出问题了~前一个SQL也就失去了意义~~
这样的特点我们就叫做原子性:要么全都执行完,要么一个都不执行,任务不可以被再细分~~
案例:
转账~
A给B转500
账户表(name, balance)
A 1000
B 1000
给A这个账户余额减500
update 账户表 set balance = balance - 500 where name = "A";
给B这个账户余额加500
update 账户表 set balance = balance + 500 where name = "B";
那么在转账过程中,就可能会出现问题~
考虑一个极端情况:
执行完成第一个SQL之后,在执行第二个SQL之前,数据库崩了/程序崩了/机器崩了/机器断电了~
导致这里面的转账转出了一半这样一个中间状态~
显然这种中间状态是不科学的~~
而事务的原子性,就能避免出现这种中间状态~~
事务的原子性到底是咋避免的呢?
要么就全部执行~
要么就一个都不执行~~(其实该执行还是得执行,无法预知失败~当我们出现执行失败之后,由数据库自动执行一些"还原"性的工作,来消除前面的SQL带来的影响~看起来就好像一个都没执行一样~)
像这样还原性的工作我们把它称为"回滚"
你在执行到第二个SQL之前,你又没办法预知这次行动失败~~这个时候,就还是得先执行第一个SQL,执行完之后再执行第二个SQL,你只能试试看~
案例:
假设A给B转了500之后,接下来执行第二个SQL的时候出现了意外,导致无法执行~~
那么接下来数据库就会还原之前操作~~
也就是把之前改动过的数据给还原回去(上个操作是减,现在就是加,上个操作是乘,下个操作就是除)~
数据库是如何知道该还原成哪个值的呢?
数据库会拿个小本子,把执行过得每个操作都给记录下来~
既然都能还原,是不是就可以放心大胆的删除数据库了呢?
不可以~
数据库如果把这里面的所有操作都记得那么详细,也是要消耗大量的时间和空间的~
因此这个记录是必不会保存那么久~
如果你的数据库是经过了一年的时间沉淀出来的数据,但你的这些操作可能只记录了几天的.....
最近的这几天的操作可能也去能给你还原出来,但是要想把这一年中间零零散散的各种操作给它回溯回去,这种事其实就没那么容易了~
如果你想把整个库都还原,这事还是得依赖于备份~~
不要对删库这种行为跃跃欲试~~~no zuo no die why you try~~
备份其实更多的是为了应对一些突发情况,硬盘坏了.....(出现的概率比删库的概率其实更高,就不细讲了)~~
谈谈事务的基本特征~
四个基本特征~
1.原子性:
把这些操作打包成一个整体,要么全都执行要么就一个都不执行~
2.一致性~
在事务执行之前和执行之后,数据库中的数据都是合理合法的~
例如:你转完账之后,不能出现这种账户为负数的情况~或者出现小数的情况
3.持久性~
事务一旦提交之后,数据就被持久性存储起来了~数据就写入硬盘了~
4.隔离性~
隔离性描述的是,事务并发执行的时候,产生的情况!!!
并发编程,是当下最主要的一个编程方式~~
写出来的代码,是并发式执行的~~(通俗点讲就是一心两用~)
但是人脑特别不擅长一心两用,而计算机特别擅长~~
计算机擅长并发,也是迫不得已~~
大家应该都听过"duoheCPU"
单个核心的提升能力已经快达到瓶颈了~~CPU是通过在硅晶圆上进行刻蚀电路(光刻机)制作出来的,很难很难做出来~
为什么难做呢?因为要提高算力,就得让单个的电路模块尽量小~能无限小嘛?显然不可能~
当物体小到一定程度的时候,经典力学就已经失效了,量子力学开始监管~
当我们并发执行多个事务,尤其是这多个事务都在尝试修改/读取同一份数据~
这个时候就同一出现一些问题~
事物的隔离性,就是在解决这样的问题~~
并发执行事务可能带来的问题~~
有一天,我在写代码~~写一个课后作业的代码~~
比如:我在写代码中写了一个student类~~在我写代码的过程中,有一个同学,在后面瞄了我屏幕,看到了我写的代码(看到了我写的代码里面有一个student类,有一些属性....)看完之后,他就走人了~~
然后后面我把代码给改了~~
这个时候就意味着他在这里瞄的一眼,瞄到的并不是一个最终版本的数据.而是一个中间过程的数据~~这里可能最终会被修改成别的值~~
像这样的一个情况就成为"脏读"问题~
事务A在对某个数据进行修改,修改的同时,事务B去读取了这个数据.此时事务B读到的很可能是一个"脏数据"(这个数据是一个临时的结果,而不是最终的结果)
那么如果事务B拿着这个"脏数据"在进行一些后续操作很有可能就会出现问题.
出现脏读问题,原因就是事务和事务之间,没有进行任何的隔离~~如果加上一些约束限制,就可以有效的避免脏读问题~
如何处理脏读:给写操作加锁~
在修改的过程中,别人不能读了(加锁状态)
等修改完了之后,别人才能读(解锁状态)
和同学约好~不用着急窥屏~
我写完的作业会提交到码云上,你来看我码云上的代码~~
码云上就是我修改完毕的代码~~这样,同学读到的数据就不再是中间数据了~
一旦加上了写锁之后,就意味着事务之间的隔离性就高了,并发性就降低了
我在写代码~~朋友在通过我的码云来读取代码~~
我开始写代码,写完之后提交码云,就可以把这个操作视为一个事务
同学在我提交之后开始读代码~
按照刚才针对脏读问题的约束.
给写操作进行了加锁,朋友要等我提交之后才能开始读~
但是在朋友读的过程中我可不可以继续写代码呢
当然可以
所以我可能要对代码进行修改
所以我又开始写代码,提交码云
这个时候在我第二次修改代码到提交之前,朋友读到的是我写的第一个版本的代码,在我修改完毕重新上传之后读到的又是我改的第二个版本的代码~
这样的话就出现了另外的问题:
在一个事务中,包含了多次的读操作,这多次的读操作读出来的结果不一致~
刚才约定的是,写的过程中不能读,没说读的过程中不能写~
这样的问题我们就叫做"不可重复读"
如何解决?
刚才这个问题就好比说:朋友在通过码云读代码~~随便刷新了一下,发现代码变了....(又得重新整理)
所以我们再做一个约定~~之前咱么约定的是,你修改的时候我不能读,现在再约定一下,我读的时候,你也别修改~~
给读操作也加锁了~~
意味着我必须得等朋友把代码读完了,才能进行修改~~
通过给读操作也加锁,就解决了不可重复读的问题~
此时事务的并发性又降低了,隔离性又提高了~~
并发性和隔离性二者不可得兼~~
到底要跑的快还是跑的对?
这件事需要看情况进行取舍了~~
我开始写代码,提交代码~
朋友读代码
此时我想写代码或者修改代码就得等朋友读完代码~
等的这段时间我该干嘛呢?不能干等着把,带薪摸鱼?不可取
所以我就决定去写另外一个代码,再提交码云~
注意:事务虽然在提交隔离性的时候要进行一系列加锁,但是这个锁也不是把整个数据库都给锁定了~
还可以改其他的表,甚至说改这个表的其他的行~~
假设朋友在读我写的代码JavaA,在读的过程中,发现代码数量变了~~
本来只有一个JavaA的,现在有多了一个JavaB~
这种就被称为"幻读"问题
一个事务执行的过程中进行了多次查询,多次查询的结果集不一样了~~(多了一条或者少了一条)
这个操作算是一种特殊的不可重复读~~
解决幻读问题:彻底串行化执行~~
朋友和我说好,他读代码的时候,我就赶紧摸鱼,不要写任何代码了......
这种就是隔离性最高,并发程度最低,数据最可靠,速度最慢~
并发(更快)和隔离(更准)是不可兼得~~
就可以根据实际需要来调整数据哭的隔离级别~~
通过不同的隔离级别,也就控制了事务之间的隔离性,也就控制了并发程度~~
1.read uncommitted:允许读取未提交的数据,并发程度最高,隔离程度最低,会引入脏读+不可重复读+幻读问题~~
2.read committed:只允许读取提交之后的数据,相当于写加锁,并发程度降低了一些,隔离程度提高了一些,解决脏读,会引入不可重复读+幻读~
3.repeatable read:相当于给读和写都加锁,并发程度又降低了,隔离程度又提高了,解决了脏读和不可重复读,会引入幻读
4.serializable:串行化,并发程度最低(串行执行),隔离程度最高,解决了脏读,不可重复读,幻读问题,但执行速度最慢~
这几个隔离级别我们可以通过修改my.ini这个配置文件,来设置当前的隔离级别~
根据实际需求场景,来决定使用哪种隔离级别~~
实际开发程序必要过程~
首先MySQL是一个客户端-服务器结构程序~
黑框框只是一个官方提供的客户端,官方也允许程序猿自己实现MySQL的客户端,并且MySQL提供了一组API(计算机行业中经常见到的术语~Application Programming Interface:应用程序编程接口,MySQL提供了一些方法/类,供程序猿直接使用)来支持程序猿实现这样的客户端~~自己实现的客户端可以根据需要来完成一些更具体的增删改查功能~~
咱们很容易的能够实现一个MySQL客户端
但是并不容易实现MySQL服务器.....MySQL服务器是本体,非常复杂....
数据库又不是只有MySQL~~
还有Oracle,SQL sever,SQLite......
MySQL提供了API
Oracle也提供了API......
MySQL提供的API和Oracle提供的API是不是一样的?
显然这些不同的数据库是出自不同厂商之手~
而且关于数据库API的约定,并没有一个业界统一的标准~~
Java一看,不乐意了~~Java表示我诞生就是为了跨平台(这是Java诞生的初衷)
Java就搞了一个大一统的方案:JDBC~
Java约定了一组API:称为JDBC
这组API里面就包含了一些类和一些方法,通过这些类和方法来实现数据库的基本操作~~
让JDBC和这些不同的数据库对接,再由各个厂商,提供各自的"数据库驱动包(通过驱动包把自己的API转换成JDBC风格的,两者对接在一起)",来和JDBC的API对接~~
程序猿只需要掌握这一套JDBC API就可以操作各种数据库了~~
总结一句话:啥是JDBC,就是一组类和方法~~会了这个,就可以操作所有的数据库~~
创建项目~
1.打开idea: 点击new project
点击之后
设置名字,目录,点击Create
JDBC编程需要用到MySQL的驱动包(驱动包就是把MySQL自身的API给转换成JDBC风格的)
驱动包是MySQL官方提供的~~
下载驱动包(从MySQL官方网站~~但是MySQL被Oracle收购了,Oracle的官方做的不太行.....)
更好的选择可以去maven(Java中构建工具)中央仓库~
每次装个软件(第三方库)都需要去官网上找,就比较麻烦,就可以把这些第三方库给汇总起来,统一放到一个地方,中央仓库来下载~
中央仓库就相当于手机的应用商店~
下载地址:www.mvnrepository.com
搜索MySQL第一个就是
选择版本
因为我都MySQL版本是5系列的,所以jdbc版本要和mysql版本对应~~
如果你MySQL版本是8系列,就得用8系列的jdbc驱动~~
选一个版本点击一下:
点击之后就会进入到这个界面
点击标红的地方就会下载驱动包
下载之后就会得到一个jar这样的文件(就相当于.zip这样的压缩包一样)
jar里面就是一些其他人写好的.class文件~
创建个目录,随便起个名字,例如叫做lib~
把刚才下载的jar文件拷贝到刚才的目录中去~
右键刚才的目录,有一个选项,叫做add as library,点击这个选项才能把这个jar引入到项目中.
此时项目才会从jar里面读取内部的.class~否则,代码就找不到jar中的一些类了
这样就成功了
下一章来如何编写代码~