-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 
Author Message
 Post subject: Lazy loading with detached objects
PostPosted: Sat Jul 23, 2005 10:29 am 
Regular
Regular

Joined: Wed May 11, 2005 11:57 pm
Posts: 80
I am running into a problem when trying to reattach an object to a new session, and then navigate its associations. The problem seems to occur in the following conditions:

* The object (A) is loaded in session1, along with some related objects (B). After doing some work, session1 is closed.
* After the user makes a selection, a new session2 is started, and A is reattached by calling session2.update(A).
* The related B objects are not attached to session2, so calling A.getB() works, but A.getB().getC() fails, because B is still attached to session1, which is closed.

I am running Hibernate 2.1.8 (not ready to switch to 3 yet, unless it is known to solve this problem).

Note that there is no cascading configured in this mapping file - I tried setting cascade to 'save-update' on the courses relationship, which did fix the problem. However, Hibernate in Action specifically recommends against using cascading relationships in the entire object graph (for obvious performance reasons), and I need to be able to safely navigate any association from a persistent object.

The conclusion that I am reaching is that if a persistent object might have related objects loaded in a previous session, then it is unsafe to navigate associations, even while the object's session is open, unless those associations have been set to cascade update operations. If this conclusion is correct, that seems like an unacceptable restriction.

Here is a test case that illustrates the problem:

Code:
<hibernate-mapping package="com.prosc.test">
   <class lazy="true" name="Instructor">
      <id name="id" type="java.lang.Long" unsaved-value="null">
         <generator class="native"/>
      </id>
      <property name="firstName" type="java.lang.String"/>
      <property name="lastName" type="java.lang.String"/>
      <property name="title" type="java.lang.String"/>
      <list name="courses" lazy="true" >
         <key column="instructorId"/>
         <index column="sortOrderForinstructor"/>
         <one-to-many class="Course" />
      </list>
   </class>
</hibernate-mapping>

<hibernate-mapping package="com.prosc.test">
   <class lazy="true" name="Course">
      <id name="id" type="java.lang.Long" unsaved-value="null">
         <generator class="native"/>
      </id>
      <property name="name" type="java.lang.String"/>
      <property name="courseNumber" type="java.lang.String"/>
      <property name="courseDescription" type="java.lang.String"/>
      <many-to-one name="instructor" column="instructorId"/>
      <list name="students" lazy="true" table="JoinCourseStudent">
         <key column="coursesId"/>
         <index column="sortOrderForcourses"/>
         <many-to-many column="studentsId" class="Student"/>
      </list>
   </class>
</hibernate-mapping>

public class DetachedObjectsTest extends TestCase {
   public void testDetachObjects() throws HibernateException {
      Session session1 = TestUtils.sessionFactory.openSession();
      Instructor instructor = (Instructor)session1.load( Instructor.class, new Long(1) );
      System.out.println("Instructor: " + instructor.getFirstName() + " " + instructor.getLastName() );
      for (Iterator it = instructor.getCourses().iterator(); it.hasNext();) {
         Course course = (Course) it.next();
         System.out.println("Course: " + course.getName() + " / " + course.getCourseDescription() ); //Make sure it's initialized
      }
      session1.close();

      Session session2 = TestUtils.sessionFactory.openSession();
      session2.update( instructor ); //Reattach to new session
      Course firstCourse = (Course)instructor.getCourses().get(0); //This firstCourse is still assigned to session1, which is closed
      Student firstStudent = (Student)firstCourse.getStudents().get(0); //This throws a LazyInitializationException
      System.out.println("First student: " + firstStudent.getFirstName() + " " + firstStudent.getLastName() );
   }
}


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jul 23, 2005 10:36 am 
Senior
Senior

Joined: Thu May 12, 2005 11:40 pm
Posts: 125
Location: Canada
Try cascade="lock"


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jul 23, 2005 12:02 pm 
Regular
Regular

Joined: Wed May 11, 2005 11:57 pm
Posts: 80
cascade="lock" did not seem to work. Is this new in Hibernate 3? I don't see it as a valid attribute in http://www.hibernate.org/hib_docs/v3/re ... tions.html

Here is what I get when I try to create a SessionFactory:

Code:
SEVERE: Error parsing XML: XML InputStream(11) Value "lock" is not one of the enumerated values for this attribute.


Here is the relevant section of the mapping file:

Code:
<list name="courses" lazy="true" cascade="lock" >
   <key column="instructorId"/>
   <index column="sortOrderForinstructor"/>
   <one-to-many class="Course" />
</list>


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jul 23, 2005 12:32 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Use the right DTD.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jul 23, 2005 5:48 pm 
Regular
Regular

Joined: Wed May 11, 2005 11:57 pm
Posts: 80
That worked, thanks for both tips.

Is there a significant performance problem with setting all relationships to cascade="lock"? If not, great. If so, what is the recommended way to handle this?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 26, 2005 3:03 pm 
Senior
Senior

Joined: Thu May 12, 2005 11:40 pm
Posts: 125
Location: Canada
It depends on the lock mode you specify when you call lock() to reattach the graph. If you specify NONE, the overhead will be minimal. If you use a more aggressive lock mode, you may end up doing a lot of version checks, which could be expensive.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 27, 2005 4:05 pm 
Regular
Regular

Joined: Wed May 11, 2005 11:57 pm
Posts: 80
Nebob wrote:
It depends on the lock mode you specify when you call lock() to reattach the graph. If you specify NONE, the overhead will be minimal. If you use a more aggressive lock mode, you may end up doing a lot of version checks, which could be expensive.


OK, that sounds perfect - thanks! Most of the time I'll be doing lockmode.NONE, but if I set locks to cascade for my whole relationship graph, is there a way to NOT cascade if I want a more expensive lock, ie. lockmode.UPGRADE?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 27, 2005 5:15 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Note that lock(parent, LockMode.XXXX) always cascades to lock(child, LockMode.NONE). We do not propagate the lockmode, since that would be unecessary and very expensive in almost all cases.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 28, 2005 8:12 am 
Regular
Regular

Joined: Wed May 11, 2005 11:57 pm
Posts: 80
gavin wrote:
Note that lock(parent, LockMode.XXXX) always cascades to lock(child, LockMode.NONE). We do not propagate the lockmode, since that would be unecessary and very expensive in almost all cases.


Thanks - sounds like this will work great.

--Jesse


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.