瘋狂

          STANDING ON THE SHOULDERS OF GIANTS
          posts - 481, comments - 486, trackbacks - 0, articles - 1
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          hibernate 事務(wù) annoation

          Posted on 2009-08-31 10:12 瘋狂 閱讀(1243) 評(píng)論(0)  編輯  收藏 所屬分類: databasehibernate

          事務(wù)隔離

           
          使用封鎖技術(shù),事務(wù)對(duì)申請(qǐng)的資源加鎖,但是會(huì)影響數(shù)據(jù)庫性能。根據(jù)數(shù)據(jù)對(duì)象封鎖的程度,可以分成多種不同的事務(wù)隔離級(jí)別。
           
          數(shù)據(jù)并發(fā)執(zhí)行時(shí),產(chǎn)生不一致的現(xiàn)象:
          1
          ,丟失更新(Lost Update
           
          兩個(gè)事務(wù)讀入同一數(shù)據(jù)并修改,然后提交修改,T2 提交的結(jié)果破壞了 T1 提交的結(jié)果,導(dǎo)致 T1 的修改丟失。
          2
          , 不可重復(fù)讀
           
          事務(wù)T1 讀取數(shù)據(jù)后,事務(wù)T2 執(zhí)行了同一數(shù)據(jù)的跟新操作,使得事務(wù) T1 無法再現(xiàn)前一次讀取的結(jié)果。
             
          事務(wù)1 讀取某一數(shù)據(jù)后,事務(wù)2 對(duì)該數(shù)據(jù)作了修改,事務(wù)1 再次讀取時(shí),得到數(shù)據(jù)和前一次不一致。
              ①
          事務(wù)1 讀取某一些記錄后,事務(wù)2 刪除了同一數(shù)據(jù)源的部分?jǐn)?shù)據(jù),事務(wù)1 再次讀取時(shí),發(fā)現(xiàn)某些記錄丟失。
              ①
          事務(wù)1 讀取某一些記錄后,事務(wù)2 插入了同一數(shù)據(jù)源的新數(shù)據(jù),事務(wù)1 再次讀取時(shí),發(fā)現(xiàn)某些記錄增加。
          3
          , 讀數(shù)據(jù)
           
          事務(wù)T1 修改某一數(shù)據(jù),并將其寫回物理數(shù)據(jù)庫。事務(wù)T2 讀取同一數(shù)據(jù)后,事務(wù)T1 由于某種原因被撤銷,數(shù)據(jù)庫將已經(jīng)修改的數(shù)據(jù)恢復(fù)原值,導(dǎo)致事務(wù)T2 保持的數(shù)據(jù)和數(shù)據(jù)庫中的數(shù)據(jù)產(chǎn)生了不一致。

            ANSI SQL-99
          標(biāo)準(zhǔn)定義了下列隔離級(jí)別:
          Hibernate
          在配置文件中聲明事務(wù)的隔離級(jí)別,Hibenate 獲取數(shù)據(jù)庫連接后,將根據(jù)隔離級(jí)別自動(dòng)設(shè)置數(shù)據(jù)庫連接為指定的事務(wù)隔離級(jí)別。
          <property name="connection.isolation">8</property>
          未提交讀(Read Uncommitted):隔離事務(wù)的最低級(jí)別,只能保證不會(huì)讀取到物理上損壞的數(shù)據(jù)。Hibernate配置:1;允許產(chǎn)生:123

          已提交讀(Read Committed):常見數(shù)據(jù)庫引擎的默認(rèn)級(jí)別,保證一個(gè)事務(wù)不會(huì)讀取到另一個(gè)事務(wù)已修改但未提交的數(shù)據(jù)。Hibernate配置:2;允許產(chǎn)生:1
          2

          可重復(fù)讀(Repeatable Read):保證一個(gè)事務(wù)不能更新已經(jīng)由另一個(gè)事務(wù)讀取但是未提交的數(shù)據(jù)。相當(dāng)于應(yīng)用中的已提交讀和樂觀并發(fā)控制。Hibernate配置:4;允許產(chǎn)生:
          1

          可串行化(Serializable):隔離事務(wù)的最高級(jí)別,事務(wù)之間完全隔離。系統(tǒng)開銷最大。Hibernate配置:8;這種情況很容易造成死鎖的問題,hibernate表現(xiàn)為:

          Deadlock found when trying to get lock; try restarting transaction
          2-3、并發(fā)控制類型
            根據(jù)使用的鎖定策略和隔離等級(jí),可以把事務(wù)的并發(fā)控制分為兩種:
          悲觀并發(fā)控制
           
            用戶使用時(shí)鎖定數(shù)據(jù)。主要應(yīng)用于數(shù)據(jù)爭用激烈的環(huán)境中,以及發(fā)生并發(fā)沖突時(shí)用鎖保護(hù)數(shù)據(jù)的成本低于回滾事務(wù)成本的環(huán)境中。
            Hibernate 的悲觀鎖定不在內(nèi)存中鎖定數(shù)據(jù),由底層數(shù)據(jù)庫負(fù)責(zé)完成。
          樂觀并發(fā)控制
           
            用戶讀取數(shù)據(jù)時(shí)不鎖定數(shù)據(jù)。當(dāng)一個(gè)用戶更新數(shù)據(jù)時(shí),系統(tǒng)將進(jìn)行檢查該用戶讀取數(shù)據(jù)后其他用戶是否更改了該數(shù)據(jù),是則產(chǎn)生一個(gè)錯(cuò)誤,一般情況下,收到錯(cuò)誤信息的用戶將回滾事務(wù)并重新開始。主要用戶數(shù)據(jù)爭用不大,且偶爾回滾事務(wù)的成本低于讀取數(shù)據(jù)時(shí)鎖定數(shù)據(jù)的成本的環(huán)境中。
            Hibernate 中使用元素 version  timestamp 實(shí)現(xiàn)樂觀并發(fā)控制模式的版本控制,并提供多種編程方式。版本是數(shù)據(jù)庫表中的一個(gè)字段,可以是一個(gè)遞增的整數(shù),也可以是一個(gè)時(shí)間戳,它們對(duì)應(yīng) Java 持久化類的一個(gè)屬性。事務(wù)提交成功后,Hibernate 自動(dòng)修改版本號(hào)。如果另外一個(gè)事務(wù)同時(shí)訪問同一數(shù)據(jù),若發(fā)現(xiàn)提交前的版本號(hào)和事前載入的版本號(hào)有出入,則認(rèn)為發(fā)生了沖突,事務(wù)停止執(zhí)行,撤銷操作,并拋出異常。應(yīng)用程序必須捕捉該異常并做出一定的處理。
          應(yīng)用程序級(jí)別的版本控制
          長生命周期會(huì)話的自動(dòng)化版本控制
          托管對(duì)象的自動(dòng)化版本控制
          定制自動(dòng)化版本控制

           

           二 實(shí)際應(yīng)用中我們使用Read Committed作為我們的事務(wù)級(jí)別 也就是 已提交讀(Read Committed),因?yàn)槿绻脩?有權(quán)限 修改一個(gè) 數(shù)據(jù) ,那 那么 就可以提交這個(gè)事務(wù),這是系統(tǒng)需要解決的權(quán)限問題,由于這種情況會(huì)造成第二類數(shù)據(jù)丟失的情況,因此要配備樂觀鎖的機(jī)制,這種事物方法使用比較多。下面 舉例實(shí)驗(yàn):

          1,例如A持久化后會(huì)被事務(wù)關(guān)聯(lián)(事務(wù)針對(duì)修改,插入,不針對(duì)查詢,如果對(duì)于一般不需要修改的表,如字典表,可以不配置事務(wù),而是配置二級(jí)緩存來提高性能)

          class A代碼(先不加樂觀鎖,會(huì)造成第二類數(shù)據(jù)丟失):

           

          @Entity
          public class A {
          private int id;
          private int anum;
          private String aname;
          @Id
          @GeneratedValue(strategy=GenerationType.IDENTITY)
          public int getId() {
          return id;
          }
          //get..set
          }

           

           

           hibernate配置:<property name="connection.isolation">2</property>

          事先插入一條數(shù)據(jù)(a.setAnum(1);

          測試代碼:

           

          public static void test(){
          new Thread(new Runnable(){//線程2,啟動(dòng)事務(wù)后不提交,然后讓線程1啟動(dòng)
          public void run() {
          Session session = HibernateSessionFactory.getSession();
          Transaction tt = session.beginTransaction();
          System.out.println("t2 statr....");
          A a = (A) session.get(A.class, 1);
          System.out.println("未修改之前的num:"+a.getAnum());
          a.setAnum(a.getAnum()+1);
          session.update(a);
          try {
          Thread.sleep(5000);//讓出時(shí)間讓線程1執(zhí)行
          } catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
          }
          tt.commit();
          session.clear();//清除一級(jí)緩存
          System.out.println("t2 cummit end....");
          A a1 = (A) session.get(A.class, 1);
          System.out.println("修改之后的num:"+a1.getAnum());
          session.close();
          }
          }).start();//正確結(jié)果為1+1=2
          new Thread(new Runnable(){
          public void run() {
          Session session = HibernateSessionFactory.getSession();
          Transaction tt = session.beginTransaction();
          System.out.println("t1 statr....");
          A a = (A) session.get(A.class, 1);
          a.setAnum(a.getAnum()+2);
          session.update(a);
          tt.commit();
          System.out.println("1 cummit end....");
          session.clear();//清除一級(jí)緩存
          session.close();
          }
          }).start();//正確結(jié)果為1+1=3
          }

           

           

           打印信息:

           

          t2 statr....
          Hibernate: select a0_.id as id0_0_, a0_.aname as aname0_0_, a0_.anum as anum0_0_ from A a0_ where a0_.id=?//讓出時(shí)間讓t1執(zhí)行
          t1 statr....
          Hibernate: select a0_.id as id0_0_, a0_.aname as aname0_0_, a0_.anum as anum0_0_ from A a0_ where a0_.id=?
          未修改之前的num:1
          Hibernate: update A set aname=?, anum=? where id=?
          1 cummit end....
          Hibernate: update A set aname=?, anum=? where id=?
          t2 cummit end....
          Hibernate: select a0_.id as id0_0_, a0_.aname as aname0_0_, a0_.anum as anum0_0_ from A a0_ where a0_.id=?
          修改之后的num:2

           

           

           可以看出他t1本來應(yīng)該是3但是被t2覆蓋

           

           為此我們加上鎖:

          修改A(鎖可以有兩種,version,和時(shí)間鎖,但是時(shí)間鎖有個(gè)精確度問題)使用version

           

          private int version;
          ...
          @Version//使用version注釋來表明版本控制
          public int getVersion() {
          return version;
          }

           

           

           測試(同樣的測試代碼):

          打印信息:

           

          t2 statr....
          Hibernate: select a0_.id as id0_0_, a0_.aname as aname0_0_, a0_.anum as anum0_0_, a0_.version as version0_0_ from A a0_ where a0_.id=?
          t1 statr....
          Hibernate: select a0_.id as id0_0_, a0_.aname as aname0_0_, a0_.anum as anum0_0_, a0_.version as version0_0_ from A a0_ where a0_.id=?
          未修改之前的num:1
          Hibernate: update A set aname=?, anum=?, version=? where id=? and version=?
          1 cummit end....
          Hibernate: update A set aname=?, anum=?, version=? where id=? and version=?
          Exception in thread "Thread-0" org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.eric.po.A#1]
          at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1792)
          at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2435)
          at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2335)
          at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2635)
          at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:115)
          at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
          at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
          at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168)
          at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
          at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
          at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
          at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:365)
          at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
          at com.eric.dao.Testcreate$1.run(Testcreate.java:139)
          at java.lang.Thread.run(Thread.java:619)

           

           

           此時(shí)樂觀所就起作用了,防止了數(shù)據(jù)的丟失。針對(duì)異常可自行捕獲。

           

           

           

           

           

           

           

           



          主站蜘蛛池模板: 黄山市| 淮阳县| 天长市| 沙雅县| 金沙县| 三河市| 孟津县| 金塔县| 平顺县| 闵行区| 于都县| 闸北区| 莱州市| 赣榆县| 方山县| 克什克腾旗| 浦城县| 潜山县| 道真| 河北区| 元谋县| 桐柏县| 铜陵市| 科技| 新化县| 拜泉县| 府谷县| 凤凰县| 鄯善县| 建始县| 扎囊县| 淄博市| 镇巴县| 昌平区| 蓬溪县| 九龙城区| 闸北区| 潜江市| 江川县| 大关县| 宜良县|