1.1事务的特性
ACID
原子性:事务里面的操作单元不可切割,要么全部成功,要么全部失败
一致性:事务执行前后,业务状态和其他业务状态保持一致.
隔离性:一个事务执行的时候最好不要受到其他事务的影响
持久性:一旦事务提交或者回滚.这个状态都要持久化到数据库中
1.2不考虑隔离性会出现的问题
脏读:在一个事务中读取到另一个事务没有提交的数据
不可重复读:在一个事务中,两次查询的结果不一致(针对的update操作) 不可重复读,是指在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。
虚读(幻读):在一个事务中,两次查询的结果不一致(针对的insert操作) 无法演示出来,MySQL已经默认避免了
1.3MYSQL的四种隔离级别
通过设置数据库的隔离级别来避免上面的问题
read uncommitted 读未提交 上面的三个问题都会出现
read committed 读已提交 可以避免脏读的发生 Oracle 默认级别
repeatable read 可重复读 可以避免脏读和不可重复读的发生 MySQL 默认级别
serializable 串行化 可以避免所有的问题
1.4演示
未提交
package com.xawl.demo;
import com.xawl.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
public class MyTest {
public static void main(String[] args) {
//事务:生活中的事务,指的就是比较正式的事情。
//数据库中的事务,值得是一个操作,这个操作里面可能会包含很多个步骤,那么这些步骤,是整体不可再分割的,这些步骤,要么同时成功,要么同时失败。
//比如转账这个操作。
// 张三给李四转钱。
//张三的账户要扣钱 5000 -1000=4000
//异常
//李四的账户要加钱 2000 +1000=3000
//数据库默认的设置,事务自动开启,自动提交
try {
//张三扣钱。
Connection conn1 = JDBCUtils.getConnection();
//数据库默认的设置,事务自动开启,自动提交
//默认 扣钱是一个单独的事务
PreparedStatement statement1 = conn1.prepareStatement("update bank set money=money-1000 where username='zhangsan'");
statement1.executeUpdate();
//模拟异常
int i = 1 / 0;
//李四加钱。
Connection conn2 = JDBCUtils.getConnection();
//数据库默认的设置,事务自动开启,自动提交
//默认,加钱也是一个单独的事务。
PreparedStatement statement2 = conn2.prepareStatement("update bank set money=money+1000 where username='lisi'");
statement2.executeUpdate();
//对于转账,这个业务逻辑,那么减钱和扣钱,必须在同一个事务中。上面默认就是减钱一个事务,扣钱也是一个事务。
//所以我们得这么做,关闭数据库自动提交事务,由我们手动提交事务,那么这么做,我们可以把扣钱操作和加钱操作,放在同一个事务中,如果转账途中遇到异常,我们可以回滚事务。
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}
已提交
package com.xawl.demo;
import com.xawl.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class MyTest2 {
public static void main(String[] args) {
//事务:生活中的事务,指的就是比较正式的事情。
//数据库中的事务,值得是一个操作,这个操作里面可能会包含很多个步骤,那么这些步骤,是整体不可再分割的,这些步骤,要么同时成功,要么同时失败。
//比如转账这个操作。
// 张三给李四转钱。
//张三的账户要扣钱 5000 -1000=4000
//异常
//李四的账户要加钱 2000 +1000=3000
//数据库默认的设置,事务自动开启,自动提交
Connection conn1 = null;
try {
//张三扣钱。
conn1 = JDBCUtils.getConnection();
//设置数据库的隔离级别
// conn1.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
//设置事务不自动提交
//将此连接的自动提交模式设置为给定状态。如果连接处于自动提交模式下,则它的所有 SQL 语句将被执行并作为单个事务提交
//autoCommit - 为 true 表示启用自动提交模式;为 false 表示禁用自动提交模式
conn1.setAutoCommit(false); //set @@autocommit=0;
//数据库默认的设置,事务自动开启,自动提交
//默认 扣钱是一个单独的事务
PreparedStatement statement1 = conn1.prepareStatement("update bank set money=money-1000 where username='zhangsan'");
statement1.executeUpdate();
//模拟异常
int i = 1 / 0;
//数据库默认的设置,事务自动开启,自动提交
//默认,加钱也是一个单独的事务。
PreparedStatement statement2 = conn1.prepareStatement("update bank set money=money+1000 where username='lisi'");
statement2.executeUpdate();
//对于转账,这个业务逻辑,那么减钱和扣钱,必须在同一个事务中。上面默认就是减钱一个事务,扣钱也是一个事务。
//所以我们得这么做,关闭数据库自动提交事务,由我们手动提交事务,那么这么做,我们可以把扣钱操作和加钱操作,放在同一个事务中,如果转账途中遇到异常,我们可以回滚事务。
} catch (Exception e) {
e.printStackTrace();
//遇到任何异常,我们都要回滚事务。
try {
conn1.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
//手动提交事务
try {
conn1.commit(); //手动提交事务
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/*
* set @@autocommit=0; //设置事务手动提交
SHOW VARIABLES like '%autocommit%';
START transaction ; -- 手动开启事务
SELECT * from emp WHERE id='06c4c8b4cc0640f6848696aaa0cb5f2b' for update;
rollback -- 回滚事务
COMMIT; -- 提交事务
* 注意;数据定义语言(DDL)语句不能被回滚
* */
}
1.5补充
演示脏读的发生:
将数据库的隔离级别设置成 读未提交
set session transaction isolation level read uncommitted;
查看数据库的隔离级别
select @@tx_isolation;
演示:
打开两个窗口进行演示:给两个窗口设置好同的隔离级别
开启事务 start transaction;
修改数据:update bank set money=1500 where username='lisi';
让另一个窗口开启事务 查询数据 他查到了 就是脏读
我这边窗口 一回滚(rollback),钱又没过去
避免脏读的发生,将隔离级别设置成 读已提交
set session transaction isolation level read committed;
不可避免不可重复读的发生.
避免不可重复读的发生 经隔离级别设置成 可重复读
set session transaction isolation level repeatable read;
演示串行化 可以避免所有的问题
set session transaction isolation level serializable;
我这边的事务不提交,那边的事务无法执行锁表的操作.
四种隔离级别的效率
read uncommitted>read committed>repeatable read>serializable
四种隔离级别的安全性
read uncommitted<read committed<repeatable read<serializable
开发中绝对不允许脏读发生.
mysql中默认级别:repeatable read
oracle中默认级别:read committed
java中控制隔离级别:
Connection的api
void setTransactionIsolation(int level)
level是常量