posts - 156,  comments - 601,  trackbacks - 0

              好久的筆記了,趁剛好休息整理文檔,翻出這一部分,稍加整理后,就發上來給大家共享一下,希望對各位有所幫助。

              關于LazyLoadException異常,使用過Hibernate O/R Mapping工具的人應該都遇到過,網上也是有很多解決的方案,其中Spring提供的一個方案就是在web.xml增加一個filter,示例代碼如下:

          <filter>  
              
          <filter-name>entityManager</filter-name>  
              
          <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>  
          </filter>  
            
          <filter-mapping>  
              
          <filter-name>entityManagerFilter</filter-name>  
              
          <url-pattern>*.action</url-pattern>  
          </filter-mapping> 

            解決辦法有了,接下來應該會有人好奇:這個配置filter后它是如何工作的?
            下面來分析一下這個功能實現的源代碼, 不過之前,比較重要的是了解,為何會出現lazyload exception的異常發生。

             下面我模擬寫了一段代碼,這段代碼就會發生該異常
             注:只是為了說明,相關的代碼就省略了。

          @Entity
          public class Room {

           @Id
           @Column(length
          =32)
           
          private String id;

           @Column(length
          =20)
           
          private code;
           
           @OneToMany(mappedBy
          ="room"//default is use lazy load strategy 
           private Set desks;
          }

          @Entity
          public class Desk {

               @Id
               @Column(length
          =32)
               
          private String id;
              
               @Column(length
          =20)
               
          private code;
               
               @ManyToOne
               
          private Room root;

          }

          public class RoomSerivce {

              @Transactional(readOnly
          =true)
              
          public Room getRoomById(String roomId) {
                Assert.notBlank(roomId, 
          "room'id is null);
                  return getDao().findById(roomId);
              
              }

          }


           1 public class RoomServiceTest {
           2 
           3     public static void main(String[] args[]) {
           4     
           5         //get service from spring beanfactory
           6         RoomService service = SpringContext.getSerivce("roomService");
           7         Assert.notNull(service, " roomService bean not exsit");
           8     
           9       Room room = service.getRoomById("1");
          10       //here lazy exception throw out
          11       Set Desks = room.getDesks();
          12       CollectionsUtils.toString(Desks);
          13     }
          14 }

             分析這段代碼,我們不難發現,在RoomServiceTest這個測試的例子中,因為使用了基于Annotation的聲明性事務,所以在RoomSerivce.getRoomById方法運行結束后,事務就已經提交了。但示例中Room實體與Desk實例的關系使用的是lazy fetch的策略,此時Room對象中的desks集合還是為空。
          當執行到下面兩句時(這才真正使用到desks集合時)
            Set Desks = room.getDesks();
            CollectionsUtils.toString(Desks);
          Hibernate就會根據原來lazy設定的方式,取EntityManager, 根據它從數據庫查詢 Desk實現的數據,這時上面我們已經提到,事務已經隨getRoomById方法的運行結束提交. EntityManager對象也已經關閉。此時再調用 EntityManager操作,就會報EntityManager has been closed 異常(lazy load exception)

             ok, 清楚這塊,大家有時可能也猜想到了Spring這個解決方案是怎么處理的了。
             Spring的TransactionInterceptor 其就是通過AOP負責攔截著所有針對事務TransactionManager的操作.
             這樣Spring就可以針對lazy異常進行攔截了。

             清楚上面的后,下面的代碼是非常好理解了,來看一下OpenEntityManagerInViewFilter的代碼:
          我加了些注釋,大家很容易明白:

           1     protected void doFilterInternal(
           2             HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
           3             throws ServletException, IOException {
           4 
           5       //通過WebApplicationContext,從Web服務中取得context實例后,根據EntityManagerFactory.class類型
           6       //取得EntityManagerFacotry實例
           7         EntityManagerFactory emf = lookupEntityManagerFactory(request);
           8         boolean participate = false;
           9 
          10         //如果靜態方法hasResource已經有EntityManagerFactory實例了,就不用再通過
          11         //EntityManagerFactory創建一個新EntityManger了
          12         if (TransactionSynchronizationManager.hasResource(emf)) {
          13             // Do not modify the EntityManager: just set the participate flag.
          14             participate = true;
          15         }
          16         else {
          17             logger.debug("Opening JPA EntityManager in OpenEntityManagerInViewFilter");
          18             try {
          19             //通過EntityManagerFactory創建一個新EntityManger,并通過bindResource方法
          20             //保存到TransactionSynchronizationManager中
          21             //這樣,通TransactionSynchronizationManager的getResource方法取得EntityMangerHolder
          22                 EntityManager em = createEntityManager(emf);
          23                 TransactionSynchronizationManager.bindResource(emf, new EntityManagerHolder(em));
          24             }
          25             catch (PersistenceException ex) {
          26                 throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex);
          27             }
          28         }
          29 
          30         try {
          31             filterChain.doFilter(request, response);
          32         }
          33 
          34         finally {
          35             if (!participate) {
          36             //每次請求結束后,就把EntityManager關閉
          37                 EntityManagerHolder emHolder = (EntityManagerHolder)
          38                         TransactionSynchronizationManager.unbindResource(emf);
          39                 logger.debug("Closing JPA EntityManager in OpenEntityManagerInViewFilter");
          40                 EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager());
          41             }
          42         }
          43     }
          44    

              上面的代碼就不用多解釋了, 到現在已經很清楚知道Spring針對 Hibernate的Lazy問題是怎么解決的。
              當然我們可以擴展到除Web服務以外,來實現解決lazy的問題。(我們自己來管理TransactionSynchronizationManager就可以了)
             
              當然Spring針對 Hibernate(非JPA的實現)原理也是一樣,只是它針對的SessionFactory,也是由TransactionSynchronizationManager來統一管理。
             
              最后如果大家如還有不清楚的,歡迎一起討論。
             
          Good Luck!
          Yours Matthew!

          posted on 2008-10-11 18:01 x.matthew 閱讀(3098) 評論(3)  編輯  收藏 所屬分類: Spring|Hibernate|Other framework
          主站蜘蛛池模板: 宁波市| 武乡县| 桐梓县| 柞水县| 红原县| 东阿县| 玛沁县| 曲沃县| 汉川市| 新龙县| 洮南市| 霍山县| 梁山县| 张家界市| 阿克苏市| 博乐市| 塔城市| 普格县| 定边县| 旺苍县| 奉节县| 博乐市| 阳东县| 遵义市| 永安市| 秦安县| 汝城县| 徐州市| 敦煌市| 京山县| 彰武县| 岳普湖县| 南召县| 容城县| 遵义县| 柳河县| 宜阳县| 邵阳市| 女性| 山阴县| 兴和县|