转载于:http://spaces.msn.com/members/zcgly/Blog/cns!1pQwDnSfBx4siamZpHR2gqMQ!121.entry

Hibernate和Spring的延迟加载和DAO模式
原文:http://www.jroller.com/page/kbaum/20040708
作者:Karl Baum
译者:zcgly
时间:2005-07-13
Hibernate和延迟加载
Hibernate对象关系映射提供了两种对象初始化模式:延迟加载和非延迟加载。非延迟加载在加载时获取对象本身以及它关联的所有对象
。这可能导致在获取一个实例时,执行成百上千的select语句。当使用双向关联时,这个问题被放大,常常出现初始化请求时,整个数据
库都被载入。显然检查每个对象的关系,并手工删除他们会费点事,但最终我们可能会因此丢失使用ORM工具的优势。
一个明细的解决方式是使用hibernate提供的延迟载入机制。这种初始化策略在类成员被访问时只载入它的一个对象的一对多和多对多关
系。对开发人员来说,这种方式是透明的,并且只有最少数量的请求发生,这样就获得了最佳的性能。这种技术的一个缺点是延迟载入要
求当对象还在使用中时,Hibernate的Session必须保持打开状态。当尝试通过DAO模式抽象持久层时,这会引起一个重要问题。为了充分
地抽象持久层,所有的数据库逻辑,包括打开、关闭Session都不能在应用层出现。最常见的是,这些逻辑隐藏在DAO的实现类中。快速和
差一些的方案是:避免采用DAO模式,在应用层中包含数据连接的逻辑。这在小应用中是可行的,但在大系统中,这会是一个设计缺陷,
它损害了应用的扩展性。
在Web层使用延迟加载
幸运的是,Spring框架已经提供了一个DAO模式结合Hibernate延迟加载的Web方案。对于任何不熟悉Spring框架结合Hibernate人来说,我
在这里不会深入细节,但是我希望你去阅读“结合Spring框架的Hibernate数据库访问”章节。这个案例是一个Web应用,Spring提供了
OpenSessionInViewerFilter和OpenSessionInViewInterceptor。使用它们中的任一个都能获得同样的功能。这两者唯一不同的是
interceptor在Spring容器中运行,并且在web应用的上下文中配置;fitler在Spring前运行,并且在web.xml中配置。不管使用哪一个,
他们都会在请求绑定到Session的当前线程期间打开Hibernate Session。一旦绑定到线程,打开的Hibernate Session能被DAO的实现类透
明地使用。Session会持续打开允许延迟加载访问数据库。一旦View逻辑完成,hibernate session会被关闭,无论是在Filter的doFilter
方法中还是在Interceptor的postHandle方法中。下面是一个配置实例:
Interceptor配置
<beans>
<bean id="urlMapping"    
     class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">   
       <property name="interceptors">
         <list>
              <ref bean="openSessionInViewInterceptor"/>
         </list>
       </property>
       <property name="mappings">
...
</bean>
...
<bean name="openSessionInViewInterceptor"
    class="org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor">
       <property name="sessionFactory"><ref bean="sessionFactory"/></property>
</bean>
</beans>
Filter配置
<web-app>
...     
<filter>
    <filter-name>hibernateFilter</filter-name>
    <filter-class>
      org.springframework.orm.hibernate.support.OpenSessionInViewFilter
    </filter-class>
   </filter>
...     
<filter-mapping>
    <filter-name>hibernateFilter</filter-name>
     <url-pattern>*.spring</url-pattern>
</filter-mapping>
...
</web-app>
使用打开的session的HibernateDAO实现类很简单。实际上,如果你已经使用Spring框架实现Hibernate的DAO对象,最有可能的是,你不
需要做任何改动。DAO必须通过方便的HibernateTemplate工具访问Hibernate,这对数据库访问来说就是小菜一碟。下面是一个DAO的实例

DAO实例
public class HibernateProductDAO extends HibernateDaoSupport implements ProductDAO {     
       public Product getProduct(Integer productId) {
              return (Product)getHibernateTemplate().load(Product.class, productId);
       }
       public Integer saveProduct(Product product) {
              return (Integer) getHibernateTemplate().save(product);
       }      
       public void updateProduct(Product product) {
              getHibernateTemplate().update(product);
       }
}
在业务层使用延迟加载
甚至在表现层外,Spring框架也通过AOP拦截器HibernateInterceptor提供了便利的延迟加载支持。hibernate拦截器透明地拦截了配置在
Spring应用上下文中的业务对象的调用,在调用前打开hibernate session,在调用结束时关闭这个session。让我们通过一个简单的例子
来说明。假设我们有一个interface叫做BussinessObject:
public interface BusinessObject {
     public void doSomethingThatInvolvesDaos();
}
类BusinessObjectImpl实现了BusinessObject接口:
public class BusinessObjectImpl implements BusinessObject {
    public void doSomethingThatInvolvesDaos() {
        // lots of logic that calls
        // DAO classes Which access
        // data objects lazily
    }
}
通过Spring上下文的一些配置,我们可以让HibernateInterceptor拦截对BusinessObjectImpl的调用,允许它的方法延迟访问数据对象。
看一下下面的片断:
<beans>
    <bean id="hibernateInterceptor" class="org.springframework.orm.hibernate.HibernateInterceptor">
         <property name="sessionFactory">
           <ref bean="sessionFactory"/>
         </property>
    </bean>
    <bean id="businessObjectTarget" class="com.acompany.BusinessObjectImpl">
       <property name="someDAO"><ref bean="someDAO"/></property>
    </bean>
    <bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
         <property name="target"><ref bean="businessObjectTarget"/></property>
         <property name="proxyInterfaces">
           <value>com.acompany.BusinessObject</value>
         </property>
         <property name="interceptorNames">
           <list>
              <value>hibernateInterceptor</value>
           </list>
         </property>
     </bean>           
</beans>
当businessObject的实例被引用,HibernateInterceptor打开一个hibernate session并允许对BussinessObjectImpl的调用。当
BusinessOjbectImpl执行完成,HibernateInterceptor透明的关闭这个session。应用代码并不知道任何持久层逻辑,但它仍然能够使用
延迟加载访问数据对象。
在单元测试中使用延迟加载
最后,我们要在JUnit中测试我们的延迟加载应用。覆盖TestCase类的setUp和tearDown方法非常容易。我更喜欢将这段代码放在一个简便
的抽象TestCase类中,作为我所有测试的基类。
public abstract class MyLazyTestCase extends TestCase {


        public void setUp() throws Exception {
     super.setUp();
     SessionFactory sessionFactory = (SessionFactory) getBean("sessionFactory");
     Session s = sessionFactory.openSession();
     TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s));
        }
        protected Object getBean(String beanName) {
            //Code to get objects from Spring application context
        }

        public void tearDown() throws Exception {
     super.tearDown();
     SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
     Session s = holder.getSession();
     s.flush();
     TransactionSynchronizationManager.unbindResource(sessionFactory);
     SessionFactoryUtils.closeSessionIfNecessary(s, sessionFactory);
        }