首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网站开发 > Web前端 >

源代码解读Spring+Hibernate(JPA)的LazyLoadException错误

2012-10-08 
源代码解读Spring+Hibernate(JPA)的LazyLoadException异常好久的笔记了,趁刚好休息整理文档,翻出这一部分,

源代码解读Spring+Hibernate(JPA)的LazyLoadException异常
    好久的笔记了,趁刚好休息整理文档,翻出这一部分,稍加整理后,就发上来给大家共享一下,希望对各位有所帮助。

    关于LazyLoadException异常,使用过Hibernate O/R Mapping工具的人应该都遇到过,网上也是有很多解决的方案,其中Spring提供的一个方案就是在web.xml增加一个filter,示例代码如下:


分析这段代码,我们不难发现,在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的代码:
我加了些注释,大家很容易明白:
 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方法取得EntityMangerHolder22                 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     }

  上面的代码就不用多解释了, 到现在已经很清楚知道Spring针对 Hibernate的Lazy问题是怎么解决的。
    当然我们可以扩展到除Web服务以外,来实现解决lazy的问题。(我们自己来管理TransactionSynchronizationManager就可以了)
  
    当然Spring针对 Hibernate(非JPA的实现)原理也是一样,只是它针对的SessionFactory,也是由TransactionSynchronizationManager来统一管理。
  
    最后如果大家如还有不清楚的,欢迎一起讨论。
  
Good Luck!
Yours Matthew!

热点排行