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() );
}
}