2015年11月6日 星期五

悲觀鎖定 (Hibernate3.x 十二)

悲觀鎖定認為在每次拿資料時,一定會有人修改,所以它的機制是當有人在操作資料庫時,必需等待對方操作完成,才能換下一個,也就是同一個時間只能有一個人操作,優點是絕對安全,缺點是只適合小系統或很少人使用的情況
例如去ATM領錢,此ATM是全台灣的系統,那就變成在台灣想領錢的人要等很久,如果是全世界的系統,那就等的更久,但如果只是10幾個人在用的系統,那就還ok

悲觀鎖定總共有以下11種模式:
NONE:預設值,無鎖定模式

READ:查詢時自動進行鎖定處理

UPGRADE:已廢棄-->使用PESSIMISTIC_WRITE代替

UPGRADE_NOWAIT:和UPGRADE相同,但有人鎖定就不等待返回,等同Oracle的 for update nowait

WRITE:在update、insert時自動進行鎖定處理

FORCE:已廢棄-->使用PESSIMISTIC_FORCE_INCREMENT代替

OPTIMISTIC:樂觀的假設Transaction不會出現爭用的實體

OPTIMISTIC_FORCE_INCREMENT:Transaction 立刻增加實體版本

PESSIMISTIC_READ:Implemented as PESSIMISTIC_WRITE

PESSIMISTIC_WRITE:transaction 將立即獲得一個資料庫鎖定,等同Oracle 的 for update

PESSIMISTIC_FORCE_INCREMENT:類似UPGRADE,但使用了版本記錄會強制增加版本的值

※版本在樂觀鎖定會說明
Hibernate API的說明可參考,但還不是很清楚,可看原始碼的註解,如下:

/**
 * No lock required. If an object is requested with this lock
 * mode, a <tt>READ</tt> lock will be obtained if it is
 * necessary to actually read the state from the database,
 * rather than pull it from a cache.<br>
 * <br>
 * This is the "default" lock mode.
 */
public static final LockMode NONE = new LockMode(0, "NONE");
    
/**
 * A shared lock. Objects in this lock mode were read from
 * the database in the current transaction, rather than being
 * pulled from a cache.
 */
public static final LockMode READ = new LockMode(5, "READ");
    
/**
 * An upgrade lock. Objects loaded in this lock mode are
 * materialized using an SQL <tt>select ... for update</tt>.
 * @deprecated instead use PESSIMISTIC_WRITE
 */
public static final LockMode UPGRADE = new LockMode(10, "UPGRADE");
    
/**
 * Attempt to obtain an upgrade lock, using an Oracle-style
 * <tt>select for update nowait</tt>. The semantics of
 * this lock mode, once obtained, are the same as
 * <tt>UPGRADE</tt>.
 */
public static final LockMode UPGRADE_NOWAIT = new LockMode(10, "UPGRADE_NOWAIT");
    
/**
 * A <tt>WRITE</tt> lock is obtained when an object is updated
 * or inserted.   This lock mode is for internal use only and is
 * not a valid mode for <tt>load()</tt> or <tt>lock()</tt> (both
 * of which throw exceptions if WRITE is specified).
 */
public static final LockMode WRITE = new LockMode(10, "WRITE");
    
/**
 * Similiar to {@link #UPGRADE} except that, for versioned entities,
 * it results in a forced version increment.
 * @deprecated instead use PESSIMISTIC_FORCE_INCREMENT
 */
public static final LockMode FORCE = new LockMode( 15, "FORCE" );
    
/**
 *  start of javax.persistence.LockModeType equivalent modes
 */
    
/**
 * Optimisticly assume that transaction will not experience contention for
 * entities.  The entity version will be verified near the transaction end.  
 */
public static final LockMode OPTIMISTIC = new LockMode( 3, "OPTIMISTIC");
    
/**
 * Optimisticly assume that transaction will not experience contention for entities.
 * The entity version will be verified and incremented near the transaction end. 
 */
public static final LockMode OPTIMISTIC_FORCE_INCREMENT = new LockMode( 4, "OPTIMISTIC_FORCE_INCREMENT");
    
/**
 * Implemented as PESSIMISTIC_WRITE.
 * TODO:  introduce separate support for PESSIMISTIC_READ
 */
public static final LockMode PESSIMISTIC_READ = new LockMode( 12, "PESSIMISTIC_READ");
    
/**
 * Transaction will obtain a database lock immediately.
 * TODO:  add PESSIMISTIC_WRITE_NOWAIT
 */
public static final LockMode PESSIMISTIC_WRITE = new LockMode( 13, "PESSIMISTIC_WRITE");
    
/**
 * Transaction will immediately increment the entity version.
 */
public static final LockMode PESSIMISTIC_FORCE_INCREMENT = new LockMode( 17, "PESSIMISTIC_FORCE_INCREMENT");

可以使用鎖定模式的有以下幾個地方(有可能還有其他地方):
Session.lock-->已廢棄
Session.refresh-->已廢棄
Session.get/load-->已廢棄
Session.buildLockRequest.lock(LockOptions)
Query.setLockMode(LockMode)
Criteria.setLockMode(LockMode)
以下是使用的情況:
Session s = HibernateUtil2.getSession();
    
try {
    Emp e1 = (Emp) s.get(Emp.class, 7369, LockMode.UPGRADE_NOWAIT);
    System.out.println("e1:" + e1.getEmpno());
    
    Emp e2 = (Emp) s.get(Emp.class, 7369);
    LockOptions l = new LockOptions().setLockMode(LockMode.UPGRADE_NOWAIT);
    s.buildLockRequest(l).lock(e2);
    
    Emp e3 = (Emp) s.get(Emp.class, 7369);
    s.buildLockRequest(LockOptions.UPGRADE).lock(e3);
    
    Query q = s.createQuery("from Emp e where empno = 7369");
    q.setLockMode("e", LockMode.UPGRADE_NOWAIT);
    Emp e4 = (Emp) q.uniqueResult();
    System.out.println("e4:" + e4.getEname());
    
    Criteria c = s.createCriteria(Emp.class);
    c.setLockMode(LockMode.PESSIMISTIC_WRITE);
    c.add(Restrictions.eq("empno", 7369));
    Emp e5 = (Emp) c.uniqueResult();
    System.out.println("e5:" + e5.getEname());
} catch (Exception e) {
    System.err.println("例外錯誤!");
    e.printStackTrace();
} finally {
    if (s.isOpen()) {
        s.close();
    }
}

※e1的get或load是廢棄方法
※注意控制台的輸出有for update和for update nowait,我是用oracle的,其他資料庫有可能會不太一樣

沒有留言:

張貼留言