我遇到了这样的情况,我需要将分离的对象重新附加到hibernate会话,尽管会话中可能已经存在相同标识的对象,这将导致错误。
现在,我可以做两件事之一。
getHibernateTemplate()。更新(obj)
当且仅当对象在hibernate会话中还不存在时,这才有效。当我以后需要它时,抛出异常,声明具有给定标识符的对象已经存在于会话中。
getHibernateTemplate()。合并(obj)
当且仅当hibernate会话中存在对象时,此操作才有效。如果稍后使用此方法,则在需要对象处于会话中时抛出异常。
对于这两种场景,我如何将会话附加到对象?我不想使用异常来控制这个问题解决方案的流程,因为一定有更优雅的解决方案……
我这样做在c#与NHibernate,但它应该在Java中以同样的方式工作:
public virtual void Attach()
{
if (!HibernateSessionManager.Instance.GetSession().Contains(this))
{
ISession session = HibernateSessionManager.Instance.GetSession();
using (ITransaction t = session.BeginTransaction())
{
session.Lock(this, NHibernate.LockMode.None);
t.Commit();
}
}
}
对每个对象都调用First Lock,因为Contains总是false。问题是NHibernate通过数据库id和类型来比较对象。Contains使用equals方法,如果没有被覆盖,则通过引用进行比较。使用equals方法,它可以在没有任何异常的情况下工作:
public override bool Equals(object obj)
{
if (this == obj) {
return true;
}
if (GetType() != obj.GetType()) {
return false;
}
if (Id != ((BaseObject)obj).Id)
{
return false;
}
return true;
}
如果您确定您的实体没有被修改(或者您同意任何修改都将丢失),那么您可以将其重新绑定到带锁的会话。
session.lock(entity, LockMode.NONE);
它不会锁定任何东西,但它会从会话缓存中获取实体,或者(如果在那里没有找到)从DB中读取它。
当你从一个“旧的”实体(例如HttpSession)中导航关系时,防止LazyInitException是非常有用的。首先“重新附加”实体。
使用get也可以工作,除非你映射了继承(这已经会在getId()上抛出异常)。
entity = session.get(entity.getClass(), entity.getId());