首页 > 代码库 > 事务隔离机制_悲观锁_乐观锁

事务隔离机制_悲观锁_乐观锁

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

事务隔离机制_悲观锁_乐观锁