humor200

            BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
            1 Posts :: 1 Stories :: 1 Comments :: 0 Trackbacks
          樂觀鎖是hibernate用來識別update到數(shù)據(jù)庫的對象是否是臟對象的功能鎖,在get->operation->update這種非原子操作中這種并發(fā)風險非常常見,get同一個對象,按照相反順序設置新值然后更新就會出現(xiàn)臟對象的覆蓋。
          可以按照如下配置設置樂觀鎖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的覆蓋就不行了,和業(yè)務邏輯違背。也就說你要知道要update的這個對象是怎么來的,中間做了什么,才得到的新值,這幾個小操作作為原子操作,在重試的時候要還原回當初的業(yè)務邏輯。于是我寫了個下面的小類:
          /**
           * 更新hibernate對象,用此類更新可以實現(xiàn)臟對象自動重試更新功能
           * @author hongweizhang@cyou-inc.com
           * @param <T>
           */
          public abstract class Updater<T> {
            private Logger logger = LoggerFactory.getLogger(Updater.class);
              /**
               * 構(gòu)建實際對象
               * 
               * @return
               * @throws Exception
               */
              public abstract T makeObjectForUpdate() throws Exception;
              /**
               * 實際更新方法,已經(jīng)實現(xiàn)臟對象更新自動重試功能。
               * @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) {    //不怕一萬就怕萬一 死循環(huán)
          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 {
          }
              }
              
              /**
               * 更新數(shù)據(jù)庫
               * @param object
               * @param session
               * @throws Exception
               */
              public boolean update(Dao dao) throws Exception{
              return update(dao, 0);
          }
          }
          使用方法調(diào)用端如下(多線程測試,實際使用去掉多線程):
          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對象是我們自己封裝的數(shù)據(jù)操作載體,你可以用原生session都一個意思。
          通過實現(xiàn)抽象方法的方式將你make對象的業(yè)務邏輯包裝起來,和調(diào)用端依賴的外圍參數(shù),比如form都可以可見,在工具類里面當更新失敗遞歸的時候,重新調(diào)用抽象實現(xiàn),也就是你的業(yè)務邏輯。這樣就確保萬無一失了,小方法和大家分享一下。經(jīng)過測試,效果非常滿意。
          posted on 2013-07-23 09:51 蟑螂也瘋狂 閱讀(1432) 評論(1)  編輯  收藏

          Feedback

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


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


          網(wǎng)站導航:
           
          主站蜘蛛池模板: 体育| 唐海县| 韩城市| 陆丰市| 益阳市| 界首市| 泾阳县| 合肥市| 隆安县| 苍南县| 洪江市| 腾冲县| 故城县| 都昌县| 安远县| 邛崃市| 丹棱县| 富顺县| 台前县| 平舆县| 诸城市| 辽阳县| 航空| 华亭县| 通江县| 微山县| 庄河市| 蒙自县| 彭泽县| 工布江达县| 屏边| 鄱阳县| 隆昌县| 邢台县| 永泰县| 定安县| 马公市| 泊头市| 乌兰浩特市| 若尔盖县| 玉门市|