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]