Hi all,
I am having a problem with the behavior of Hibernate's session cache management. I already know one or two solutions to my problem, but I am asking for you expert guys if there is another solution.
I have two threads and a Parent/child association and the child's parent is nullable and I am using ThreadLocal to store the session for each thread and I never close a session.
If I load the childs on one thread, deletes it's parent on another thread and then re-load the child again, it is with his parent still setted. I know that this is the natural behavior of the session cache that Hibernate provides
I know that if I close the session in every DAO "action" (throwing away the ThreadLocal solution), it would work, but I would also loose the session cache advantages.
Other solution that I thought was to load the parent, call session.evict on it and then load the parent again. But this sounds very bad to me.
Here is the code that can reproduce what I am saying:
Code:
import net.sf.hibernate.HibernateException;
public class StaleTest {
Deleter deleter;
Checker checker;
public class Deleter implements Runnable {
private int parentId;
public Deleter(int parentId) {
this.parentId = parentId;
}
public void run() {
//sleep till Checker's first check is done.
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ParentDAO parentDAO = new ParentDAO();
try {
System.out.println("Gonna delete parent "+parentId+"...");
parentDAO.delete(parentId);
} catch (HibernateException e1) {
e1.printStackTrace();
return;
}
System.out.println("Deleted...");
}
}
public class Checker implements Runnable {
public void run() {
Child child;
ChildDAO childDAO = new ChildDAO();
try {
child = childDAO.load(1);
} catch (HibernateException e1) {
e1.printStackTrace();
return;
}
System.out.println("Child's parent (before)= "+child.getParent());
//sleeps till the parent has been deleted
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
child = childDAO.load(1);
} catch (HibernateException e2) {
e2.printStackTrace();
return;
}
System.out.println("Child's parent (after)= "+child.getParent());
}
}
public StaleTest(int parentId) {
this.deleter = new Deleter(parentId);
this.checker = new Checker();
}
public void start() {
System.out.println("Starting test!");
new Thread(this.deleter).start();
new Thread(this.checker).start();
}
public static void main(String args[]) throws HibernateException {
System.setProperty("java.util.logging.config.file", "res/logging.properties");
System.loadLibrary("_corelibsj");
Parent parent;
ParentDAO parentDAO = new ParentDAO();
parent = (Parent)parentDAO.findAll().get(0);
Child child;
ChildDAO childDAO = new ChildDAO();
child = childDAO.load(1);
child.setParent(parent);
childDAO.update(child);
StaleTest st = new StaleTest(parent.getId());
st.start();
}
}
Here is the output of this code:
Code:
Starting test!
Child's parent (before)= com.hoplon.core.data.Parent@1
Gonna delete parent 1...
Deleted...
Child's parent (after)= com.hoplon.core.data.Parent@1
And here is the output that I would like this code to have:
Code:
Starting test!
Child's parent (before)= com.hoplon.core.data.Parent@1
Gonna delete parent 1...
Deleted...
Child's parent (after)= null
Any help with this would be greatly appreciated!
Hibernate version:2.1.3
Mapping documents:
Parent.hbm:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
<hibernate-mapping package="com.hoplon.core.data">
<class name="Parent" table="parent">
<id
column="parent_id"
name="Id"
type="int"
>
<generator class="vm" />
</id>
</class>
</hibernate-mapping>
Child.hbm
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
<hibernate-mapping package="com.hoplon.core.data">
<class name="Child" table="child">
<id
column="child_id"
name="Id"
type="int"
>
<generator class="vm" />
</id>
<many-to-one
class="Parent"
name="Parent"
not-null="false"
>
<column name="parent_id" />
</many-to-one>
</class>
</hibernate-mapping>
Name and version of the database you are using:Oracle 10g
The generated SQL (show_sql=true):
Hibernate: select this.parent_id as parent_id0_ from parent this where 1=1
Hibernate: select child0_.child_id as child_id1_, child0_.parent_id as parent_id1_, parent1_.parent_id as parent_id0_ from child child0_ left outer join parent parent1_ on child0_.parent_id=parent1_.parent_id where child0_.child_id=?
Hibernate: update child set parent_id=? where child_id=?
Hibernate: select child0_.child_id as child_id1_, child0_.parent_id as parent_id1_, parent1_.parent_id as parent_id0_ from child child0_ left outer join parent parent1_ on child0_.parent_id=parent1_.parent_id where child0_.child_id=?
Hibernate: select parent0_.parent_id as parent_id0_ from parent parent0_ where parent0_.parent_id=?
Hibernate: delete from parent where parent_id=?