我遇到了这样的情况,我需要将分离的对象重新附加到hibernate会话,尽管会话中可能已经存在相同标识的对象,这将导致错误。

现在,我可以做两件事之一。

getHibernateTemplate()。更新(obj) 当且仅当对象在hibernate会话中还不存在时,这才有效。当我以后需要它时,抛出异常,声明具有给定标识符的对象已经存在于会话中。 getHibernateTemplate()。合并(obj) 当且仅当hibernate会话中存在对象时,此操作才有效。如果稍后使用此方法,则在需要对象处于会话中时抛出异常。

对于这两种场景,我如何将会话附加到对象?我不想使用异常来控制这个问题解决方案的流程,因为一定有更优雅的解决方案……


当前回答

如果您确定您的实体没有被修改(或者您同意任何修改都将丢失),那么您可以将其重新绑定到带锁的会话。

session.lock(entity, LockMode.NONE);

它不会锁定任何东西,但它会从会话缓存中获取实体,或者(如果在那里没有找到)从DB中读取它。

当你从一个“旧的”实体(例如HttpSession)中导航关系时,防止LazyInitException是非常有用的。首先“重新附加”实体。

使用get也可以工作,除非你映射了继承(这已经会在getId()上抛出异常)。

entity = session.get(entity.getClass(), entity.getId());

其他回答

会话。contains(Object obj)检查引用,不会检测到表示同一行且已经附加到该引用的不同实例。

这里是带有标识符属性的实体的通用解决方案。

public static void update(final Session session, final Object entity)
{
    // if the given instance is in session, nothing to do
    if (session.contains(entity))
        return;

    // check if there is already a different attached instance representing the same row
    final ClassMetadata classMetadata = session.getSessionFactory().getClassMetadata(entity.getClass());
    final Serializable identifier = classMetadata.getIdentifier(entity, (SessionImplementor) session);

    final Object sessionEntity = session.load(entity.getClass(), identifier);
    // override changes, last call to update wins
    if (sessionEntity != null)
        session.evict(sessionEntity);
    session.update(entity);
}

这是. net EntityFramework中我喜欢的几个方面之一,关于更改实体及其属性的不同附加选项。

直截了当的回答:您可能正在寻找一个扩展的持久性上下文。这是Seam框架背后的主要原因之一…如果您特别纠结于在Spring中使用Hibernate,请查看Seam的这篇文档。

外交回答:这在Hibernate文档中有描述。如果你需要更多的说明,请参阅Hibernate的Java持久化第9.3.2节“使用分离对象”。如果您正在使用Hibernate做CRUD以外的事情,我强烈推荐您阅读这本书。

在最初的帖子中,有两种方法,更新(obj)和合并(obj),被提到是有效的,但在相反的情况下。如果这是真的,那么为什么不先测试一下对象是否已经在会话中,然后调用update(obj)如果它是,否则调用merge(obj)。

会话是否存在的测试是session.contains(obj)。因此,我认为下面的伪代码可以工作:

if (session.contains(obj))
{
    session.update(obj);
}
else 
{
    session.merge(obj);
}

所有这些答案都忽略了一个重要的区别。update()用于(重新)将对象图附加到Session。你传递给它的对象是被管理的对象。

merge() is actually not a (re)attachment API. Notice merge() has a return value? That's because it returns you the managed graph, which may not be the graph you passed it. merge() is a JPA API and its behavior is governed by the JPA spec. If the object you pass in to merge() is already managed (already associated with the Session) then that's the graph Hibernate works with; the object passed in is the same object returned from merge(). If, however, the object you pass into merge() is detached, Hibernate creates a new object graph that is managed and it copies the state from your detached graph onto the new managed graph. Again, this is all dictated and governed by the JPA spec.

就“确保该实体受到管理,或者使其受到管理”的通用策略而言,这在某种程度上取决于您是否还想考虑尚未插入的数据。假设你这样做,使用一些类似

if ( session.contains( myEntity ) ) {
    // nothing to do... myEntity is already associated with the session
}
else {
    session.saveOrUpdate( myEntity );
}

注意,我使用的是saveOrUpdate()而不是update()。如果您不想在这里处理尚未插入的数据,请使用update()来代替…

如果您确定您的实体没有被修改(或者您同意任何修改都将丢失),那么您可以将其重新绑定到带锁的会话。

session.lock(entity, LockMode.NONE);

它不会锁定任何东西,但它会从会话缓存中获取实体,或者(如果在那里没有找到)从DB中读取它。

当你从一个“旧的”实体(例如HttpSession)中导航关系时,防止LazyInitException是非常有用的。首先“重新附加”实体。

使用get也可以工作,除非你映射了继承(这已经会在getId()上抛出异常)。

entity = session.get(entity.getClass(), entity.getId());