Announcement Announcement Module
Collapse
No announcement yet.
The classic LazyInitializationException Problem :( Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • The classic LazyInitializationException Problem :(

    Hi all,

    Sorry for this yet-another-post on the classic "LazyInitializationProblem".
    I extensively searched through the forum, both Spring's and Hibernate,
    and googled the internet, but sorry to say, I could not find a single
    definitive reference or step-by-step guide like solution.

    They were many different views, and some were very confusing. I read about this
    "OpenSessionInView" pattern and the "Filter" thing. And at the end, I am still
    confused. Though some links like Karl Baum's Post were helpful in understanding
    the concept, but still, what exactly to-do, was missing.

    I would really appreciate, if someone could guide me through this maze.

    Just for the background, following are Spring-Config, .hbm.xml, the DAOImpl, and the
    PoJo files.

    I am using Hibernate v3.0.5 and Spring v1.2.3 and MySQL v4.1.
    Their is no Web or JUnit component, because I first like to get my this small-house in order.

    Please help !!!

    Thanks in advance,
    Vaibhav


    Spring Configuration File
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
        "http&#58;//www.springframework.org/dtd/spring-beans.dtd">
    
    <beans>
    	
    	<!-- Data Source Configuration -->
        <bean id="dataSource" 
        	class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
            <property name="url">
            	<value>jdbc&#58;mysql&#58;//localhost&#58;3306/cdcDB</value>
            </property>
            <property name="username" value="root"/>
    		<property name="password" value="quanta"/>
        </bean>
        
        <!-- Hibernate Properties -->
        <bean id="hibernateProperties" 
        	class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    		<property name="properties">
    			<props>
    				<prop key="hibernate.hbm2ddl.auto">update</prop>
    				<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
    				<prop key="hibernate.show_sql">true</prop>
    			</props>
    		</property>
    	</bean>
        
         <!-- Hibernate SessionFactory -->
        <bean id="sessionFactory" 
        	class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
            <property name="dataSource">
            	<ref local="dataSource"/>
            </property>
            <property name="hibernateProperties">
            	<ref local="hibernateProperties"/>
            </property>
            <property name="mappingResources">
                <list>
                    <value>Country.hbm.xml</value>
                    <value>State.hbm.xml</value>
                </list>
            </property>
        </bean>
        
        <!-- Transaction manager for a single Hibernate SessionFactory &#40;alternative to JTA&#41; -->
        <bean id="transactionManager" 
        	class="org.springframework.orm.hibernate3.HibernateTransactionManager">
       		 <property name="sessionFactory">
            	<ref local="sessionFactory"/>
            </property>
        </bean>
        
        <!-- This bean implements the functionality -->
    	<bean id="territorialDataServiceProvider" 
        	class="com.csam.ccp.cdc.service.realization.TerritorialDataServiceProvider">
             <property name="sessionFactory">
            	<ref local="sessionFactory"/>
            </property>
        </bean>
        
    	<!-- This bean is the interface -->
        <bean id="territorialDataService" 
            class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
            <property name="transactionManager">
            	<ref local="transactionManager"/>
            </property>
            <property name="target">
            	<ref local="territorialDataServiceProvider"/>
            </property>
            <property name="transactionAttributes">
            	<props>
    				<prop key="add*">PROPAGATION_REQUIRED</prop>
    			</props>
            </property>
        </bean>
        
    </beans>
    DAOImpl Implementation

    Code:
    package com.csam.ccp.cdc.service.realization;
    
    import org.hibernate.Session;
    import org.hibernate.Transaction;
    import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
    
    import com.csam.ccp.cdc.exceptions.NonExistentParentException;
    import com.csam.ccp.cdc.persistence.Country;
    import com.csam.ccp.cdc.persistence.State;
    import com.csam.ccp.cdc.service.TerritorialDataService;
    
    public class TerritorialDataServiceProvider extends HibernateDaoSupport
    implements TerritorialDataService 
    &#123;
    	public String addCountry&#40;&#41; 
    	&#123;
    		Country bharat = new Country&#40;&#41;;
    		
    		bharat.setLabel&#40;"India"&#41;;
    		
    		getHibernateTemplate&#40;&#41;.save&#40; bharat &#41;;
    		
    		return bharat.getCountryID&#40;&#41;.toString&#40;&#41;;
    	&#125;
    
    	public String addState&#40;String countryID, String label&#41;
    			throws NonExistentParentException 
    	&#123;
    		final Country bharat = 
    			&#40;Country&#41; getHibernateTemplate&#40;&#41;
    			.load&#40; Country.class, Long.valueOf&#40;countryID&#41; &#41;;
    		
    		if&#40; bharat == null &#41;
    		&#123;
    			throw new NonExistentParentException&#40;"The Country identified " +
    					"by the supplied countryID &#40;" + countryID + "&#41; " +
    							"does not exist"&#41;;
    		&#125;
    		
    		State stateToBeAdded = new State&#40;&#41;;
    		
    		stateToBeAdded.setLabel&#40; label &#41;;
    		
    		bharat.addState&#40; stateToBeAdded &#41;;
    		
    		session.save&#40; stateToBeAdded &#41;;
    		
    		session.update&#40; bharat &#41;;
    		
    		return stateToBeAdded.getStateID&#40;&#41;.toString&#40;&#41;;
    	&#125;
    
    	public Country getCountry&#40;String countryID&#41; 
    	&#123;
    		final Country bharat = &#40;Country&#41; getHibernateTemplate&#40;&#41;
    								.load&#40; Country.class, Long.valueOf&#40;countryID&#41; &#41;;
    		
    		return bharat;
    	&#125;
    
    	public State getState&#40;String stateID&#41; 
    	&#123;
    		final State state = 
    			&#40;State&#41; getHibernateTemplate&#40;&#41;
    			.load&#40; State.class, Long.valueOf&#40;stateID&#41; &#41;;
    		
    		return state;
    	&#125;
    &#125;
    Hibernate Mappings
    Country.hbm.xml
    Code:
    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http&#58;//hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
    
    <hibernate-mapping>
    	<class
    		name="com.csam.ccp.cdc.persistence.Country" table="COUNTRY"
    		dynamic-update="false" dynamic-insert="false">
    		<id name="countryID" column="COUNTRY_ID" type="java.lang.Long">
    			<generator class="native"/>
    		</id>
    		<property 
    			name="label" 
    			type="string" 
    			length="40"
    			not-null="true"/>
    		<set 
    			name="states"
    			inverse="true"
    			cascade="all-delete-orphan">
    			<key column="COUNTRY_ID"/>
    			<one-to-many class="com.csam.ccp.cdc.persistence.State"/>
    		</set>
    	</class>
    	<query name="findCountryByID">
    		<!&#91;CDATA&#91;  from Country country where country.countryID = &#58;countryID &#93;&#93;>
    	</query>
    </hibernate-mapping>
    State.hbm.xml
    Code:
    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http&#58;//hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
    
    <hibernate-mapping>
    	<class
    		name="com.csam.ccp.cdc.persistence.State" table="STATE" 
    		dynamic-update="false" dynamic-insert="false">
    		<id name="stateID" column="STATE_ID" type="java.lang.Long">
    			<generator class="native"/>
    		</id>
    		<property 
    			name="label" 
    			type="string" 
    			length="40"
    			not-null="true"/>
    		<many-to-one
    			name="country"
    			column="COUNTRY_ID"
    			class="com.csam.ccp.cdc.persistence.Country"
    			not-null="true" 
    			cascade="none"/>
    		<set 
    			name="districts"
    			inverse="true"
    			cascade="all-delete-orphan">
    			<key column="STATE_ID"/>
    			<one-to-many class="com.csam.ccp.cdc.persistence.District"/>
    		</set>
    	</class>
    	<query name="findStateByID">
    		<!&#91;CDATA&#91; from State state where state.stateID = &#58;stateID &#93;&#93;>
    	</query>
    </hibernate-mapping>
    PoJo
    Country.java
    Code:
    package com.csam.ccp.cdc.persistence;
    
    import java.util.HashSet;
    import java.util.Set;
    
    public class Country extends BaseObject
    &#123;
    	private static final long serialVersionUID = 1L;
    	
    	private Long 	countryID;
    	private String 	label;
    	private Set 	states = new HashSet&#40; 7 &#41;;
    	
    	protected void setCountryID&#40; Long countryID &#41;
    	&#123;
    		this.countryID = countryID;
    	&#125;
    	
    	public Long getCountryID&#40;&#41;
    	&#123;
    		return countryID;
    	&#125;
    	
    	public void setLabel&#40; String label &#41;
    	&#123;
    		this.label = label;
    	&#125;
    	
    	public String getLabel&#40;&#41;
    	&#123;
    		return label;
    	&#125;
    	
    	public void setStates&#40; Set states &#41;
    	&#123;
    		this.states = states;
    	&#125;
    	
    	public Set getStates&#40;&#41;
    	&#123;
    		return states;
    	&#125;
    	
    	public void addState&#40; State state &#41;
    	&#123;
    		if&#40; state == null &#41;
    		&#123;
    			throw new IllegalArgumentException&#40;"State reference is null."&#41;;
    		&#125;
    		
    		final Country country = state.getCountry&#40;&#41;;  
    		
    		if&#40; country != null &#41;
    		&#123;
    			country.getStates&#40;&#41;.remove&#40; state &#41;;
    		&#125;
    			
    		state.setCountry&#40; this &#41;;
    		
    		states.add&#40; state &#41;;
    	&#125;
    &#125;
    State.java
    Code:
    package com.csam.ccp.cdc.persistence;
    
    import java.util.HashSet;
    import java.util.Set;
    
    public class State extends BaseObject
    &#123;
    	private static final long serialVersionUID = 1L;
    	
    	private Long 				stateID;
    	private String 				label;
    	private Country 			country;
    	private Set 				districts = new HashSet&#40; 10 &#41;;
    	
    	protected void setStateID&#40; Long stateID &#41;
    	&#123;
    		this.stateID = stateID;
    	&#125;
    	
    	public Long getStateID&#40;&#41;
    	&#123;
    		return stateID;
    	&#125;
    	
    	public void setLabel&#40; String label &#41;
    	&#123;
    		this.label = label;
    	&#125;
    	
    	public String getLabel&#40;&#41;
    	&#123;
    		return label;
    	&#125;
    	
    	public void setCountry&#40; Country country &#41;
    	&#123;
    		this.country = country;
    	&#125;
    	
    	public Country getCountry&#40;&#41;
    	&#123;
    		return country;
    	&#125;
    	
    	public void setDistricts&#40; Set districts &#41;
    	&#123;
    		this.districts = districts;
    	&#125;
    	
    	public Set getDistricts&#40;&#41;
    	&#123;
    		return districts;
    	&#125;
    	
    	public void addDistrict&#40; District district &#41;
    	&#123;
    		if&#40; district == null &#41;
    		&#123;
    			throw new IllegalArgumentException&#40;"District reference is null."&#41;;
    		&#125;
    		
    		final State state = district.getState&#40;&#41;;  
    		
    		if&#40; state != null &#41;
    		&#123;
    			state.getDistricts&#40;&#41;.remove&#40; district &#41;;
    		&#125;
    			
    		district.setState&#40; this &#41;;
    		
    		districts.add&#40; district &#41;;
    	&#125;
    &#125;

  • #2
    It boils down to the following:

    Although the OpenSessionInViewFilter (OSIV) provided by spring avoids lazy loading issues by keeping the Hibernate session open while the view is rendering, it also brings with it a host of non trivial issues when updating data or dealing with transactions. So unless you're app is read only (just displays data loaded with Hibernate) or trivial, I suggest you stay away from OSIV.

    If you're not using OSIV you are ofcourse forced to eagerly load all of your lazy associations before giving the data to the view for rendering since the Hibernate sessions (and transactions for that matter) are managed at the service layer and will already have been closed when the view is rendering. So basically it will be the responsability of the controller to pull all data from the DB using Hibernate before giving it to the view for redering. There are several ways you can do this: using a callback that touches lazy associations, using specialized Hibernate queries with eager fetching, ...).

    Erwin

    Comment


    • #3
      Re: The classic LazyInitializationException Problem

      Originally posted by vaibhavkhattri
      [size=10]Hi all,

      Sorry for this yet-another-post on the classic "LazyInitializationProblem".
      I extensively searched through the forum, both Spring's and Hibernate,
      and googled the internet, but sorry to say, I could not find a single
      definitive reference or step-by-step guide like solution.

      They were many different views, and some were very confusing. I read about this
      "OpenSessionInView" pattern and the "Filter" thing. And at the end, I am still
      confused. Though some links like Karl Baum's Post were helpful in understanding
      the concept, but still, what exactly to-do, was missing.

      I would really appreciate, if someone could guide me through this maze.
      Hi,

      I'm solving the same problem, and have very similiar appconfig.

      My proposed solutions:
      1) set lazy = 'false' for all collections in hbm.xml files. This will ensure, that everything will be loaded immediately, and no LazyInitExceptions will occur.
      This is not a good solution, altought it works.

      2) ensure good transaction demarcation. Spring binds one session to one transaction, so collection with lazy-init can be initalized at any time in transaction. No LazyInitExceptions will occur. Probably this is the right solution.
      I just tested it, and it works! However, you need to set up the transaction boundaries correctly. This shouldn't be too hard, since you also use BeanNameAutoProxyCreator.

      Here is my excerpt from appconfig.xml, for inspiration:

      <!-- each method begining with tx or load on the proxied target, will be in transaction -->
      <bean id="txAttributes" class="org.springframework.transaction.interceptor .NameMatchTransactionAttributeSource">
      <property name="properties">
      <value>
      tx*=PROPAGATION_REQUIRED
      load*=PROPAGATION_REQUIRED
      </value>
      </property>
      </bean>


      <!-- here are the beans, which should be proxied for transaction -->
      <bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy .BeanNameAutoProxyCreator">
      <property name="interceptorNames">
      <value>txInterceptor</value>
      </property>
      <property name="beanNames">
      <value>*Service,*Control,transmissionDAO</value>
      </property>
      </bean>

      p.s.: I'm a spring beginner, so I could had incorrect statements.

      Comment


      • #4
        Re: The classic LazyInitializationException Problem

        sorry, just noticed, you don't use BeanNameAutoProxyCreator, however with TransactioProxyCreatorBean you can achive the same (check out sprin-doc, 7.4.1)

        Comment

        Working...
        X