关于ORM延迟加载问题

 前言:题目有点大了(#>.<),主要就是讨论一下,在使用JPA或者Hibernate的时候(JPA虽然有Hibernate实现,但是还是拆开来说),多表关联加载时机方案的选择的问题。Lazy给很多开发带来困扰,也有很多文章讲解了这些问题,下面是我个人的解决思路和解决方案。
        关键词:    JPA         Hibernate         动态代理         Aop

会话管理
        无论是使用JPA或者Hibernate在延迟加载处理中,我们关心的问题无非就是,Session或者EntityManager什么时候打开,什么时候关闭!而解决延迟加载的问题核心在于解决Session或者EntityManager的开关时效管理。
视图层(JPA)
        在WEB项目中,基于Filter接口规范来处理,配合Spring使用,在Spring ORM的支持包中已经有解决方案:
       
        OpenEntityManagerInViewFilter
        1.  在entity中的@OneToMany、@OneToOne等中设置fetch = FetchType.LAZY
        2.  标准的Filter,在web.xml配置即可
  <filter-mapping>

xml 代码
 
  1. <filter>  
  2.          <filter-name>jpaFilter</filter-name>  
  3.          <filter-class>  
  4. org.springframework.orm.jpa.support. OpenEntityManagerInViewFilter  
  5.          </filter-class>  
  6.          <init-param>  
  7.            <param-name>entityManagerFactory</param-name>  
  8.            <param-value>entityManagerFactory</param-value>  
  9.          </init-param>  
  10. </filter>  
  11. <filter-mapping>  
  12.        <filter-name>jpaFilter</filter-name>  
  13.        <url-pattern>*.action</url-pattern>  
  14. </filter-mapping>  


</filter-mapping>        3.  OpenEntityManagerInViewFilter处理的是Spring通过在ServletContext 中载入配置,通过WebApplicationContextUtils.getWebApplicationContext(ServletContext sc)方法获得ApplicationContext,如果想要自行载入构建的ApplicationContext被OpenEntityManagerInViewFilter处理,需要修改其源代码。
       
        OpenEntityManagerInViewInterceptor
        这个Interceptor使用在Spring MVC的结构,使用Spring MVC的可以在applicationContext-*.xml中定义注入即可,它的ref指向EntityManagerFactory ,它也是SimpleUrlHandlerMapping的interceptors属性的list之一,使用Spring MVC的开发人员可以看一下,配置很方便。

视图层(Hibernate)
        Hiberate和JPA在Spring中的解决方案是非常类似的。
       
        OpenSessionInViewFilter

        1.   必须在Hibernate的配置文件*.hbm.xml中,对class定义lazy="true"或者在many-to-one、
        one-to-one映射关心中定义lazy="proxy"。
        2.   标准的filter配置,在web.xml中

xml 代码
 
  1.    <filter>  
  2.        <filter-name>hibernateFilter</filter-name>  
  3.        <filter-class>  
  4. rg.springframework.orm.hibernate3.support.OpenSessionInViewFilter  
  5.        </filter-class>  
  6. <!-- 是否单个Session-->  
  7.        <init-param>  
  8.            <param-name>singleSession</param-name>  
  9.            <param-value>false</param-value>  
  10.        </init-param>  
  11. <!-- 你在applicationCointext*.xml中配置的SessionFactory bean的id名-->  
  12.        <init-param>  
  13.            <param-name>sessionFactoryBeanName</param-name>  
  14.            <param-value>mySessionFactory</param-value>  
  15.        </init-param>  
  16.    </filter>  
  17.    <filter-mapping>  
  18.        <filter-name>hibernateFilter</filter-name>  
  19.        <url-pattern>*.action</url-pattern>  
  20.    </filter-mapping>  


<filter></filter><filter-mapping><url-pattern></url-pattern>   </filter-mapping>     3.   OpenSessionInViewFilter处理的是Spring通过在ServletContext 中载入配置,通过WebApplicationContextUtils.getWebApplicationContext(ServletContext sc)方法获得ApplicationContext,如果想要自行载入构建的ApplicationContext被OpenSessionInViewFilter处理,需要修改其源代码。

        OpenSessionInViewInterceptor
        这个Interceptor使用在Spring MVC的结构,使用Spring MVC的可以在applicationContext-*.xml中定义注入即可,它的ref指向SessionrFactory ,它也是SimpleUrlHandlerMapping的interceptors属性的list之一

服务层
    动态代理(JPA)
    使用动态代理AOP结构,来管理EntityManager,这种方案不依赖于容器,在后台服务、测试中都可以良好使用。先看代码:
OpenEntityManagerHandler.java

java 代码
 
  1. import java.lang.reflect.InvocationHandler;  
  2. import java.lang.reflect.Method;  
  3. import java.lang.reflect.Proxy;  
  4.   
  5. import javax.persistence.EntityManager;  
  6. import javax.persistence.EntityManagerFactory;  
  7.   
  8. import org.apache.log4j.Logger;  
  9. import org.springframework.context.ApplicationContext;  
  10. import org.springframework.orm.jpa.EntityManagerHolder;  
  11. import org.springframework.transaction.support.TransactionSynchronizationManager;  
  12.   
  13. import cn.neto.util.JSFUtils;  
  14.   
  15. public class OpenEntityManagerHandler implements InvocationHandler {  
  16.     private static final Logger logger = Logger  
  17.             .getLogger(OpenEntityManagerHandler.class);  
  18.     private ApplicationContext ctx = null;  
  19.     private EntityManagerFactory emFactory = null;     
  20.     private Object obj;  
  21.   
  22.     public OpenEntityManagerHandler() {  
  23.         if (ctx == null)  
  24.             ctx = getApplicationContext();  
  25.         if (emFactory == null)  
  26.             emFactory = (EntityManagerFactory) ctx.getBean(  
  27.                     "entityManagerFactory", EntityManagerFactory.class);  
  28.     }  
  29.   
  30.     public Object invoke(Object arg0, Method arg1, Object[] arg2)  
  31.             throws Throwable {  
  32.         Object result = null;  
  33.         boolean isOpen = false;  
  34.         if (TransactionSynchronizationManager.hasResource(emFactory)) {  
  35.             isOpen = true;  
  36.         } else {  
  37.             // 开  
  38.             logger.info("[open entity manager!]");  
  39.             EntityManager em = emFactory.createEntityManager();  
  40.             TransactionSynchronizationManager.bindResource(emFactory,  
  41.                     new EntityManagerHolder(em));  
  42.         }  
  43.         try {  
  44.             result = arg1.invoke(this.obj, arg2);  
  45.         } catch (Exception e) {  
  46.             e.printStackTrace();  
  47.         } finally {  
  48.             try {  
  49.                 // 关  
  50.                 if (!isOpen) {  
  51.                     EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager  
  52.                             .unbindResource(emFactory);  
  53.                     emHolder.getEntityManager().close();  
  54.                     logger.info("[close entity manager!]");  
  55.                 }  
  56.             } catch (Exception e) {  
  57.                 e.printStackTrace();  
  58.             }  
  59.         }  
  60.         return result;  
  61.   
  62.     }  
  63.   
  64.     public Object bind(Object obj) {  
  65.         this.obj = obj;  
  66.         return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj  
  67.                 .getClass().getInterfaces(), this);  
  68.     }  
  69.     private ApplicationContext getApplicationContext(){  
  70.          //TODO  
  71.         /* 
  72.         *自行获得ApplicationContext 
  73.         */  
  74.     }  
  75. }  


        演示
            接口
        ITask.java

java 代码
 
  1. public interface ITask {  
  2.             public void process();  
  3. }  


            实现
        ITaskImpl.java

java 代码
 
  1. public class ItaskImpl implements Itask{  
  2.     public void process(){  
  3.         System.out.println(“Hello World!”);  
  4.     }  
  5. }  


            使用
       

java 代码
 
  1. OpenEntityManagerHandler handler = new OpenEntityManagerHandler();  
  2. ITask task = (ITask) handler.bind(new ITaskImpl());  
  3. task.process();  


            运行结果

java 代码
  1. [open entity manager!]  
  2. Hello World!  
  3. [close entity manager!]  


    是不是很简单!

    深入
        以上的方案不修改的Service/DAO模型,侵入性小,测试方便,但是有一些局限性。更良好的方案可以将OpenEntityManagerHandler进行一些修改,
1.    加入private String FACTORY_NAME=DEFAULT_ENTITYMANGER_FACTORY,修改获取EntityManagerFactory的方法。
2.    在applicationContext-*.xml中配置bean,并ref指向EntityManagerFactory的bean。
3.    修改你的Service/DAO模型,引入OpenEntityManagerHandler。
留给有兴趣的自己研究,修改一下。

补充
    Hiberate的解决方案和JPA非常类似,不再重复了。
   
使用IBatis
        不是开玩笑,标准并不能决定一切,JPA和Hiberante也不是持久层的唯一解决方案(JDO等其他不用再考虑了,慢慢的退出舞台吧)!
        Ibatis严格来讲不是一个标准ORM框架(真的不太喜欢用框架这个词,头大^.^),如果你本人的SQL造诣比较精深,无论是新项目的开发或者老项目的改造Ibatis都能给你不错的惊喜!
        Ibatis延迟加载的开关参数是lazyLoadingEnabled,具体内容参考官方文档,开发者本身SQL水平的高低直接影响Ibatis的使用优劣。
        Ibatis现在是apache的顶级项目http://ibatis.apache.org/,文档(有中文的,好像是夏新或者曹晓刚翻译的,有点记得不太清楚了),范例应有尽有,自己去Happy吧。

范例运行
    范例中是一个简单的动态代理运行的例子,其中把载入Spring和JPA、Hiberante的依赖去掉了,让大家可以看一下结构,真正配合Spring、JPA/Hiberante使用,需要大家自己增添代码,感兴趣的自己丰富一下代码,希望对你有所启发。

总结
    文中提到了四种解决方案,
第一、    使用OpenEntityManagerInViewFilter/OpenSessionInViewFilter,在Spring MVC结构中还可以更方便简洁的使用 OpenEntityManagerInViewInterceptor/OpenSessionInViewInterceptor。
第二、    使用非注入的AOP的结构,应用动态代理,不依赖于任何容器。
第三、    配合Spring使用,需要修改Service/DAO模型,引入自实现的OpenEntityManagerInterceptor/OpenSessionInterceptor
第四、    使用Ibatis。(这个貌似不能算方案#>.<)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值