我在Java中使用Hibernate和JPA API,我正在运行以下测试:
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());
}
我希望测试能够通过,因为带有load调用的代码的第二部分可能会被另一个为不同目的加载对象的方法调用。我期待对象的状态反映数据库的状态,这不是这里的情况!
所以我发现发生这种情况的原因是持久化上下文缓存了对象,除非我们将它分离。所以我实现store方法的方式是:
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);
}
分离调用使测试通过但从那时起我遇到了另一个问题:当我存储包含对不同对象的引用的实体时,使用该引用对象的getter被注释为验证验证因为持久化上下文(或Hibernate会话)将不同的对象传递给验证器(一个不具有相同状态的副本)而失败。让我举个例子:
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.
}
所以c.name在第二次尝试时为空,尽管我没有更改对象。中间唯一的东西是detach()调用。如果我删除它,test2将通过但test1将再次失败。 这里有什么样的魔力?我该如何解决这个问题?