Spring / Hibernate惰性加载需要帮助 [英] Help needed with Spring/Hibernate Lazy-loading

查看:85
本文介绍了Spring / Hibernate惰性加载需要帮助的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道这已经讨论了很多次。我只是无法理解这项工作或者我的错误所在。

我认为给你一个简化的例子是向你展示我想要做什么以及我正在做什么假设的最佳方式...



我有一个带名称的Product类。



我的DAO:

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


我的服务界面:

  public interface ProductService {
//这个方法是事务性的,但是如果没有
@Transactional $则抛出相同的异常错误b $ b public Product getProduct();
@Transactional
public String getName(Product tp);
}

我的服务实施:

  public class ProductServiceImpl实现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);
//加载没有名称的产品
Product p = main.productService.getProduct();
//载入懒惰名称
System.out.println(main.productService.getName(p)); //在本行中抛出异常
}
public void setProductService(ProductService productService){
this.productService = productService;
}

public ProductService getProductService(){
return productService;
}

我的applicationContext.xml:

 <?xml version =1.0encoding =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 =dataSourceclass =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>
< / bean>

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

<! - 单个Hibernate SessionFactory的事务管理器(替代JTA) - >
< bean id =transactionManagerclass =org.springframework.orm.hibernate3.HibernateTransactionManager>
< property name =sessionFactory>< ref local =sessionFactory/>< / property>
< / bean>

< bean id =hibernateTemplateclass =org.springframework.orm.hibernate3.HibernateTemplate>
< property name =sessionFactory>< ref bean =sessionFactory/>< / property>
< property name =allowCreatevalue =true/>
< / bean>
< bean id =productDAOclass =product.model.data.ProductDAO>
< property name =sessionFactoryref =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  - 打开Hibernate Session 
11:59:57,775 [main] DEBUG org.hibernate.impl.SessionImpl - 打开会话时间戳:12749723977
11:59:57,777 [main] ERROR org.hibernate.LazyInitializationException - 无法初始化代理 - 无会话
org.hibernate.LazyInitializationException:无法初始化代理 - 无会话
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 Session Open?如果我是这样,为什么在加载产品对象后关闭会话?



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



是否有任何代码错误或者错误理解这个工作?

你知道任何简单的示例代码,我可以下载它来检查它是如何工作的吗?
Neuquino $ / $ b $问题是HibernateInterceptor在从 ProductService getProduct()返回后关闭会话。




这个拦截器在一个方法调用,关闭和删除之前将一个新的Hibernate Session绑定到线程之后在任何方法结果的情况下。如果已经有一个预先绑定的Session(例如来自HibernateTransactionManager或者周围的Hibernate拦截方法),那么拦截器就会参与其中。



并打开一个新的窗口调用 ProductService.getProductName()。新创建的会话不了解您在前一个会话中从数据库中获取的产品实体。



您有各种可能性来解决此问题:



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

2.)在调用 Product.getName()之前,您可以使用 Session.update(myProductEntity)将实体重新附加到活动会话c>在中ProductService.getProductName()



3)你可以把它全部包装在一个事务中,对 ProductService.getProduct() ProductService.getProductName()参与的方法调用。请参阅交易传播



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



5。)我认为您也可以直接使用AOP。你可以有一个方面保持会议打开一个任意的Joinpoint,但我没有真正的经验,不能更具体;)

希望有帮助...


I know that this has been discussed lot of times. I just can't understand how this work or where my mistake is.
I think giving you a reduced example is the best way to show you what I'm trying to do and what assumptions I'm taking...

I have a Product class with a name. The name is a String property that is lazy.

My DAO:

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

My Service Interface:

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);
}

My Service Implementation:

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();
    }
}

My Main class:

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;
    }

My 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>

The exception fragment:

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)

Am I right if I assume that the HibernateInterceptor keeps the Hibernate Session Open among the different calls? If I'm so, Why is the session closed after loading the product object?

I read somewhere that I also can use OpenSessionInViewInterceptor, but I can't make it work. How would you add that interceptor to this little example?

Is there any code mistake or missunderstanding on how this work?

Do you know any simple example code I can download to check how this work?

Thanks in advance, Neuquino

解决方案

the problem is that HibernateInterceptor closes the session after returning from ProductService getProduct()

From the API DOCs:

This interceptor binds a new Hibernate Session to the thread before a method call, closing and removing it afterwards in case of any method outcome. If there already is a pre-bound Session (e.g. from HibernateTransactionManager, or from a surrounding Hibernate-intercepted method), the interceptor simply participates in it.

and opens a new one for the follwing call to ProductService.getProductName(). The newly created session has no knowledge of the Product Entity which you fetched from the DB in the previous session.

you have various possibilities to resolve this:

1.) add a method which eagerly loads the names, and use it in the specific contexts, this is obviuos ;)

2.) you can reattach the entity to the active session using Session.update(myProductEntity) before calling Product.getName() in ProductService.getProductName()

3.) you can wrap it all in a transaction which the wrapped method calls to ProductService.getProduct() and ProductService.getProductName() participate in. See Transaction Propagation

4.) in a web application you can use an OpenSessionInViewFilter to keep the session open as long as the view gets created in your controllers

5.) i think you can also use AOP directly. you can have an aspect keep the session open on an arbitrary Joinpoint but i have no real experience there and can not be more specific ;)

hope that helped...

这篇关于Spring / Hibernate惰性加载需要帮助的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆