5. 事务并发处理
a) 事务的特性:ACID
i.Atomicity 原子性
ii.Consistency 一致性
iii.Isolation 隔离性
iiii.Durability 持久性
b) 事务并发时可能出现的问题
i. 第一类丢失更新(Lost Update)
时间 | 取款事务A | 存款事务B |
T1 | 开始事务 | |
T2 | 开始事务 | |
T3 | 查询账户余额为1000元 | |
T4 | 查询账户余额为1000元 | |
T5 |
汇入100元把余额改为1100元 |
|
T6 | 提交事务 | |
T7 | 取出100元把余额改为900元 | |
T8 | 撤销事务 | |
T9 |
余额恢复为1000元(丢失更新) |
ii.脏读(Dirty Read)
时间 | 取款事务A | 转账事务B |
T1 | 开始事务 | |
T2 | 开始事务 | |
T3 | 查询账户余额为1000元 | |
T4 | 汇入100元把余额改为1100元 | |
T5 |
查询账户余额为1100元(读取脏数据) |
|
T6 | 回滚 | |
T7 | 取款 1100 | |
T8 | 提交事务失败 |
iii.不可重复读(non-repeatable read)
在一个事务中前后两次读取的结果并不致,导致了不可重复读,会导致不一致的分析。
时间 | 取款事务A | 转账事务B |
T1 | 开始事务 | |
T2 | 开始事务 | |
T3 | 查询账户余额为1000元 | |
T4 | 汇入100元把余额改为1100元 | |
T5 | 提交事务 | |
T6 | 查询账户余额为1100元 | |
T7 | 提交事务 |
iiii.第二类丢失更新——不可重复读的特殊情况(second lost update problem)
时间 | 转账事务A | 取款事务B |
T1 | 开始事务 | |
T2 | 开始事务 | |
T3 | 查询账户余额为1000元 | |
T4 | 查询账户余额为1000元 | |
T5 | 取出100元把余额改为900元 | |
T6 | 提交事务 | |
T7 | 汇入100元 | |
T8 | 提交事务 | |
T9 |
把余额改为1100元(丢失更新) |
v.幻读(phantom read)
时间 | 查询学生事务A | 插入新学生事务B |
T1 | 开始事务 | |
T2 | 开始事务 | |
T3 | 查询学生为10人 | |
T4 | 插入一个新学生 | |
T5 | 查询学生为11人 | |
T6 | 提交事务 | |
T7 | 提交事务 |
c) 数据库的事务隔离机制
i. 查看 java.sql.Connection 文档
ii.
(1:read-uncommitted
2:read-committed
4:repeatable read
8:serializable)
1. 只要数据库支持事务,就不可能出现第一类丢失更新
2. read-uncommitted 会出现 dirty read,phantom-read,non-repeatable read 问题
3. read-commited 不会出现 dirty read.因为只有一个事务提交才会读出结果,但仍然会出现 non-repeatable 和 phantom-read
4. repeatable read
d) 设定 hibernate 的事务隔离级别
i. hibernate.cfg.xml 配置文件中进行配置:hiberante.connection.isolation=2
ii.用悲观锁解决 repeatable read 的问题(依赖于数据库的锁)
1.select ... for update
2.load(xx.class,i,LockMode.Upgrade),
a) LockMode.NONE 无锁的机制,Transaction 结束时,切换到此模式
b) LockMode.READ 在查询的时候 hibernate 会自动获取锁
c) LockMode.WRITE insert update hibernate 或自动获取锁
d) 以上三种锁的模式,是 hibernate 内部使用的
e) LockMode.UPGRADE_NOWAIT Oracle 支持的锁的方式
如下:
1 @Test 2 public void testPessimisticLock(){ 3 Session session = sf.getCurrentSession(); 4 session.beginTransaction(); 5 6 Account a = (Account) session.load(Account.class, 1,LockMode.UPGRADE);//一般用 UPGRADE 7 int balance = a.getBalance(); 8 //do some caculations 9 balance -= 10; 10 a.setBalance(balance); 11 12 session.getTransaction().commit(); 13 }
e) Hibernate(JPA)乐观锁定(ReadCommitted)
1 package com.bjsxt.hibernate; 2 3 import javax.persistence.Entity; 4 import javax.persistence.GeneratedValue; 5 import javax.persistence.Id; 6 7 @Entity 8 public class Account { 9 10 private Integer id; 11 12 private int balance; 13 14 @Id 15 @GeneratedValue 16 public Integer getId() { 17 return id; 18 } 19 20 public void setId(Integer id) { 21 this.id = id; 22 } 23 24 public int getBalance() { 25 return balance; 26 } 27 28 public void setBalance(int balance) { 29 this.balance = balance; 30 } 31 32 }
保存:
1 @Test 2 public void testOptimisticLock(){ 3 Session session = sf.openSession(); 4 Session session2 = sf.openSession(); 5 6 session.beginTransaction(); 7 Account a1 = (Account) session.load(Account.class, 2); 8 9 session2.beginTransaction(); 10 Account a2 = (Account) session2.load(Account.class, 2); 11 12 a1.setBalance(900); 13 a2.setBalance(1100); 14 15 session.getTransaction().commit(); 16 System.out.println(a1.getVersion()); 17 18 session2.getTransaction().commit();//第二次提交时会对比 version 字段,如果值改变,则事务处理失败,回滚 19 System.out.println(a2.getVersion()); 20 21 session.close(); 22 session2.close(); 23 }
jar包链接: https://pan.baidu.com/s/1qYHdnbA 密码: p429
悲观锁代码链接: https://pan.baidu.com/s/1o8Llad0 密码: 1x2x
乐观锁代码链接: https://pan.baidu.com/s/1c1DhHtu 密码: ed4p