humor200

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            1 Posts :: 1 Stories :: 1 Comments :: 0 Trackbacks
          樂觀鎖是hibernate用來識別update到數據庫的對象是否是臟對象的功能鎖,在get->operation->update這種非原子操作中這種并發風險非常常見,get同一個對象,按照相反順序設置新值然后更新就會出現臟對象的覆蓋。
          可以按照如下配置設置樂觀鎖version,這樣剛才的臟對象操作就會彈出 StaleObjectStateException 異常。配置如下(注解也可以):
          <class name="com.hibe.hbm.Customer" table="CUSTOMERS" optimistic-lock="version">
          <id name="id" column="ID" type="int">
          <generator class="increment"/>
          </id>
          <version name="version" column="ver" type="int"></version>
          <property name="name" column="NAME" type="string" not-null="true"/>
          <property name="password" column="PASSWORD" type="string" not-null="true"/>
          </class>
          在po和db里也要增加相應的字段。
          不要以為配置這個就萬事大吉了,用10個線程操作更新同一個對象,幾乎有9-10個都會失敗,彈出異常,那怎么辦呢,就要處理,不處理還不如不加鎖呢,全失敗了。
          如果update的新值是絕對值比如表單form也無所謂了,不加鎖覆蓋了也行,加鎖重試也是一樣效果,可是要是那種getValue,然后+1 或者+2,+3,再update的覆蓋就不行了,和業務邏輯違背。也就說你要知道要update的這個對象是怎么來的,中間做了什么,才得到的新值,這幾個小操作作為原子操作,在重試的時候要還原回當初的業務邏輯。于是我寫了個下面的小類:
          /**
           * 更新hibernate對象,用此類更新可以實現臟對象自動重試更新功能
           * @author hongweizhang@cyou-inc.com
           * @param <T>
           */
          public abstract class Updater<T> {
            private Logger logger = LoggerFactory.getLogger(Updater.class);
              /**
               * 構建實際對象
               * 
               * @return
               * @throws Exception
               */
              public abstract T makeObjectForUpdate() throws Exception;
              /**
               * 實際更新方法,已經實現臟對象更新自動重試功能。
               * @param object
               * @param session
               * @param c
               * @return
               * @throws Exception
               */
              private boolean update(Dao dao, int c) throws Exception {
              Object obj = null;
              try {
          obj = makeObjectForUpdate();
          if(c>=100) {    //不怕一萬就怕萬一 死循環
          logger.error("thread:" + Thread.currentThread().getId() +",error: update retry over 100 times,object:" + obj + ",object class:" + obj.getClass());
          return false;
          }
          dao.update(obj);
          if(c>0) {
          logger.info("thread:" + Thread.currentThread().getId() +",success: update retry success,object:" + obj + ",object class:" + obj.getClass() + ",times:" + c);
          }
          c++;
          return true;
          } catch (Exception e) {
          if(e instanceof Exception) {
          c++;
          logger.error("thread:" + Thread.currentThread().getId() +",StaleObjectStateException,object:"+obj+",class:"+obj.getClass());
          update(dao, c);
          }
          return false;
          } finally {
          }
              }
              
              /**
               * 更新數據庫
               * @param object
               * @param session
               * @throws Exception
               */
              public boolean update(Dao dao) throws Exception{
              return update(dao, 0);
          }
          }
          使用方法調用端如下(多線程測試,實際使用去掉多線程):
          public void update() throws Exception {
                  try {
                  for(int i=0; i < 10; i ++) {
                  new Thread(new Runnable() {
              @Override
              public void run() {
              try {
              new Updater<Customer>() {
              @Override
              public Customer makeObjectForUpdate() throws Exception {
              Customer c = getById(1);
              //c.setName("name"+System.currentTimeMillis());
              int p = Integer.parseInt(c.getPassword());
              c.setPassword(String.valueOf((p+1)));
              System.out.println("Thread:"+Thread.currentThread().getId()+",reload," + c);
              return c;
              }
              }.update(dao);
              } catch (Exception e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
              }
              }
              }).start();
                  }
                  } catch (Exception e) {
                      throw new ServiceException(e);
                  }
                  
              }
          注:dao對象是我們自己封裝的數據操作載體,你可以用原生session都一個意思。
          通過實現抽象方法的方式將你make對象的業務邏輯包裝起來,和調用端依賴的外圍參數,比如form都可以可見,在工具類里面當更新失敗遞歸的時候,重新調用抽象實現,也就是你的業務邏輯。這樣就確保萬無一失了,小方法和大家分享一下。經過測試,效果非常滿意。
          posted on 2013-07-23 09:51 蟑螂也瘋狂 閱讀(1432) 評論(1)  編輯  收藏

          Feedback

          # re: hibernate樂觀鎖以及臟對象更新失敗自動重試小工具 2013-07-23 09:59 mshow
          虎子  回復  更多評論
            


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


          網站導航:
           
          主站蜘蛛池模板: 越西县| 宣化县| 三穗县| 喀什市| 城固县| 金寨县| 纳雍县| 抚宁县| 伊川县| 南郑县| 西贡区| 萝北县| 安庆市| 察雅县| 江华| 永宁县| 西宁市| 宜宾市| 同仁县| 荆门市| 肥西县| 铁岭市| 静海县| 达州市| 泗阳县| 通道| 铜鼓县| 花莲市| 南部县| 芮城县| 噶尔县| 阿荣旗| 武冈市| 岳普湖县| 广汉市| 子长县| 香港| 白银市| 芦山县| 筠连县| 南部县|