I'm trying to make the switch to hibernate (using Spring config and HibernateTemplate), but I keep getting LazyInitializationExceptions all over the place.
Basically, I'm configuring hibernate thusly:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<description>Configuration file for avp-Dao (Hibernate config).</description>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>@@db.driver@@</value>
</property>
<property name="url">
<value>@@db.url@@</value>
</property>
<property name="username">
<value>@@db.user@@</value>
</property>
<property name="password">
<value>@@db.password@@</value>
</property>
</bean>
<bean id="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
<!--<property name="flushModeName" value="FLUSH_AUTO"/>-->
<property name="sessionFactory" ref="sessionFactory"/>
<property name="singleSession" value="true"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="mappingResources" ref="mappingResources"/>
<!-- <property name="connectionReleaseMode"><value>???</value></property> -->
<property name="hibernateProperties" ref="hibernateProperties"/>
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- Hibernate Properties -->
<bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.max_fetch_depth">3</prop>
<!--<prop key="flushMode">AUTO</prop>-->
<prop key="hibernate.hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.jdbc.batch_size">50</prop>
<!--<prop key="hibernate.connection.release_mode">auto</prop>-->
<!--<prop key="transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</prop>-->
<!-- Connection Pool -->
<prop key="hibernate.connection.pool_size">10</prop>
<prop key="hibernate.c3p0.min_size">1</prop>
<prop key="hibernate.c3p0.max_size">20</prop>
<prop key="hibernate.c3p0.timeout">1800</prop>
<prop key="hibernate.c3p0.max_statements">100</prop>
<!-- Use EHCache as secondary cache-->
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
<prop key="hibernate.cache.provider_configuration_file_resource_path">com/foo/avp/repository/hibernate/ehcache.xml</prop>
</props>
</property>
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<constructor-arg>
<ref bean="sessionFactory"/>
</constructor-arg>
<!-- <property name="allowCreate" value="false"/> -->
<!--<property name="flushModeName" value="FLUSH_AUTO"/>-->
</bean>
<bean id="hibernateTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<constructor-arg>
<ref bean="sessionFactory"/>
</constructor-arg>
</bean>
<!-- hibernate mapping files (in avp.hibernate JAR) -->
<bean id="mappingResources" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list>
<value>com/foo/avp/model/Category.hbm.xml</value>
<value>com/foo/avp/model/CategoryProperty.hbm.xml</value>
<value>com/foo/avp/model/Channel.hbm.xml</value>
<value>com/foo/avp/model/ChannelGroup.hbm.xml</value>
<value>com/foo/avp/model/ChannelProperty.hbm.xml</value>
<value>com/foo/avp/model/Media.hbm.xml</value>
<value>com/foo/avp/model/MediaFile.hbm.xml</value>
<value>com/foo/avp/model/Role.hbm.xml</value>
<value>com/foo/avp/model/RssAccount.hbm.xml</value>
<value>com/foo/avp/model/User.hbm.xml</value>
<value>com/foo/avp/model/UserChannelPermission.hbm.xml</value>
</list>
</property>
</bean>
<!-- Dao layer -->
<bean id="categoryRepository" class="com.foo.avp.repository.hibernate.CategoryRepositoryImpl">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"/>
</property>
</bean>
<bean id="channelRepository" class="com.foo.avp.repository.hibernate.ChannelRepositoryImpl">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"/>
</property>
</bean>
<bean id="mediaRepository" class="com.foo.avp.repository.hibernate.MediaRepositoryImpl">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"/>
</property>
</bean>
<bean id="rssAccountRepository" class="com.foo.avp.repository.hibernate.RssAccountRepositoryImpl">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"/>
</property>
</bean>
<bean id="userRepository" class="com.foo.avp.repository.hibernate.UserRepositoryImpl">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"/>
</property>
</bean>
<bean id="userChannelPermissionRepository" class="com.foo.avp.repository.hibernate.UserChannelPermissionRepositoryImpl">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"/>
</property>
</bean>
<bean id="channelPropertyRepository" class="com.foo.avp.repository.hibernate.ChannelPropertyRepositoryImpl">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"/>
</property>
</bean>
<bean id="categoryPropertyRepository" class="com.foo.avp.repository.hibernate.CategoryPropertyRepositoryImpl">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"/>
</property>
</bean>
<bean id="roleRepository" class="com.foo.avp.repository.hibernate.RoleRepositoryImpl">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"/>
</property>
</bean>
<bean id="channelGroupRepository" class="com.foo.avp.repository.hibernate.ChannelGroupRepositoryImpl">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"/>
</property>
</bean>
</beans>
That OSIV interceptor was a last desperate attempt to solve this issue, but doesn't appear to have any discernible effect.
The whole repository / DAO layer, as well as the entities it returns, is defined in a separate JAR. I don't want the client application to be aware of Hibernate, or any special transaction / session considerations, which the Spring HibernateTemplate is supposed to take care of.
Problem: Each ChannelGroup entity contains a child Set of Channel objects, which is lazy-initialized when the ChannelGroup is returned by the Dao. Within the client application, any time I attempt to access members of the Channel set, I receive
Code:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.foo.avp.model.ChannelGroup.channels, no session or session was closed
As I understand it, this is because, after returning the retrieved ChannelGroup entity, the HibernateTemplate (or whatever) closes the session. So basically the entity is disconnected from the session. This means I can't browse child collections or objects, can't update the entity, or perform any meaningful data-backed operation.
From my perusals on the internet, either no one in the history of the world has ever solved this problem, which begs the question: why the hell would anyone want to use Hibernate, or else
everyone but me has solved this problem. In which case, I beg you to share your solution.
Is there a way to "re-attach" the entity to a hibernate session, to complete the lazy-init? Can this be done in a way that the client application doesn't need to know that I'm using Hibernate?
Is lazy-init useful
at all?
And yes, I tried the n00b solution of setting all my entities to lazy="false". Of course Hibernate attempted to load the entire database into memory and ran out of Heap :-).