一、事务(Transaction):其实是一组操作(包含许多个单一的逻辑)。只要有一个逻辑没有执行成功,那么都算失败。 所有的数据都回归到最初的状态(回滚)
例如:银行转账
A用户向B用户转账,这样的话转账中就有两个逻辑操作,1、A用户账户金额要减少 2、B用户账户金额要增加,此时就需要把这两个逻辑操作放入事务中
代码格式:
@Testpublic voidtestTransaction(){
Connection conn= null;
PreparedStatement ps= null;
ResultSet rs= null;try{
conn=JDBCUtil.getConn();//连接,事务默认就是自动提交的。 关闭自动提交。
conn.setAutoCommit(false);
String sql= "update account set money = money - ? where id = ?";
ps=conn.prepareStatement(sql);//扣钱, 扣ID为1 的100块钱
ps.setInt(1, 100);
ps.setInt(2, 1);
ps.executeUpdate();//加钱, 给ID为2 加100块钱
ps.setInt(1, -100);
ps.setInt(2, 2);
ps.executeUpdate();//成功: 提交事务。
conn.commit();
}catch(SQLException e) {try{//事变: 回滚事务
conn.rollback();
}catch(SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally{
JDBCUtil.release(conn, ps, rs);
}
}
二、事务的特性
①、原子性:事务中包含的逻辑,不可分割。
②、一致性:事务执行前后。数据完整性
③、隔离性:事务在执行期间不应该受到其他事务的影响
④、持久性:事务执行成功,那么数据应该持久保存到磁盘上。
三、隔离级别
①、读未提交(read uncommitted)
引发问题: 脏读
②、读已提交(read committed)
解决: 脏读 , 引发: 不可重复读,SQL Server没有这个问题
③、可重复读(repeatable-read)
解决: 脏读 、 不可重复读 , 引发: 幻读
④、可串行化(SERIALIZABLE)
解决: 脏读、 不可重复读 、 幻读
mySql 默认的隔离级别是 可重复读
Oracle 默认的隔离级别是 读已提交
Sql Server 默认的隔离级别是 读已提交
四、事务的安全隐患
不考虑隔离级别设置,那么会出现以下问题
①、读
脏读:一个事务读到了另一个事务未提交的数据
事务A:
USEBankGO
--使用默认隔离级别
BEGIN TRANSACTION
UPDATE ACCOUNT SET Money=Money-300 WHERE Id=1
WAITFOR DELAY '00:00:10'
ROLLBACK TRANSACTION
事务B:
USEBankGO
--设置隔离级别:未提交读
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
BEGIN TRANSACTION
--发生在事务回滚前
SELECT * FROM ACCOUNT WHERE Id=1
WAITFOR DELAY '00:00:10'
--发生在事务回滚后
SELECT * FROM ACCOUNT WHERE Id=1
COMMIT TRANSACTION
先执行事务A再执行事务B结果:
不可重复读:一个事务读到了另一个事务已提交的数据,造成前后两次查询结果不一致
事务A:
USEBankGO
--使用默认隔离级别
BEGIN TRANSACTION
UPDATE ACCOUNT SET Money=Money-300 WHERE Id=1
WAITFOR DELAY '00:00:10'
COMMIT TRANSACTION --注意
事务B:
USEBankGO
--设置隔离级别:读已提交
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
BEGIN TRANSACTION
--发生在事务回滚前
SELECT * FROM ACCOUNT WHERE Id=1
WAITFOR DELAY '00:00:10'
--发生在事务回滚后
SELECT * FROM ACCOUNT WHERE Id=1
COMMIT TRANSACTION
先执行事务B再执行事务A结果:
幻读:一个事务读到了另一个事务insert的数据 ,造成前后查询结果不一致
事务A
USE Bank
GO
--使用默认隔离级别
BEGIN TRANSACTION
INSERT INTO ACCOUNT VALUES('Kimi',1000)
COMMIT TRANSACTION
事务B
USEBankGO
--设置隔离级别:读已提交
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRANSACTION
--发生在事务提交前
SELECT * FROMACCOUNTWAITFOR DELAY '00:00:10'
--发生在事务提交后
SELECT * FROMACCOUNTCOMMIT TRANSACTION
先执行事务B再执行事务A结果不一致,为幻读:
②、写
丢失更新
数据丢失更新可以通过锁机制,来加以控制
五、总结
脏读
更新丢失
不可重复读
幻读
Read Uncommitted
可能
可能
可能
可能
Read Committed
不可能
可能
可能
可能
Repeatable Read
不可能
不可能
不可能
可能
Serializable
不可能
不可能
不可能
不可能