ORM框架关于Lazy的解决方案

ORM框架关于Lazy的解决方案

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

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

[list=1]<filter>
          <filter-name>jpaFilter</filter-name>
          <filter-class>org.springframework.orm.jpa.support. OpenEntityManagerInViewFilter  </filter-class>
[color=#99cc00]  [/color]       <init-param>
            <param-name>entityManagerFactory</param-name>
            <param-value>entityManagerFactory</param-value>
          </init-param>
</filter>
<filter-mapping>
        <filter-name>jpaFilter</filter-name>
        <url-pattern>*.action</url-pattern>
[*]</filter-mapping>
[/list]

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

[size=3][b]视图层(Hibernate)[/b][/size]
        Hiberate和JPA在Spring中的解决方案是非常类似的。
[i]        
        OpenSessionInViewFilter[/i]
        1.   必须在Hibernate的配置文件*.hbm.xml中,对class定义lazy="true"或者在many-to-one、
        one-to-one映射关心中定义lazy="proxy"。
        2.   标准的filter配置,在web.xml中

xml 代码

[list=1]
<filter>
        <filter-name>hibernateFilter</filter-name>
        <filter-class>
rg.springframework.orm.hibernate3.support.OpenSessionInViewFilter          </filter-class>
<!-- 是否单个Session-->
        <init-param>
            <param-name>singleSession</param-name>
            <param-value>false</param-value>
        </init-param>
<!-- 你在applicationCointext*.xml中配置的SessionFactory bean的id名-->
        <init-param>
            <param-name>sessionFactoryBeanName</param-name>
            <param-value>mySessionFactory</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>hibernateFilter</filter-name>
        <url-pattern>*.action</url-pattern>
[*]   </filter-mapping>
[/list]

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

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

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

java 代码

[list=1]import java.lang.reflect.InvocationHandler;   import java.lang.reflect.Method;   import java.lang.reflect.Proxy;   
import javax.persistence.EntityManager;   import javax.persistence.EntityManagerFactory;   
import org.apache.log4j.Logger;   import org.springframework.context.ApplicationContext;   import org.springframework.orm.jpa.EntityManagerHolder;   import org.springframework.transaction.support.TransactionSynchronizationManager;   
import cn.neto.util.JSFUtils;   
public
class OpenEntityManagerHandler implements InvocationHandler {                                                                            private static final Logger logger = Logger .getLogger(OpenEntityManagerHandler.class);                                                                                                                         private ApplicationContext ctx = null;                                                                                                                                  private EntityManagerFactory emFactory = null;                                                                                                                   private Object obj;   
     public OpenEntityManagerHandler() {                                        

                            if (ctx == null)           ctx = getApplicationContext();                                                                                                    if (emFactory == null)               emFactory = (EntityManagerFactory) ctx.getBean(                       "entityManagerFactory", EntityManagerFactory.class);       }   


     public Object invoke(Object arg0, Method arg1, Object[] arg2)       throws Throwable {                                                  Object result = null;                boolean isOpen = false;         

  if (TransactionSynchronizationManager.hasResource(emFactory)) {            isOpen = true;           }

    else {               // 开
             logger.info("[open entity manager!]");                                                                                                                     EntityManager em = emFactory.createEntityManager();                 TransactionSynchronizationManager.bindResource(emFactory,   new EntityManagerHolder(em));           }       

    try {          

        result = arg1.invoke(this.obj, arg2);      } catch (Exception e) {     e.printStackTrace();         } finally {               try {                   // 关
                 if (!isOpen) {                       EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager .unbindResource(emFactory);                           emHolder.getEntityManager().close();                                                                                                                   logger.info("[close entity manager!]");       }       } catch (Exception e) {   e.printStackTrace();  }  }   

        return result;   
     }

   
     public Object bind(Object obj) {          

                this.obj = obj;                  return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj                   .getClass().getInterfaces(), this);       }    

   private ApplicationContext getApplicationContext(){            //TODO
         /*          *自行获得ApplicationContext          */
     }   [*]}  [/list]

        [b]演示[/b]
            [i]接口[/i]
        ITask.java

java 代码

[list=1]public
interface ITask {               public
void process();   [*]}  [/list]

            [i]实现[/i]
        ITaskImpl.java

java 代码

[list=1]public
class ItaskImpl implements Itask{       public
void process(){           System.out.println(“Hello World!”);       }   [*]}  [/list]

            [i]使用[/i]
        
java 代码

[list=1]OpenEntityManagerHandler handler = new OpenEntityManagerHandler();   ITask task = (ITask) handler.bind(new ITaskImpl());   [*]task.process();  [/list]

            [i]运行结果[/i]

java 代码
[list=1][open entity manager!]   Hello World!   [*][close entity manager!]  [/list]

    是不是很简单!

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

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

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

[size=3][b]总结[/b][/size]
    文中提到了四种解决方案,
第一、    使用OpenEntityManagerInViewFilter/OpenSessionInViewFilter,在Spring MVC结构中还可以更方便简洁的使用 OpenEntityManagerInViewInterceptor/OpenSessionInViewInterceptor。
第二、    使用非注入的AOP的结构,应用动态代理,不依赖于任何容器。
第三、    配合Spring使用,需要修改Service/DAO模型,引入自实现的OpenEntityManagerInterceptor/OpenSessionInterceptor
第四、    使用Ibatis。(这个貌似不能算方案#>.<)
[table=95%][tr][td=3,1][b]demo.rar[/b][/td][/tr][tr][td=1,1,15%] 描述:[/td][td=1,1,75%]  [/td][td=1,4,10%][img]http://www.javaeye.com/images/forum/icon_clip.gif[/img]
[b][color=#800080]下载[/color][/b] [/td][/tr][tr][td=1,1,15%] 文件名:[/td][td=1,1,75%] demo.rar[/td][/tr][tr][td=1,1,15%] 文件大小:[/td][td=1,1,75%] 1 KB[/td][/tr][tr][td=1,1,15%] 下载过的:[/td][td=1,1,75%] 文件被下载或查看 3 次[/td][/tr][/table] 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值