zoukankan      html  css  js  c++  java
  • 关于Hibernate延迟加载

    延迟加载就是在当真正需要数据的时候去执行数据加载操作,从而避免一些多余的性能开销(数据库查询)。在Hibernate3中提供了对实体对象、集合和属性的延迟加载。

    1.实体对象的延迟加载

    延迟加载实体对象只需在实体映射关系中将lazy属性设置为true,如下:

        <class name=”xxx” table=”xxx” lazy=”true”>  
            ……  
        </class>  

    通过一个中间代理对象,Hibernate实现了实体的延迟加载,只有当用户真正发起获得实体对象属性的动作时,才真正会发起数据库查询操作。所以实体的延迟加载是用通过中间代理类完成的,所以只有session.load()方法才会利用实体延迟加载,因为只有session.load()方法才会返回实体类的代理类对象。

    实体对象的延迟加载是通过代理类来实现的,当调用session.load()时,首先返回实体对象的一个代理类,当访问该实体的方法或属性时才真正执行数据加载。

    2.集合类型的延迟加载

    集合类型的延迟加载在实际应用中比较常见,比如在一对多关联中定义的集合属性,如下:

    <class name=”net.sample.entity.Product” table=”product”>  
            …..  

        <set name="productAttributes" table="PRODUCT_ATTRIBUTE" inverse="true" lazy="true">

            <cache usage=”read-only”/>  

            <key column=”product_id”/>

            <one-to-many class="net.sample.entity.ProductAttribute"/>

        </set>  
    </class>  

    通过将<set>元素的lazy属性设置为true就打开了集合类型的延迟加载特性。此时当访问实体对象时,Hibernate不会立即加载关联对象的数据集,只有当访问关联对象集合中的对象时,Hibernate才会加载相应的关联对象实体。

    在Hibernate中,集合类型的缓存是分两部分进行的,首先是实体的ID列表(即数据索引),然后才是实体对象。Hibermate在加载集合类型时,先查在缓存中查找数据索引,如果没有找到对应的数据索引就会发起一条Select语句的查询,取得符合条件的数据组装成实体对象集合和数据索引,并纳入缓存中,并返回实体对象集合。如果能找到数据索引,Hibernate就会从数据索引中取到ID列表,然后根据ID在缓存中取得相应的实体,如果缓存中不存在该实体,则发起select查询。

    在上面的配置中,我们采用了<cache usage=”read-only”/>配置,在这种策略下Hibernate将只会对数据索引进行缓存,而不会对集合中的实体对象进行缓存。那么在第二次再次加载关联实体时,Hibernate找到了对应实体的数据索引,但根据数据索引,却无法在缓存中找到对应的实体,此时Hibernate就会根据找到的数据索引再发起select SQL的查询操作,以取得相对应的实体对象。

    如果我们需要对集合类型中的实体也进行缓存,那么应该将cache设置为<cache usage=”read-write”/> ,此时Hibernate不光缓存了数据索引,同时还缓存在集合中的实体对象。

    3.属性延迟加载

    这个机制主要是为提高大数据对象的查询,比如表中存在java.sql.Clob类型字段,其中存放的是一个大数据量的信息,这种大数据对象的读取本身会带来一定的性能开销,而应用中却不是时常在使用该信息。此时,我们就可以通过属性延迟加载机制,来使我们只有当真正需要操作这个字段时,才去读取这个字段的数据。

    <class name=”net.sample.entity.Product” table=”product”>  
            …..  
            <property name=”productDescription” type=”java.sql.Clob” column=”product_description” lazy=”true”/>  

    </class>  

    通过对<property>元素的lazy属性设置true来开启属性的延迟加载。

    我们也可以使用注解方式来实现延迟加载,如下例,示例了属性和集合延迟加载:

        @Basic(fetch = FetchType.LAZY)

        @Column(name = " product_description", columnDefinition = "")

        protected byte[]productDescription;

        @OneToMany(fetch = FetchType.LAZY)

        @Column(name = " PRODUCT_ATTRIBUTE ")

        public Set getCategory() {

            return this. productAttributes;

        }

    延迟加载虽然可以带来更好的性能,但这种技术的有一个缺点:就它要求 Hibernate  Session要在对象使用的时候保持打开状态。所以在Spring+Hibernate的Web 应用中使用延迟加载时经常会遇上LazyInitializationException异常。

    Spring 提供了 OpenSessionInViewFilter 和 OpenSessionInViewInterceptor 来解决Web表现层延迟加载的情况。这两个类实现了相同的功能,不同的是OpenSessionInViewInterceptor在 Spring 容器中运行并被配置在 web 应用的上下文中,而OpenSessionInViewFilter在 Spring 之前运行并被配置在 web.xml 中。这两个类都实现了在一个页面请求时打开 Hibernate的Session,并一直保持这个Session,直到这个请求结束。请求结束时, Hibernate 会话就会在 Filter 的 doFilter 方法或者 Interceptor 的 postHandle 方法中被关闭。

    Interceptor的配置:
    <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>

    Filter的配置
      <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>*.c</url-pattern>
    </filter-mapping>

    Spring框架中还提供了通过AOP拦截器HibernateInterceptor方式来支持延迟加载。这个拦截器在进入方法调用之前为当前线程绑定一个新的Hibernate Session实例,在方法执行完毕之后关闭并移除。所以拦截器可以拦截业务对象的调用,在调用前打开hibernate session,在调用结束时关闭这个session。见下例子:
    public interface BaseService{
         ……
    }

    public class BusinessService implements BaseService {
        public void invoke() {
            // call the business logic
            // Access the DAO to get the data Objects
            // data objects lazily
        }
    }
    在Spring上下文配置中,我们可以通过HibernateInterceptor拦截对BusinessService的调用来支持延迟访问数据对象。如下:
        <bean id="hibernateInterceptor" class=" org.springframework.orm.hibernate3.HibernateInterceptor">
             <property name="sessionFactory">
               <ref bean="sessionFactory"/>
             </property>
        </bean>
        <bean id=" businessSerivceTarget" class="org.sample.BusinessService">
           <property name="baseDAO"><ref bean="baseDAO"/></property>
        </bean>
        <bean id="businessSerivce" class="org.springframework.aop.framework.ProxyFactoryBean">
             <property name="target"><ref bean="businessSerivceTarget"/></property>
             <property name="proxyInterfaces">
               <value>org.sample.BaseService</value>
             </property>
             <property name="interceptorNames">
               <list>
                  <value>hibernateInterceptor</value>
               </list>
             </property>
         </bean>           
    </beans>
    当businessSerivce的实例被引用时,HibernateInterceptor就会打开一个hibernate session。当BusinessService执行完成后,HibernateInterceptor将关闭这个session。调用者不需要知道持久层的细节就可以使用延迟加载访问数据对象。

    参考:Hibernate和Spring的延迟加载和DAO模式

  • 相关阅读:
    异常处理 try catch throw(C++)
    Kubernetes轻量级日志收集系统LokiStack
    第一章.java
    第四章.选择结构(二)
    java语法
    第三章if选择结构
    第二章.数据类型变量名和运算符
    【转载】跳槽七诫
    【转载】修改shell终端提示信息
    ubuntu11.10面板上输入法图标消失解决办法
  • 原文地址:https://www.cnblogs.com/jevo/p/3039228.html
Copyright © 2011-2022 走看看