一、事务(Transaction):其实是一组操作(包含许多个单一的逻辑)。只要有一个逻辑没有执行成功,那么都算失败。 所有的数据都回归到最初的状态(回滚)
例如:银行转账
A用户向B用户转账,这样的话转账中就有两个逻辑操作,1、A用户账户金额要减少 2、B用户账户金额要增加,此时就需要把这两个逻辑操作放入事务中
代码格式:
@Test public void testTransaction(){ 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:
USE Bank GO --使用默认隔离级别 BEGIN TRANSACTION UPDATE ACCOUNT SET Money=Money-300 WHERE Id=1 WAITFOR DELAY '00:00:10' ROLLBACK TRANSACTION
事务B:
USE Bank GO --设置隔离级别:未提交读 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:
USE Bank GO --使用默认隔离级别 BEGIN TRANSACTION UPDATE ACCOUNT SET Money=Money-300 WHERE Id=1 WAITFOR DELAY '00:00:10' COMMIT TRANSACTION --注意
事务B:
USE Bank GO --设置隔离级别:读已提交 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
USE Bank GO --设置隔离级别:读已提交 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ BEGIN TRANSACTION --发生在事务提交前 SELECT * FROM ACCOUNT WAITFOR DELAY '00:00:10' --发生在事务提交后 SELECT * FROM ACCOUNT COMMIT TRANSACTION
先执行事务B再执行事务A结果不一致,为幻读:
②、写
丢失更新
数据丢失更新可以通过锁机制,来加以控制
五、总结
脏读 | 更新丢失 | 不可重复读 | 幻读 | |
Read Uncommitted | 可能 | 可能 | 可能 | 可能 |
Read Committed | 不可能 | 可能 | 可能 | 可能 |
Repeatable Read | 不可能 | 不可能 | 不可能 | 可能 |
Serializable | 不可能 | 不可能 | 不可能 | 不可能 |