2010-06-04 4 views
2

Je sais que cela a été discuté plusieurs fois. Je ne peux pas comprendre comment ce travail ou où est mon erreur.
Je pense que vous donner un exemple réduit est la meilleure façon de vous montrer ce que j'essaie de faire et quelles hypothèses je prends ...Besoin d'aide avec Spring/Hibernate Lazy-loading

J'ai une classe de produit avec un nom. Le nom est une propriété String paresseuse.

Mon OAC:

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

Mon interface de service:

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

Mon service Mise en œuvre:

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

Ma principale classe:

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

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

Le fragment d'exception:

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) 

Ai-je raison si je suppose que le HibernateInterceptor maintient la session hibernate ouverte entre les différents appels? Si oui, pourquoi la session est-elle fermée après le chargement de l'objet produit?

J'ai lu quelque part que je peux aussi utiliser OpenSessionInViewInterceptor, mais je ne peux pas le faire fonctionner. Comment ajouterais-tu cet intercepteur à ce petit exemple?

Y at-il une erreur de code ou une mauvaise compréhension de la façon dont cela fonctionne?

Connaissez-vous un exemple de code simple que je peux télécharger pour vérifier comment cela fonctionne?

Merci à l'avance, Neuquino

Répondre

6

le problème est que HibernateInterceptor ferme la session après le retour de ProductService getProduct()

De l'API DOCs:

Cet intercepteur se lie à une nouvelle session Mise en veille prolongée au fil avant un appel de méthode, fermant et supprimant par la suite en cas de résultat de méthode. S'il existe déjà une session pré-liée (par exemple à partir de HibernateTransactionManager ou d'une méthode interceptée par Hibernate environnante), l'intercepteur participe simplement à celle-ci.

et ouvre un nouveau pour l'appel suivant au ProductService.getProductName(). La session nouvellement créée n'a aucune connaissance de l'entité de produit que vous avez récupérée à partir de la base de données dans la session précédente.

vous avez différentes possibilités pour résoudre ce:

1.) ajouter une méthode qui charge avec impatience les noms, et l'utiliser dans les contextes spécifiques, c'est obviuos;)

2.) Vous pouvez refixer l'entité à la session active à l'aide Session.update(myProductEntity) avant d'appeler Product.getName() dans ProductService.getProductName()

3.) vous pouvez envelopper le tout dans une transaction qui la méthode enveloppées appelle à ProductService.getProduct() et ProductService.getProductName() participer. Voir Transaction Propagation

4.) dans une application Web, vous pouvez utiliser un OpenSessionInViewFilter pour garder la session ouverte tant que la vue est créée dans vos contrôleurs

5.) Je pense que vous pouvez également utiliser AOP directement. vous pouvez avoir un aspect garder la session ouverte sur un Joinpoint arbitraire mais je n'ai aucune expérience réelle là et je ne peux pas être plus spécifique;)

espérons que cela a aidé ...