I'm using Hibernate with the JPA API in Java and I'm running the following test:
public void test1()
{
A a = new A();
a.setValue(1);
getDao().store(a);
a.setValue(2);
A loaded = getDao().load(a.getId());
assertEquals(1, loaded.getValue());
}
I am expecting the test to pass because the second part of the code with the load call may be called by a different method that loads the object for a different purpose. I am expecting the state of the object to reflect the state of the database and this is not the case here!
So I found out that the reason this happens is that the persistence context is caching the object unless we detach it. So the way I implemented the store method is:
void store(T object)
{
EntityManager em = getEntityManager();
em.getTransaction().begin();
if (object.getId() == 0)
{
em.persist(object);
}
else
{
em.merge(object);
}
em.getTransaction.commit();
em.detach(object); // Detaches this very object from 1st level cache so that it always reflects DB state
}
public T load(long id)
{
return getEntityManager().find(getPersistentClass(), id);
}
The detach call made the test pass but since then I have a different problem: When I'm storing an entity that contains a reference to a different object and a getter which is using that referenced object is annotated to be validated the validation fails because the persistence context (or the Hibernate session) is passing a different object to the validator (a copy which is not having the same state). Let me give you an example:
class B
{
private int value;
@Transient
private C c = new C(); // Shall not be persisted
@Size(min = 1)
public String getName()
{
return c.getName();
}
public void setName(String name)
{
c.setName(name);
}
}
public void test2()
{
B b = new B();
b.setName("name");
getDao().store(b); // Includes detach, as above
// Validation has passed. c.name was "name".
getDao().store(b);
// Validation has failed. c.name was empty.
}
So c.name was empty in the second attempt although I haven't changed the object. The only thing inbetween is the detach() call. If I remove it it test2 will pass but test1 will fail again. What kind of magic is going on here? How do I fix this?