views:

80

answers:

2

A little background: I'm using Spring and Hibernate to create a really simple domain/dao/service structure. I'm also using Autowiring to inject all my beans into their happy places.

While refactoring, I recently got the all-too-popular error message "could not initialize proxy - no Session" when trying to access a FetchType.LAZY property on my hibernate object. It goes something like this:

public class Person {

...

 @ManyToOne(cascade = {}, fetch = FetchType.LAZY)
 @JoinColumn(name = "pet_id", nullable = false)
 public Pet getPet() {
  return pet;
 }
...
}

I used to have a PersonService bean that accessed the Pet property, and had no trouble doing so. However, I recently refactored the code so that instead of the PersonService looking at the Pet, a PersonHelper looks at it. While my helper bean can see the PersonDao, can make a call to get the person, it cannot access the Pet as my session is closed.

So, I think that I'm unclear as to when I loose my hibernate session. All the configs look fine, and the DAO is getting injected into my helper, just like it used to get injected into my service. I'm not sure why my service could get the Pet just fine, but my helper cannot.

Any help with understand this "SessionFactory mystery" is well appreciated. I realize this might be a complicated subject, so links to some good reading material would rock.

I've since changed the code to FetchType.EAGER (working fine), but this riddle is burning a whole in my brain :).

Per requests, here is a (simplified) look into my config:

    <bean id="personSvc" class="org.comp.service.impl.PersonServiceImpl" />
    <bean id="personHelper" class="org.comp.service.helper.PersonHelper" />

    <bean id="personDao" class="org.comp.dao.hibernate.HibPersonDaoImpl">
        <property name="sessionFactory">
            <ref bean="sessionFactory" />
        </property>
    </bean>

...

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource">
            <ref bean="dataSource" />
        </property>
        <property name="packagesToScan" value="org.comp.domain"/>
        <property name="schemaUpdate" value="true" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.show_sql">false</prop>

                <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider
                </prop>
                <prop key="hibernate.cache.provider_configuration_file_resource_path">/hibernate-ehcache.xml</prop>
            </props>
        </property>
    </bean>
<bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory">
        <ref bean="sessionFactory"/>
    </property>
</bean>

DAO is autowired into the helper:

@Autowired
private PersonDao personDao;
+3  A: 

Without seeing the code/context config for PersonService, I can only guess as to why it worked before the refactoring. If you're using a HibernateInterceptor to wrap session management around your DAO methods, then the session is closed immediately after the method has finished, unless it was opened beforehand (such as by a OpenSessionInViewFilter).

My guess is that the scope of the HibernateInterceptor may have changed during refactoring so that the session is now closed immediately after the data is fetched. You may want to look at extending the scope of HibernateInterceptor to cover your service/business methods, so that the session is kept around long enough for lazy fetching to work, or, alternatively, use the OpenSessionInViewFilter, which ensures the session is always available.

mdma
Very interesting. I'm going to look into filters, as this is something I don't know much about.
Stephano
Added in some config that I'm using. The AnnotationSessionFactoryBean might look a little strange as I'm trying to simplify that idea down for this post (it's a bit more complex in practice)
Stephano
@Stephano - Thanks for the update. I guess your DAO is implemented using HibernateDaoSupport? From the details here, I don't see how it would have worked before. You may have simply been lucky! (Or more realisticly, there are further details that would clear up the mystery. E.g. is there any kind of transaction management?) It may be that your DAOs were calling other DAOs, and so the session was kept open for the duration of the first call, which spans the nested calls. Related: http://stackoverflow.com/questions/2145024/lazy-loading-with-spring-hibernatedaosupport
mdma
I think you nailed it. I have some AOP going on (that I don't really understand yet). It would seem that because my service wasn't included there, things didn't work. I need to go google BeanNameAutoProxyCreator... but thank you for all your help :) .
Stephano
AOP is the way to go - there are some that consider the HibernateDaoSupport to be old hat. AOP requires more "cerebral investment" but it pays back with great flexibility.
mdma
If by cerebral investment you mean coffee... I'm on it!
Stephano
+1  A: 

OSIV is always a must-read be it you are writing a web-based application or not.

I use the @Transactional tag in my Spring beans' methods (at the service layer) and let Spring to manage the session for me that way (Spring handles Hibernate's session per thread by default).

yclian
+1 for throwing down two very useful links
Stephano