文章目录
1.什么是事务(银行转账)
所谓的事务其实就是对一件事情的处理,只不过这件事情比较特殊!特殊的地方是实施这件事情的所有步骤要么全部成功,要么全部失败!
例如:银行转账,银行转账这件事情就是一个事务。因为他满足事务的特殊性!
比方说,小明要给小红转账1000,那么他就要有以下两个步骤(每个步骤对应于一条sql语句):
- 给小明的账户减去1000元。(第一条sql)
- 给小红的账户加上1000元。(第二条sql)
如果在第一条SQL语句执行成功后,在执行第二条SQL语句之前,程序被中断了,那么小红的账户没有加上1000元,而小明却减去了1000元。这肯定是不符合常规的!
这个就是所谓的事务,在整个转账操作步骤中,要么全部成功,要么全部失败!不可能存在成功一半的情况!也就是说给小明的账户减去1000元如果成功了,那么给小红的账户加上1000元的操作也必须是成功的;否则给小明减去1000元,以及给小红加上1000元都是失败的!
2.事务的四大特性(ACID)
- 原子性(Atomicity): 事务中所有操作是不可再分割的原子单位。事务中所有操作要么全部执行成功,要么全部执行失败。
- 一致性(Consistency): 事务执行后,数据库状态与其它业务规则保持一致。如转账业务,无论事务执行成功与否,参与转账的两个账号余额之和应该是不变的。
- 隔离性(Isolation): 隔离性是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰。
- 持久性(Durability): 一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证通过某种机制(日志)恢复数据。
3. MySql中事务的演示
账户表
CREATE TABLE account(
id INT PRIMARY KEY,
NAME VARCHAR(30),
balance NUMERIC(10.2)
);
MySql的事务默认是开启的
在默认情况下,MySQL每执行一条SQL语句,都是一个单独的事务。
一个事务中可以包含多个sql语句
如何使用MySql的事务
- 开启事务:start transaction;
- 结束事务:commit(事务成功)或rollback(事务失败)。
一个完整事务的范围:
两种情况:
- start transaction;步骤 commit(事务成功) 事务结束(成功,把数据持久化到数据库中)
- start transaction;步骤 rollback(事务失败) 事务结束(失败,所有的步骤都还原)
事务正常开启和提交
在执行SQL语句之前,先执行strat transaction,这就开启了一个事务(事务的起点),然后可以去执行多条SQL语句,最后各个SQL语句执行成功,然后就可以用commit提交事务。
总结:只有当事务正常开启和提交两个操作都完成时,数据的更改才会被持久化到数据库。事务正常开启但遇到异常执行回滚操作
在执行SQL语句之前,先执行strat transaction,这就开启了一个事务(事务的起点),然后可以去执行多条SQL语句,在执行各条SQL语句的过程中遇到异常,然后就可以用rollback
4. jdbc中的事务演示
在jdbc中处理事务,都是通过Connection完成的!
Connection的三个方法与事务相关:
- setAutoCommit(boolean):设置是否为自动提交事务,如果true(默认值就是true)表示自动提交,也就是每条执行的SQL语句都是一个单独的事务,如果设置false,那么就相当于开启了事务了;con.setAutoCommit(false)表示开启事务!
- commit():提交结束事务;con.commit();表示提交事务
- rollback():回滚结束事务。con.rollback();表示回滚事务
jdbc处理事务的代码格式:
try {
con.setAutoCommit(false);//开启事务…
….
throw Exception();
con.commit();//try的最后提交事务
} catch(Exception e) {
con.rollback();//回滚事务
}
代码演示:
package com.ruandy.jdbc05;
import com.ruandy.utils.JdbcUtils03;
import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TranDemo {
/*
* 小明给小红转1000
* jdbc中的事务是通过哪个对象展开?
* Connection
* 开启事务 connection.setAutoCommit(false);
* 提交事务 connection.commit();
* 回滚事务 connection.rollback();
* */
@Test
public void method(){
Connection connection = null;
PreparedStatement pstmt = null;
String sql = null;
try{
connection = JdbcUtils03.getConnection();
connection.setAutoCommit(false); //开启事务,不让事务自动提交
sql = "UPDATE account SET balance=balance+? WHERE NAME=?";
pstmt = connection.prepareStatement(sql);
pstmt.setDouble(1,-1000);
pstmt.setString(2,"xm");
pstmt.executeUpdate();
//执行之前把pstmt里面的参数,先把他擦除
pstmt.clearParameters();//建议大家好
pstmt.setDouble(1,1000);
pstmt.setString(2,"xh");
pstmt.executeUpdate();
connection.commit();//提交事务 手动提交
}catch (Exception e){ //捕获到了算术异常
try {
connection.rollback();//回滚 结束事务
} catch (SQLException throwables) {
throwables.printStackTrace();
}
//要么打印异常,要么把异常抛出去
// e.printStackTrace();
throw new RuntimeException(e);
}finally{
//释放资源
try{
if(pstmt!=null) pstmt.close();
if(connection!=null) connection.close();
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
}
5.事务的隔离性
事务的隔离性:多个事务的并发需要对事务进行隔离,避免多个事务对数据操作的干扰。
5.1 事务的隔离级别(理解,面试会问)
并发事务问题
- 脏读(dirty read):读到另一个事务的未提交更新数据,即读取到了脏数据;
- 不可重复读(unrepeatable read):对同一记录的两次读取不一致,因为另一事务对该记录做了修改;如何解决?对记录加锁(更新)-
- 幻读(虚读)(phantom read):对同一张表的两次查询不一致,因为另一事务插入了一条记录;
5.2 四大隔离级别
4个等级的事务隔离级别,在相同数据环境下,使用相同的输入,执行相同的工作,根据不同的隔离级别,可以导致不同的结果。不同事务隔离级别能够解决的数据并发问题的能力是不同的。(锁的问题很复杂)
- SERIALIZABLE(串行化)事务执行的时候排队
- 不会出现任何并发问题,因为它是对同一数据的访问是串行的,非并发访问的;
- 性能最差;
- REPEATABLE READ(可重复读)(MySQL)
- 防止脏读和不可重复读,不能处理幻读问题;
- 性能比SERIALIZABLE好
- READ COMMITTED(读已提交数据)(Oracle)
- 防止脏读,没有处理不可重复读,也没有处理幻读;
- 性能比REPEATABLE READ好
- READ UNCOMMITTED(读未提交数据)
- 可能出现任何事务并发问题
- 性能最好
MySQL的默认隔离级别为REPEATABLE READ,这是一个很不错的选择吧!
3.3 MySQL隔离级别的查看和设置
MySQL的默认隔离级别为Repeatable read,可以通过下面语句查看:
select @@tx_isolation;
也可以通过下面语句来设置当前连接的隔离级别:
set transaction isolation level [4选1]
3.4 JDBC设置隔离级别
con. setTransactionIsolation(int level)
参数可选值如下:
- Connection.TRANSACTION_READ_UNCOMMITTED;
- Connection.TRANSACTION_READ_COMMITTED;
- Connection.TRANSACTION_REPEATABLE_READ;
- Connection.TRANSACTION_SERIALIZABLE。