Spring / Hibernate Lazy-loading需要帮助

时间:2010-06-04 18:48:38

标签: hibernate spring lazy-loading interceptor

我知道很多次都讨论过这个问题。我无法理解这项工作是什么或我的错误在哪里 我想给你一个简单的例子是向你展示我正在尝试做什么以及我正在做什么假设的最佳方式......

我有一个带有名字的Product类。该名称是一个懒惰的String属性。

我的道:

public abstract class HibernateProductDAO extends HibernateDaoSupport implements ProductDAO
{
    public List getAll()
    {
        return this.getHibernateTemplate().find("from " + this.getDomainClass().getSimpleName());
    }
}

我的服务界面:

public interface ProductService {
    //This methods are Transactional, but same exception error is thrown if there weren't
    @Transactional
    public Product getProduct();
    @Transactional
    public String getName(Product tp);
}

我的服务实施:

public class ProductServiceImpl implements ProductService  {
    private ProductDAO productDAO;
    public Product getProduct() {
        List ps = this.productDAO.getAll();
        return (Product) ps.get(0);
    }
    public String getName(Product p){
        return p.getName();
    }
}

我的主要课程:

public class Main {
    private ProductService productService;
    public static void main(String[] args) {
        Main main= new Main();          
        main.productService= (ProductService)(new ClassPathXmlApplicationContext("applicationContext.xml")).getBean("productProxy");
        //load the product without the name
        Product p = main.productService.getProduct();
        //load the lazy name
        System.out.println(main.productService.getName(p));  //EXCEPTION IS THROWN IN THIS LINE
    }
    public void setProductService(ProductService productService) {
        this.productService= productService;
    }

    public ProductService getProductService() {
        return productService;
    }

我的applicationContext.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:util="http://www.springframework.org/schema/util"
   xmlns:jee="http://www.springframework.org/schema/jee"
   xmlns:lang="http://www.springframework.org/schema/lang"
   xmlns:aop="http://www.springframework.org/schema/aop"
   xmlns:tx="http://www.springframework.org/schema/tx"
   xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca">

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName"><value>oracle.jdbc.driver.OracleDriver</value></property>
        <property name="url"><value>jdbc:oracle:thin:@${hostname}:${port}:${schema}</value></property>
        <property name="username"><value>${username}</value></property>
        <property name="password"><value>${password}</value></property>
    </bean>

    <!-- Hibernate SessionFactory -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource"><ref local="dataSource"/></property>
        <property name="configLocation"><value>hibernate.cfg.xml</value></property>
        <property name="configurationClass"><value>org.hibernate.cfg.AnnotationConfiguration</value></property>
    </bean>

    <!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory"><ref local="sessionFactory"/></property>
    </bean>

    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory"><ref bean="sessionFactory"/></property>
        <property name="allowCreate" value="true"/>
    </bean> 
    <bean id="productDAO" class="product.model.data.ProductDAO" >
        <property name="sessionFactory" ref="sessionFactory"/>
</bean>
    <bean id="hibernateInterceptor"
      class="org.springframework.orm.hibernate3.HibernateInterceptor">
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
    </bean>
    <bean id="productService"
      class="product.services.ProductServiceImpl">
        <property name="productDAO">
            <ref bean="ProductDAO"/>
        </property>
    </bean>
    <bean id="productProxy"
      class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target">
             <ref bean="productService"/>
        </property>
        <property name="proxyInterfaces">
             <value>product.services.ProductService</value>
        </property>
        <property name="interceptorNames">
            <list>
                <value>hibernateInterceptor</value>
            </list>
        </property>
    </bean>
</beans>

异常片段:

11:59:57,775 [main] DEBUG org.springframework.orm.hibernate3.SessionFactoryUtils  - Opening Hibernate Session
11:59:57,775 [main] DEBUG org.hibernate.impl.SessionImpl  - opened session at timestamp: 12749723977
11:59:57,777 [main] ERROR org.hibernate.LazyInitializationException  - could not initialize proxy - no Session 
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:108)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:150)
    at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:150)

如果我假设HibernateInterceptor在不同的调用中保持Hibernate会话打开,我是对的吗?如果我是这样,为什么会在加载产品对象后关闭会话?

我在某处读到了我也可以使用OpenSessionInViewInterceptor,但我无法使其工作。你会如何将这个拦截器添加到这个小例子中?

是否有任何代码错误或对此工作的误解?

你知道我可以下载的任何简单的示例代码来检查这是如何工作的吗?

提前致谢, Neuquino

1 个答案:

答案 0 :(得分:6)

问题是HibernateInterceptor在从ProductService getProduct()

返回后关闭了会话

来自API DOCs

此拦截器在方法调用之前将新的Hibernate Session绑定到线程,然后在任何方法结果的情况下关闭并删除它。如果已经存在预绑定会话(例如来自HibernateTransactionManager,或来自周围的Hibernate截获方法),则拦截器只是参与其中。

并为ProductService.getProductName()的后续通话打开一个新的。新创建的会话不了解您在上一个会话中从数据库中提取的产品实体。

你有各种可能解决这个问题:

1。)添加一个急切加载名称的方法,并在特定的上下文中使用它,这是obviuos;)

2。)在Session.update(myProductEntity)

中调用Product.getName()之前,您可以使用ProductService.getProductName()将实体重新连接到活动会话

3。)你可以将它包装在一个包装方法调用ProductService.getProduct()ProductService.getProductName()参与的事务中。参见Transaction Propagation

4。)在Web应用程序中,只要在控制器中创建视图,就可以使用OpenSessionInViewFilter保持会话打开

5.)我认为您也可以直接使用AOP。你可以有一个方面让会话在一个任意的Joinpoint上保持开放但我没有真正的经验而且不能更具体;)

希望有所帮助......