<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 495,  comments - 11,  trackbacks - 0

    并發(fā)控制

    ?????? 當數(shù)據(jù)庫系統(tǒng)采用Read Committed隔離級別時,會導致不可重復讀取和兩次更新丟失的并發(fā)問題,可以在應用程序中采用鎖機制來避免這類問題的產(chǎn)生。

    ?????? 從應用程序的角度上看,鎖可以分為樂觀鎖和悲觀鎖兩大類。

    悲觀鎖

    ?????? 在多個客戶端可能讀取同一筆數(shù)據(jù)或同時更新一筆數(shù)據(jù)的情況下,必須要有訪問控制的手段,防止同一個數(shù)據(jù)被修改而造成混亂,最簡單的手段就是對數(shù)據(jù)進行鎖定。在自己進行數(shù)據(jù)讀取或更新等動作時,鎖定其他客戶端不能對同一筆數(shù)據(jù)進行任何的動作。

    ?????? 悲觀鎖(Pessimistic Locking),如其名稱所示,悲觀地認定每次資料存取時,其他的客戶端也會存取同一筆數(shù)據(jù),因此將會鎖住該筆數(shù)據(jù),直到自己操作完成后再解除鎖。

    ?????? 悲觀鎖假定任何時刻存取數(shù)據(jù)時,都可能有另一個客戶也正在存取同一筆數(shù)據(jù),因而對數(shù)據(jù)采取了數(shù)據(jù)庫層次的鎖定狀態(tài),在鎖定的時間內(nèi)其他的客戶不能對數(shù)據(jù)進行存取。對于單機或小系統(tǒng)而言,這并不成問題,然而如果是在網(wǎng)絡上的系統(tǒng),同時間會有許多訪問的機器,如果每一次讀取數(shù)據(jù)都造成鎖定,其后繼的存取就必須等待,這將造成效能上的問題,造成后繼使用者的長時間等待。

    ?????? 悲觀鎖通常透過系統(tǒng)或數(shù)據(jù)庫本身的功能來實現(xiàn),依賴系統(tǒng)或數(shù)據(jù)庫本身提供的鎖機制。Hibernate即是如此,可以利用Query或Criteria的setLockMode()方法來設定要鎖定的表或列及其鎖模式,可設定的鎖模式有以下幾個。

    ?????? LockMode.UPGRADE:利用數(shù)據(jù)庫的for update子句進行鎖定。

    ?????? LockMode.UPGRADE_NOWAIT:使用for update nowait子句進行鎖定,在Oracle數(shù)據(jù)庫中使用。

    ?????? 下面來實現(xiàn)一個簡單的例子,測試一下采用悲觀鎖時數(shù)據(jù)庫是如何進行操作的。

    ?????? 首先來完成一個實體對象——User,該對象包含了id,name和age三個屬性,實現(xiàn)的方法如清單14.1所示。

    ?????? 清單14.1??? User對象的實現(xiàn)

    package cn.hxex.hibernate.lock;

    public class User {

    ?????? private String id;

    ?????? private String name;

    ?????? private Integer age;

    ??????

    ?????? // 省略了getter和setter方法

    ?????? ……

    }

    ?????? 接下來就是映射文件的配置,由于該映射文件沒有涉及到任何與其他對象的關聯(lián)配置,所以實現(xiàn)的方法也非常簡單,代碼如清單14.2所示。

    ?????? 清單14.2??? User映射文件的實現(xiàn)

    <?xml version="1.0"?>

    <!DOCTYPE hibernate-mapping PUBLIC

    ?????? "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

    ?????? "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

    <hibernate-mapping package="cn.hxex.hibernate.lock">

    ?????? <class name="User" table="USERINFO">

    ????????????? <id name="id" column="userId">

    ????? ?????????? <generator class="uuid.hex"/>

    ??? ?????? </id>

    ?????????????

    ????????????? <property name="name" column="name" type="java.lang.String"/>

    ????????????? <property name="age" column="age" type="java.lang.Integer"/>

    ?????? </class>

    </hibernate-mapping>

    ?????? 另外一件重要的工作就是Hibernate的配置文件了,在這個配置文件中包含了連接數(shù)據(jù)庫的參數(shù)以及其他一些重要的參數(shù),實現(xiàn)的方法如清單14.3所示。

    ?????? 清單14.3??? Hibernate配置文件的實現(xiàn)

    <?xml version='1.0' encoding='UTF-8'?>

    <!DOCTYPE hibernate-configuration PUBLIC

    ????????? "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

    ????????? "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

    <hibernate-configuration>

    ?????? <session-factory>

    ????????????? <!-- 數(shù)據(jù)庫的URL -->

    ????????????? <!-- property name="hibernate.connection.url">

    ????????????? jdbc:oracle:thin:@192.168.10.121:1521:HiFinance</property-->

    ?????? ?????? <property name="hibernate.connection.url">

    ?????? jdbc:mysql://localhost:3306/lockdb?useUnicode=true&amp;characterEncoding=utf8&amp;autoReconnect=true&amp;autoReconnectForPools=true

    ??????? </property>

    ?????????????

    ????????????? <!-- 數(shù)據(jù)庫的驅動程序 -->

    ????????????? <!-- property name="hibernate.connection.driver_class">

    ????????????? oracle.jdbc.driver.OracleDriver</property-->

    ????????????? <property name="hibernate.connection.driver_class">

    ?????????? com.mysql.jdbc.Driver

    ??????? </property>

    ????????????? <!-- 數(shù)據(jù)庫的用戶名 -->

    ????????????? <property name="hibernate.connection.username">lockdb</property>

    ????????????? <!-- 數(shù)據(jù)庫的密碼 -->

    ????????????? <property name="hibernate.connection.password">lockdb</property>

    ????????????? <!-- 數(shù)據(jù)庫的Dialect -->

    ????????????? <!-- property name="hibernate.dialect">

    ????????????? org.hibernate.dialect.Oracle9Dialect</property -->

    ????????????? <property name="hibernate.dialect">

    ????????????? org.hibernate.dialect.MySQLDialect</property>

    ????????????? <!-- 輸出執(zhí)行的SQL語句 -->

    ????????????? <property name="hibernate.show_sql">true</property>

    ?????????????

    ????????????? <property name="hibernate.current_session_context_class">thread</property>

    ?????????????

    ????????????? <property name="hibernate.hbm2ddl.auto">update</property>

    ????????????? <!-- HBM文件列表 -->

    ????????????? <mapping resource="cn/hxex/hibernate/lock/User.hbm.xml" />

    ?????? </session-factory>

    </hibernate-configuration>

    ?????? 最后要實現(xiàn)的就是測試主程序了,在測試主程序中包含了Hibernate的初始化代碼以及悲觀鎖的測試方法。測試主程序的實現(xiàn)方法如清單14.4所示。

    ?????? 清單14.4??? 測試主程序的實現(xiàn)

    package cn.hxex.hibernate.lock;

    import java.net.URL;

    import java.util.List;

    import org.apache.commons.logging.Log;

    import org.apache.commons.logging.LogFactory;

    import org.hibernate.LockMode;

    import org.hibernate.Query;

    import org.hibernate.Session;

    import org.hibernate.SessionFactory;

    import org.hibernate.cfg.Configuration;

    public class LockMain {

    ??? private static Log log = LogFactory.getLog( LockMain.class );

    ??? // 靜態(tài)Configuration和SessionFactory對象的實例(全局唯一的)

    ??? private static Configuration configuration;

    ??? private static SessionFactory sessionFactory;

    ??? static

    ??? {

    ??????? // 從默認的配置文件創(chuàng)建SessionFactory

    ??????? try

    ??????? {

    ?????????????? ?????????? URL configURL = ClassLoader.getSystemResource(

    ?????????????? ??????????????? "cn/hxex/hibernate/lock/hibernate.cfg.xml" );

    ?????????????? ?????????? // 創(chuàng)建默認的Configuration對象的實例

    ??????????? configuration = new Configuration();

    ??????????? // 讀取hibernate.properties或者hibernate.cfg.xml文件

    ??????????? configuration.configure( configURL );

    ??????????? // 使用靜態(tài)變量來保持SessioFactory對象的實例

    ??????????? sessionFactory = configuration.buildSessionFactory();

    ??????? }

    ??????? catch (Throwable ex)

    ??????? {

    ??????????? // 輸出異常信息

    ??????????? log.error("Building SessionFactory failed.", ex);

    ??????????? ex.printStackTrace();

    ??????????? throw new ExceptionInInitializerError(ex);

    ??????? }

    ??? }

    ??????

    ??? public static SessionFactory getSessionFactory() {

    ???? ???? ??????? return sessionFactory;

    ??? }

    ???

    ??? public void testPessimisticLock() {

    ????????????? SessionFactory sf = LockMain.getSessionFactory();

    ????????????? Session session = sf.getCurrentSession();

    ????????????? session.beginTransaction();

    ?????? ??????

    ????????????? Query query = session.createQuery("from User user");

    ????????????? query.setLockMode("user", LockMode.UPGRADE);

    ????????????? List users = query.list();

    ????????????? for( int i=0; i<users.size(); i++ ) {

    ???????????????????? System.out.println( users.get( i ) );

    ????????????? }

    ????????????? session.getTransaction().commit();

    ??? }

    ???

    ?????? public static void main(String[] args) {

    ?????????????

    ????????????? LockMain main = new LockMain();

    ????????????? main.testPessimisticLock();

    ?????? }

    }

    ?????? 在上面的清單中,testPessimisticLock()方法就是測試悲觀鎖的方法,該方法在執(zhí)行查詢之前通過Query對象的setLockMode()方法設置了訪問User對象的模式,這樣,這個程序在執(zhí)行的時候就會使用以下的SQL語句:

    ?????? select user0_.userId as userId0_, user0_.name as name0_, user0_.age as age0_

    ?????? from USERINFO user0_ for update

    ?????? 除了Query對象外,也可以在使用Session的load()或是lock()時指定鎖模式。

    ?????? 除了前面所提及的兩種鎖模式外,還有三種Hibernate內(nèi)部自動對數(shù)據(jù)進行加鎖的模式,但它的處理是與數(shù)據(jù)庫無關的。

    ?????? LockMode.WRITE:在insert或update時進行鎖定,Hibernate會在調(diào)用save()方法時自動獲得鎖。

    ?????? LockMode.READ:在讀取記錄時Hibernate會自動獲得鎖。

    ?????? LockMode.NONE:沒有鎖。

    ?????? 如果數(shù)據(jù)庫不支持所指定的鎖模式,Hibernate會選擇一個合適的鎖替換,而不是拋出一個異常。

    樂觀鎖

    ?????? 樂觀鎖(Optimistic Locking)認為資料的存取很少發(fā)生同時存取的問題,因而不做數(shù)據(jù)庫層次上的鎖定。為了維護正確的數(shù)據(jù),樂觀鎖是使用應用程序上的邏輯來實現(xiàn)版本控制的。

    在使用樂觀鎖策略的情況下,數(shù)據(jù)不一致的情況一旦發(fā)生,有幾個解決方法,一種是先更新為主,一種是后更新為主,比較復雜的就是檢查發(fā)生變動的數(shù)據(jù)來實現(xiàn),或是檢查所有屬性來實現(xiàn)樂觀鎖。

    ?????? Hibernate中通過檢查版本號來判斷數(shù)據(jù)是否已經(jīng)被其他人所改動,這也是Hibernate所推薦的方式。在數(shù)據(jù)庫中加入一個version字段記錄,在讀取數(shù)據(jù)時連同版本號一同讀取,并在更新數(shù)據(jù)時比較版本號與數(shù)據(jù)庫中的版本號,如果等于數(shù)據(jù)庫中的版本號則予以更新,并遞增版本號,如果小于數(shù)據(jù)庫中的版本號就拋出異常。

    ?????? 下面就來在前面例子的基礎上進行Hibernate樂觀鎖的測試。

    ?????? 首先需要修改前面所實現(xiàn)的業(yè)務對象,在其中增加一個version屬性,用來記錄該對象所包含數(shù)據(jù)的版本信息,修改后的User對象如清單14.5所示。

    ?????? 清單14.5??? 修改后的User對象

    package cn.hxex.hibernate.lock;

    public class User {

    ?????? private String id;

    ?????? private Integer version; // 增加版本屬性

    ?????? private String name;

    ?????? private Integer age;

    ??????

    ?????? // 省略了getter和setter方法

    ?????? ……

    }

    ?????? 然后是修改映射文件,增加version屬性的配置。在這里需要注意的是,這里的version屬性應該使用專門的<version>元素來進行配置,這樣才能使其發(fā)揮樂觀鎖的作用。如果還使用<property>元素來進行配置,那么Hibernate只會將其作為一個普通的屬性來進行處理。

    修改后的映射文件如清單14.6所示。

    ?????? 清單14.6??? 修改后的映射文件

    <?xml version="1.0"?>

    <!DOCTYPE hibernate-mapping PUBLIC

    ?????? "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

    ?????? "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

    <hibernate-mapping package="cn.hxex.hibernate.lock">

    ?????? <class name="User" table="USERINFO" optimistic-lock="version">

    ????????????? <id name="id" column="userId">

    ????? ?????????? <generator class="uuid.hex"/>

    ??? ?????? </id>

    ?????????????

    ????????????? <version name="version" column="version" type="java.lang.Integer"/>

    ?????????????

    ????????????? <property name="name" column="name" type="java.lang.String"/>

    ????????????? <property name="age" column="age" type="java.lang.Integer"/>

    ?????? </class>

    </hibernate-mapping>

    ?????? 接下來還要進行測試主程序的修改。由于需要模擬兩個人同時修改同一個記錄的情況,所以在這里需要將主程序修改為是可以多線程執(zhí)行的,然后在run()方法中,調(diào)用對User對象的修改程序。

    ?????? 實現(xiàn)后的主測試程序如清單14.7所示。

    ?????? 清單14.7??? 修改后的測試主程序

    package cn.hxex.hibernate.lock;

    import java.net.URL;

    import java.util.List;

    import org.apache.commons.logging.Log;

    import org.apache.commons.logging.LogFactory;

    import org.hibernate.LockMode;

    import org.hibernate.Query;

    import org.hibernate.Session;

    import org.hibernate.SessionFactory;

    import org.hibernate.Transaction;

    import org.hibernate.cfg.Configuration;

    public class LockMain extends Thread{

    ……

    ??? public void testOptimisticLock() {

    ??? ?????? SessionFactory sf = LockMain.getSessionFactory();

    ??? ?????? Session session = sf.openSession();

    ??? ?????? Transaction tx = session.beginTransaction();

    ??? ??????

    ??? ?????? User userV1 = (User)session.load( User.class, "1" );

    ??? ??????

    ??? ?????? // 等第二個進程執(zhí)行

    ??? ?????? try {

    ???????????????????? sleep( 3000 );

    ????????????? } catch (InterruptedException e) {

    ???????????????????? e.printStackTrace();

    ????????????? }

    ??? ??????

    ????????????? userV1.setAge(new Integer(32));

    ????????????? tx.commit();

    ????????????? session.close();

    ??? }

    ???

    ??? public void run() {

    ??? ????????????? testOptimisticLock();

    ??? }

    ???

    ?????? public static void main(String[] args) {

    ?????????????

    ????????????? // LockMain main = new LockMain();

    ????????????? // main.testPessimisticLock();

    ?????????????

    ????????????? LockMain main1 = new LockMain();

    ????????????? main1.start();

    ????????????? LockMain main2 = new LockMain();

    ????????????? main2.start();

    ?????? }

    }

    ?????? 最后,執(zhí)行測試主程序,在控制臺中應該看到類似下面的輸出:

    Hibernate: select user0_.userId as userId0_0_, user0_.version as version0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from USERINFO user0_ where user0_.userId=?

    Hibernate: select user0_.userId as userId0_0_, user0_.version as version0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from USERINFO user0_ where user0_.userId=?

    Hibernate: update USERINFO set version=?, name=?, age=? where userId=? and version=?

    Hibernate: update USERINFO set version=?, name=?, age=? where userId=? and version=?

    2006-10-3 21:27:20 org.hibernate.event.def.AbstractFlushingEventListener performExecutions

    嚴重: Could not synchronize database state with session

    org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [cn.hxex.hibernate.lock.User#1]

    ……

    ?????? 在Hibernate所執(zhí)行的UPDATE語句中可以看到,version字段是作為更新的條件來執(zhí)行的。對于第二個進程來說,由于數(shù)據(jù)庫中的記錄已經(jīng)被第一個進程更新(更新的同時會導致version自動增加),就必然會導致第二個進程操作的失敗。Hibernate正是利用這種機制來避免兩次更新問題的出現(xiàn)。

    posted on 2009-07-19 21:11 jadmin 閱讀(160) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導航:
     
    主站蜘蛛池模板: 免费A级毛片无码视频| aa午夜免费剧场| 最近中文字幕大全中文字幕免费 | 成人嫩草影院免费观看| 成年丰满熟妇午夜免费视频| 亚洲女人18毛片水真多| 精品熟女少妇AV免费观看| 丁香婷婷亚洲六月综合色| 无码高潮少妇毛多水多水免费| 亚洲色大情网站www| 青草草在线视频永久免费| 国产精品亚洲片在线花蝴蝶| 亚洲国产精品尤物YW在线观看| 国产精品美女久久久免费 | 免费吃奶摸下激烈视频| 日韩一级片免费观看| 亚洲熟妇无码AV在线播放| 国产成人精品无码免费看| 亚洲国产精品综合久久久| 成人毛片18女人毛片免费视频未 | 亚洲第一中文字幕| av免费不卡国产观看| 亚洲av永久无码天堂网| 亚洲?v无码国产在丝袜线观看| yy一级毛片免费视频| 亚洲av色福利天堂| 成年女人18级毛片毛片免费观看| 三级片免费观看久久| 亚洲午夜久久久精品影院| 拍拍拍又黄又爽无挡视频免费| 黄色a三级三级三级免费看| 久久精品国产亚洲网站| 国拍在线精品视频免费观看| 免费一区二区三区在线视频 | 国产亚洲精品bv在线观看| 亚洲高清偷拍一区二区三区 | 99xxoo视频在线永久免费观看| 亚洲伊人久久大香线蕉AV| 老司机亚洲精品影视www| 日韩视频在线精品视频免费观看 | 国产又大又长又粗又硬的免费视频|