1. 什么是事务:
事务中有多个操作,这些操作的执行结果要么完全成功,要么完全失败,不可能出现一部分成功,一部分失败的情况。比如说转账业务:张三给李四转100w,那么有两个操作,一个是张三减去100w,李四加上100w,两个操作都成功,要么都失败,不能出现张三减了100w,李四没有加上100w的情况。
2.事务的四种特性:
原子性:事务中的操作都是不可分割的原子单位,事务中所有的操作要么全部成功,要么全部失败。
一致性:事务执行后,数据库的状态与他的业务规则应该保持不变。比如转账业务,转账前后,两个账户的总额是不变的。
隔离性:隔离是在并发操作中,不同事务之间应该隔离开来,使得每个并发事务都互不干扰。
持久性:一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务,数据库马上崩溃,在数据库重新启动完,数据库中的数据也要保持不变。
3.与事务有关的几个方法:
connection.setAutoCommit(boolean):设置事务是否自动提交,如果设置为true,自动提交,那么每执行一条sql语句,就相当于执行一个单独的事务,就会自动提交,如果设置为false,那就相当于开启了一条事务。
connection.commit():事务的提交。
Connection.rollback():回滚事务。
4.jdbc处理事务的格式:
try {
con.setAutoCommit(false);//开启事务…
….
…
con.commit();//try的最后提交事务
} catch() {
con.rollback();//回滚事务
}
5.事务的简单例子:
以插入数据为例,同时执行插入两条数据,这两条数据要么都成功插入到数据库中,要么都不能成功的插入到数据库中。
publicint insertUser(){
int count = 0;
String sql = "insertinto user values(?,?)";
PreparedStatementprepareStatement = null;
try {
//手动提交,也就是执行了sql语句但是不同步到数据库中,只有两个都正确,才会自动提交到数据库中。
connection.setAutoCommit(false);
prepareStatement =connection.prepareStatement(sql);
prepareStatement.setString(1,"zhang");
prepareStatement.setString(2, "123456");
//插入到数据库张三的数据,张三这个数据是数据库的主键。
prepareStatement.execute();
prepareStatement.setString(1,"zhang");
prepareStatement.setString(2,"123456");
//再次相数据库中插入张三这条数据时,因为张三主键已经存在了,所以执行是不成功的
prepareStatement.execute();
//当第二条数据执行不成功时,就会抛出异常
connection.commit();
} catch (SQLException e) {
//执行不成功回滚,回滚到两条数据都没插入之前的数据库状态。
try {
connection.rollback();
} catch(SQLException e1) {
System.out.println("回滚失败!");
}
throw newRuntimeException(e.getMessage());
}finally{
try {
prepareStatement.close();
} catch(SQLException e) {
e.printStackTrace();
}
try {
connection.close();
} catch(SQLException e) {
// TODOAuto-generated catch block
e.printStackTrace();
}
}
return count;
}
6.事务的隔离级别:
6.1事务的并发问题:
脏读:读到另一个事务未提交的更新数据,即读到了脏数据。
不可重复读:两次读取同一个数据读到的结果不一样。即另一事务修改了事务。
幻读:对同一张表两次读到的数据不一样,应为另一条事务插入了新的记录。
6.2不可重复读和幻读的区别:
不可重复读是读取到了另一事务的更新;
幻读是读取到了另一事务的插入(MySQL中无法测试到幻读)
7.数据库内部定义了四种隔离级别,用于解决三种隔离问题
1 Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)
2 Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)不可以避免虚读
3 Read committed:可避免脏读情况发生(读已提交)
4 Read uncommitted:最低级别,以上情况均无法保证。(读未提交)
操作数据库内部隔离级别
set session transaction isolation level 设置事务隔离级别
select @@tx_isolation 查询当前事务隔离级别
实验一:演示脏读发生
在A窗口将隔离级别设置 read uncommitted
A、B窗口同时开启事务
B窗口执行转账操作update account set money = money - 500 where name='bbb';
update account set money = money + 500 where name ='aaa'; 未提交事务
A窗口查询 select *from account; 查询到转账结果(脏读)
B 回滚 rollback
A窗口查询 金钱丢失
实验二:演示不可重复读
在A窗口设置隔离级别 read committed; 重复实验一,发现无法脏读
B窗口转账后,提交事务
A窗口查询到 B提交的数据 (不可重复读)
实验三:演示阻止不可重复读发生
在A窗口设置隔离级别 repeatable read 重复试验二,发现不会发生不可重复读
实验四:演示serializable 串行事务效果
A窗口设置隔离级别serializable
A、B同时开启事务
B窗口插入一条数据insert into account values(null,'ddd',1000);
在A窗口查询数据 select * from account;
发现A 窗口阻塞了,等待B事务执行结束
安全性:serializable > repeatableread > read committed > read uncommitted
性能 :serializable < repeatable read < read committed < readuncommitted
结论: 实际开发中,通常不会选择 serializable 和 read uncommitted , mysql默认隔离级别repeatable read ,oracle默认隔离级别 read committed
JDBC程序中能否指定事务的隔离级别 ?
Connection接口中定义事务隔离级别四个常量:
static int TRANSACTION_READ_COMMITTED
指示不可以发生脏读的常量;不可重复读和虚读可以发生。
static int TRANSACTION_READ_UNCOMMITTED
指示可以发生脏读 (dirty read)、不可重复读和虚读 (phantom read) 的常量。
static int TRANSACTION_REPEATABLE_READ
指示不可以发生脏读和不可重复读的常量;虚读可以发生。
static int TRANSACTION_SERIALIZABLE
指示不可以发生脏读、不可重复读和虚读的常量。
通过 void setTransactionIsolation(intlevel) 设置数据库隔离级别