Announcement Announcement Module
Collapse
No announcement yet.
Hibernate Sessions in Spring? ThreadLocal Pattern? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Hibernate Sessions in Spring? ThreadLocal Pattern?

    Hallo -- I am getting this error when I try to read a number of Objects from Hibernate using getHibernateTemplate().find():
    org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: ukco.spint.domain.customer.Customer.customerCustom erRecords - no session or session was closed
    at org.hibernate.collection.AbstractPersistentCollect ion.throwLazyInitializationException(AbstractPersi stentCollection.java:191)
    at org.hibernate.collection.AbstractPersistentCollect ion.initialize(AbstractPersistentCollection.java:1 83)
    at org.hibernate.collection.AbstractPersistentCollect ion.read(AbstractPersistentCollection.java:48)
    at org.hibernate.collection.PersistentSet.iterator(Pe rsistentSet.java:134)
    at ukco.spint.domain.customer.Customer.toString(Custo mer.java:114)
    at java.lang.String.valueOf(Unknown Source)
    at java.io.PrintStream.print(Unknown Source)
    at java.io.PrintStream.println(Unknown Source)
    at ukco.spint.domain.test.TestCustomer.getCustomers(T estCustomer.java:58)
    at ukco.spint.domain.test.TestCustomer.main(TestCusto mer.java:16)
    11:49:38,734 ERROR LazyInitializationException:19 failed to lazily initialize a collection of role: ukco.spint.domain.customer.Customer.customerCustom erRecords - no session or session was closed
    org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: ukco.spint.domain.customer.Customer.customerCustom erRecords - no session or session was closed
    at org.hibernate.collection.AbstractPersistentCollect ion.throwLazyInitializationException(AbstractPersi stentCollection.java:191)
    at org.hibernate.collection.AbstractPersistentCollect ion.initialize(AbstractPersistentCollection.java:1 83)
    at org.hibernate.collection.AbstractPersistentCollect ion.read(AbstractPersistentCollection.java:48)
    at org.hibernate.collection.PersistentSet.iterator(Pe rsistentSet.java:134)
    at ukco.spint.domain.customer.Customer.toString(Custo mer.java:114)
    at java.lang.String.valueOf(Unknown Source)
    at java.io.PrintStream.print(Unknown Source)
    at java.io.PrintStream.println(Unknown Source)
    at ukco.spint.domain.test.TestCustomer.getCustomers(T estCustomer.java:58)
    at ukco.spint.domain.test.TestCustomer.main(TestCusto mer.java:16)
    So I guess my questoin is how do I keep a session open to read Objects from?
    And is there a Spring version of that ThreadLocal pattern that the Hibernate documentation suggests to simplify access to data?
    Any & all help greatly appreciated.
    doug.

  • #2
    The problem is that you are traversing a lazy loaded relationship after the session has been closed.

    One workaround is to use http://www.springframework.org/docs/...iewFilter.html

    HTH.

    Comment


    • #3
      Cheers for that -- I guess that means I've a lot to learn about using Hibernate through Spring!
      Just for reference -- where does the session begin & end with the getHibernateTemplate()?
      Just in that call?
      That would mean that explicitly loading class members only when they are needed would be impossible, wouldn't it?
      & thanks again for your help,
      doug.

      Comment


      • #4
        Originally posted by biot023
        Cheers for that -- I guess that means I've a lot to learn about using Hibernate through Spring!
        Just for reference -- where does the session begin & end with the getHibernateTemplate()?
        Just in that call?
        That would mean that explicitly loading class members only when they are needed would be impossible, wouldn't it?
        & thanks again for your help,
        doug.
        I have to say, sometimes is really boring seeing posting users about the same problem. I'm sure, If you had googled a little-bit, or read the docs, you'll find
        the answer in a shorter time, than waiting here for answer.
        Just searching this forum for 'LazyInitializationException' returned 20 matches!
        However, hopefully the last time, I'm putting here the ultimate link about this problem. hope, you can solve this problem now!

        http://www.jroller.com/comments/kbau...ation_with_dao

        Comment


        • #5
          Or check the samples inside the Spring framework.

          Comment


          • #6
            Doing the search like you suggested did indeed produce alot of results -- I can only apologise & offer the explanation that I am trying to get to grips w/ a few technologies simultaneously (Spring, Hibernate, JSF...)
            However, having read through the posts here, Googled for any additional information, and read the reference *.pdf on Spring & Hibernate, I'm afraid I'm not really any the wiser, and therefore must be missing something *really* obvious.
            After my DAO has returned an Object, there are a few things that just don't make sense to me:
            * Class A contains a Class B. The relationship is set to lazy load, yet the function getB() still returns the corrent instance of Class B.
            * Class A also contains a Set of Class C. I can explicitly load this in the DAO just fine, but I still get the lazy load problem when I try to read this list once the Class A object has returned.

            I will include the code for AbstractDao that I have written -- hopefully it will indicate to someone just where exactly I have diverged so far from reality!
            Code:
            package ukco.spint.dao;
            
            import java.lang.reflect.InvocationTargetException;
            import java.lang.reflect.Method;
            import java.util.LinkedList;
            import java.util.List;
            
            import org.springframework.orm.hibernate3.HibernateTemplate;
            import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
            
            public abstract class AbstractDao<domainObject>
            extends HibernateDaoSupport implements Dao<domainObject> &#123;
            	
            	private List<String> preloadProperties=new LinkedList<String>&#40;&#41;;
            	
            	public abstract domainObject getNewInstance&#40;&#41;;
            
            	@SuppressWarnings&#40;"unchecked"&#41;
            	public domainObject getById&#40;long id&#41; throws PreloadPropertyException &#123;
            		HibernateTemplate ht=getHibernateTemplate&#40;&#41;;
            		domainObject obj=&#40;domainObject&#41;ht.load&#40;getDaoClass&#40;&#41;,new Long&#40;id&#41;&#41;;
            		doPreload&#40;ht,obj&#41;;
            		return obj;
            	&#125;
            	
            	@SuppressWarnings&#40;"unchecked"&#41;
            	public List<domainObject> getAll&#40;&#41; throws PreloadPropertyException &#123;
            		HibernateTemplate ht=getHibernateTemplate&#40;&#41;;
            		List lst=getHibernateTemplate&#40;&#41;.find&#40;"from "+getDaoClass&#40;&#41;.getName&#40;&#41;&#41;;
            		List<domainObject> objs=new LinkedList<domainObject>&#40;&#41;;
            		for&#40;Object obj&#58;lst&#41;&#123;
            			doPreload&#40;ht,&#40;domainObject&#41;obj&#41;;
            			objs.add&#40;&#40;domainObject&#41;obj&#41;;
            		&#125;
            		return objs;
            	&#125;
            	
            	public void save&#40;domainObject obj&#41; &#123;
            		getHibernateTemplate&#40;&#41;.saveOrUpdate&#40;obj&#41;;
            	&#125;
            
            	public void delete&#40;domainObject obj&#41; &#123;
            		getHibernateTemplate&#40;&#41;.delete&#40;obj&#41;;
            	&#125;
            
            	@SuppressWarnings&#40;"unchecked"&#41;
            	public void delete&#40;long id&#41; &#123;
            		domainObject obj=&#40;domainObject&#41;getHibernateTemplate&#40;&#41;.load&#40;getDaoClass&#40;&#41;,new Long&#40;id&#41;&#41;;
            		delete&#40;obj&#41;;
            	&#125;
            	
            	public void addPreloadProperty&#40;String propName&#41; &#123;
            		String getterName=propertyNameToGetterName&#40;propName&#41;;
            		if&#40;!preloadProperties.contains&#40;getterName&#41;&#41;
            			preloadProperties.add&#40;getterName&#41;;
            	&#125;
            	public void removePreloadProperty&#40;String propName&#41; &#123;
            		String getterName=propertyNameToGetterName&#40;propName&#41;;
            		if&#40;preloadProperties.contains&#40;getterName&#41;&#41;
            			preloadProperties.remove&#40;getterName&#41;;
            	&#125;
            	public void addAllPreloadProperties&#40;&#41;&#123;
            		Class c=getDaoClass&#40;&#41;;
            		Method&#91;&#93; methods=c.getMethods&#40;&#41;;
            		for&#40;Method m&#58;methods&#41;&#123;
            			String methodName=m.getName&#40;&#41;;
            			if&#40;methodName.substring&#40;0,3&#41;.equals&#40;"get"&#41;&#41;
            				preloadProperties.add&#40;methodName&#41;;
            		&#125;
            	&#125;
            	public void clearPreloadProperties&#40;&#41;&#123;
            		preloadProperties.clear&#40;&#41;;
            	&#125;
            	
            	protected void doPreload&#40;HibernateTemplate ht,domainObject obj&#41;
            	throws PreloadPropertyException&#123;
            		if&#40;preloadProperties.size&#40;&#41;==0&#41;
            			return;
            		Class c=obj.getClass&#40;&#41;;
            		try&#123;
            			for&#40;String methodName&#58;preloadProperties&#41;&#123;
            				Method getter=c.getMethod&#40;methodName,new Class&#91;&#93;&#123;&#125;&#41;;
            				//getter.invoke&#40;obj,new Object&#91;&#93;&#123;&#125;&#41;;
            				//TODO -- replace the 2 lines below w/ the one above
            				Object test=getter.invoke&#40;obj,new Object&#91;&#93;&#123;&#125;&#41;;
            				test=null;
            			&#125;
            		&#125;catch&#40;NoSuchMethodException nsme&#41;&#123;
            			throw new PreloadPropertyException&#40;
            					"No such method name in class '"+c.getName&#40;&#41;+"'.",nsme&#41;;
            		&#125;catch&#40;IllegalArgumentException iae&#41;&#123;
            			throw new PreloadPropertyException&#40;
            					"Method has arguments in class '"+c.getName&#40;&#41;+"'.",iae&#41;;
            		&#125;catch&#40;IllegalAccessException iae&#41;&#123;
            			throw new PreloadPropertyException&#40;
            					"Cannot access the property in class '"+c.getName&#40;&#41;+"'.",iae&#41;;
            		&#125;catch&#40;InvocationTargetException ite&#41;&#123;
            			throw new PreloadPropertyException&#40;
            					"Cannot invoke the property in class '"+c.getName&#40;&#41;+"'.",ite&#41;;
            		&#125;
            	&#125;
            	
            	protected static String propertyNameToGetterName&#40;String propName&#41;&#123;
            		StringBuffer buf=new StringBuffer&#40;"get"&#41;;
            		buf.append&#40;propName.substring&#40;0,1&#41;.toUpperCase&#40;&#41;&#41;;
            		buf.append&#40;propName.substring&#40;1&#41;&#41;;
            		return buf.toString&#40;&#41;;
            	&#125;
            	
            	public abstract Class getDaoClass&#40;&#41;;
            &#125;
            Again, sorry if this is very old news, but I promise I've been trying to figure it out!
            & cheers for replies so far,
            doug.

            Comment


            • #7
              Seeing your mapping files would be useful

              Your problem, as stated is that you are trying to traverse relationships that are marked as lazy without a session.

              You can either prevent these being lazy, or ensure that there is always a session. OSIV is one way to ensure there is always a session.

              You must be aware that hibernate implements lazy loading using either cglib or jdk 1.4 proxies. It can only use proxies if you expose a public interface, otherwise it will use cglib. If your class is final, or contains any final methods, it cannot be proxied with cglib. When you look at ClassA, the member variable for the set of ClassC will not be null, it will (if configured correctly) contain a lazyLoading proxy which implements the Collection (or Set etc.) interface. When you interrogate the collection, hibernate will load all the children.

              HTH.

              P.S. Mappings, and stacktraces are vital.

              Comment


              • #8
                Cheers for the response -- what I was trying to do was explicitly load the children within the DAO *before* the session ended, but this evidently didn't work!
                I'll have look into OSIV, now, but for reference here are the mapping files for the classes I was trying to get working.

                The "Class A":
                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="ukco.spint.domain.customer.Customer" table="customer">
                        <meta attribute="class-description">
                            this is a composite of CustomerRecords, and is the class that relates
                            to the job, properties, etc.
                        </meta>
                        
                        <!-- id -->
                        <id name="id" type="long" column="id">
                            <meta attribute="scope-set">protected</meta>
                            <generator class="native" />
                        </id>
                        
                        <!-- version -->
                        <version name="version" type="long">
                            <meta attribute="scope-set">protected</meta>
                        </version>
                        
                        <!-- name -->
                        <property name="name" type="string" not-null="true" />
                        
                        <!-- customerCustomerRecords -->
                        <set name="customerCustomerRecords" table="lnk_customer2customer_record"
                            inverse="true"
                            cascade="save-update">
                            <key column="customer_id" />
                            <one-to-many class="ukco.spint.domain.customer.CustomerCustomerRecord" />
                        </set>
                        
                        <!-- job -->
                        <one-to-one name="job" class="ukco.spint.domain.job.Job" lazy="true" />
                        
                    </class>
                </hibernate-mapping>
                The class in the Set is called CustomerCustomerRecord (as it is part of two 1:N mappings to model a M:N) & looks like this:
                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="ukco.spint.domain.customer.CustomerCustomerRecord"
                            table="customer_customer_record">
                        <meta attribute="class-description">
                            this class models the many-to-many relationship between the Customer class
                            and the CustomerRecord class, as well as any pertinent relationship data.
                        </meta>
                        
                        <!-- id -->
                        <id name="id" type="long" column="id">
                            <meta attribute="scope-set">protected</meta>
                            <generator class="native" />
                        </id>
                        
                        <!-- version -->
                        <version name="version" type="long">
                            <meta attribute="scope-set">protected</meta>
                        </version>
                        
                        <!-- customer -->
                        <many-to-one name="customer"
                            class="ukco.spint.domain.customer.Customer"
                            column="customer_id" />
                        
                        <!-- customerRecord -->
                        <many-to-one name="customerRecord"
                            class="ukco.spint.domain.customer.CustomerRecord"
                            column="customer_record_id" />
                        
                        <!-- rank -->
                        <property name="rank"
                            type="long"
                            column="rank" />
                        
                        <!-- comment -->
                        <property name="comment"
                            type="string"
                            column="comment" />
                        
                    </class>
                </hibernate-mapping>
                And the class that seems to load no matter what I do is the Job:
                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="ukco.spint.domain.job.Job" table="job">
                		<meta attribute="class-description">
                			a Job is the actual container for properties to be worked
                		</meta>
                		
                		<!-- id -->
                		<id name="id" type="long" column="id">
                			<meta attribute="scope-set">protected</meta>
                			<generator class="native" />
                		</id>
                		
                		<!-- version -->
                		<version name="version" type="long">
                			<meta attribute="scope-set">protected</meta>
                		</version>
                		
                		<!-- parent -->
                		<many-to-one name="parent"
                				class="ukco.spint.domain.job.Job"
                				column="parent_id" />
                		
                		<!-- children -->
                		<set name="children" inverse="true">
                			<key column="id" />
                			<one-to-many class="ukco.spint.domain.job.Job" />
                		</set>
                		
                		<!-- customer -->
                		<many-to-one name="customer" class="ukco.spint.domain.customer.Customer"
                				column="customer_id" unique="true" />
                		
                		<!-- jobNumber -->
                		<property name="jobNumber"
                				type="string"
                				column="job_number"
                				not-null="true" />
                		
                		<!-- description -->
                		<property name="description" type="string" not-null="true" />
                		
                		<!-- contacts -->
                		<set name="contacts" table="lnk_job2contact" inverse="true">
                			<key column="job_id" />
                			<one-to-many class="ukco.spint.domain.job.JobContact" />
                		</set>
                
                	</class>
                </hibernate-mapping>
                I don't get why my approach didn't work, but I rather suspect this comes from a very imperfect understanding of what Spring is doing w/ Hibernate.
                If anyone could help explain a little, I'd be very grateful -- and thanks for the continued input!
                doug.

                Comment


                • #9
                  Sessions are not opened and closed around your dao methods (they are methods like all others, how should spring know you want them to have some special hibernate session semantics?) it's opened and closed in every HibernateTemplate method. If you want to do multiple operations with one session either use HibernateTemplate.execute() or transactions.

                  Comment


                  • #10
                    Thanks for that -- I'm googling now, but if anyone has any links to tutorials, etc. on this, they'd be very helpful.
                    & thanks again for everyone's help.
                    doug.

                    Comment


                    • #11
                      use default-lazy=false

                      Hi,

                      this maybe solve your problem. In Hibernate3 lazy-loading is default. So if your object-graphs are not big insert this on your mapping-files:

                      <hibernate-mapping default-lazy="false">

                      Then the whole object will be loaded. So you can traverse the graph also session is closed before.
                      Maybe you will will los a bit of performance, but first make it run, then make it run better or faster.

                      Jörg Bellmann

                      Comment


                      • #12
                        re:

                        OK, so you've read some literature. please tell us, which steps did you make, to solve this problem.

                        On the blog (the link I posted) is mentioned, that business logic should be wrapped into transactions. This has a graceful side-effect: spring bounds one session/transaction. This means, that if any code will access a lazy collection within the given transaction, no lazy exceptions should occur.

                        I think, you're searching for the solution a little-bit on a wrong place.

                        As I saw your exception, it's thrown in some test. You should know, that spring opens a NEW session/dao operaation (i know, i'm quite unprecise here:-), so it's not possible to initialize a lazy-colelction in other session.

                        I recommend you to read once more that blog. Than consider, whether transactions are applicable in your case. If so, wrap your business methods into transactions (look at the samples). You'll se, than NO lazy-exceptions will occur.

                        Comment


                        • #13
                          Cheers for all the responses -- it was the Hibernate 3 lazy loading thing that was throwing me -- thanks for that!
                          As for the transactions, I will be looking into these, as I suspect they will be a better solution -- it's just I don't have time to do this now.
                          I should imagine I'll have plenty of refactoring to do when I've had the time to understand them, though.
                          Thanks again,
                          doug.

                          Comment

                          Working...
                          X