一、什么是事务
事务是数据库操作的最小逻辑单元,具有"全有或全无"的特性。以银行转账为例:
典型场景:
-
从A账户扣除1000元
-
向B账户增加1000元
这两个操作必须作为一个整体执行,要么全部成功,要么全部失败
MySQL的事务特性:
-
默认自动提交模式(每条SQL作为独立事务)
-
通过
START TRANSACTION
显式开启事务 -
使用
COMMIT
提交或ROLLBACK
回滚
二、使用事务
MySql数据库中使用事务,提供了两种方式(重点)
1、使用命令的方式
-- 开启事务
START TRANSACTION;
update t_account set money=money-1000 where username='冠希';
update t_account set money=money+1000 where username='美美';
-- 提供事务(事务已经结束了,数据永久的保存到数据库中了)
COMMIT;
-- 回滚事务(事务已经结束了,数据回滚到最初始化的状态)
ROLLBACK;
2、使用不默认提交的方式
show VARIABLES like '%commit%';-- 查看事务是否默认提交
set autocommit =off;-- 设置不默认提交
update t_account set money=money-1000 where username='冠希';
update t_account set money=money+1000 where username='美美';
COMMIT;-- 或者rollback
当会话结束此次关闭不默认提交的的事务就结束,新的会话还是默认提交事务。
3、JDBC中操作事务
JDBC事务验证实例:
package com.qcby.dao;
import com.qcby.utils.JdbcUtils2;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcUpdate {
public static void main(String[] args) {
Connection connection =null;
Statement statement=null;
//增删改不需要结果集
//ResultSet resultSet=null;
try {
//jdbc连接数据库
//1.加载驱动
//2.获取连接(URL 用户名 密码)
connection = JdbcUtils2.getConnection();
//开启事务(关闭自动提交)
connection.setAutoCommit(false);
//3.编写sql
String sql1="UPDATE account SET money = money + 1000 WHERE name = 'ccc'";
String sql2="UPDATE account SET money = money - 1000 WHERE name = 'bbb'";
//4.获取执行sql的stmt的对象
statement = connection.createStatement();
//返回的结果条数
int i=statement.executeUpdate(sql1);
//int m=10/0;
int i2=statement.executeUpdate(sql2);
//注意提交事务
connection.commit();
System.out.println("转账成功!");
} catch (SQLException e) {
//回滚事务(如果发生异常)
try {
if (connection != null) {
connection.rollback();
System.out.println("转账失败,已回滚!");
}
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
//7.关闭资源(先开的后关 后开的先关)
JdbcUtils2.close(connection, statement);
}
}
}
事务的特性:
- 原子性(Atomicity):要么全做,要么全不做
- 一致性(Consistency):钱的总数不变(你-100,商家+100)
- 隔离性(Isolation):你和别人同时买东西不会互相干扰
- 持久性(Durability):一旦成功,数据永久保存
不考虑隔离性将引发的问题:
脏读:读到另一个事务未提交的数据。
不可重复读“变魔术的字”看商品的价格突然变了,别人改的时候提交了,你这也改了。
幻读:“凭空出现的行“就像你数教室里的人数,数到一半又进来几个学生
设置事务的隔离级别(解决读的问题)
1.事务的隔离级别(设置数据库的隔离级别,根据级别的不同,解决上述的读的问题) * Read uncommitted‐‐ 什么都解决不了
* Read committed‐‐ 避免脏读,但是不可重复读和虚读有可能产生
* Repeatable read‐‐ 避免脏读和不可重复读,虚度有可能产生的
* Serializable‐‐ 避免各种读
2.4种隔离级别有安全性和效率
* 安全 Serializable > Repeatable read > Read committed > Read uncommitted
* 效率 Serializable < Repeatable read < Read committed < Read uncommitted
3.数据库都有自己默认的隔离级别
* MySQL的数据库,默认的隔离级别是Repeatable read,避免脏读和不可重复读。
演示脏数据(别人事务还未提交你就看到了)
在mysqlbin目录下开启两个窗口,一个A窗口(左),一个B窗口(右)。
A:
B:
A:
B:
避免脏读和演示不可重复读
A:
B:
A:
B:
A:中再查询一次
两次查询结果不同,发生不可重复读
避免不可重复读
两个窗口同时设置隔离级别为REPEATABLE READ
A:
B:
A:
与第一次查询结果相同,避免了不可重复读
B:
A:
避免各种读
A:
B:
没有提交事务
A:
SERIALIZABLE 是最严格的隔离级别:
- 它会在读取数据时自动加共享锁
- 其他事务不能修改这些数据,直到事务结束
- 如果其他事务正在修改这些数据,查询会被阻塞
当B提交或回滚时,事务结束,A就可以查询出结果。