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

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

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

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


当前回答

所有这些答案都忽略了一个重要的区别。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()来代替…

其他回答

我想出了一个解决方案,从持久存储中“刷新”一个对象,这将解释其他可能已经附加到会话的对象:

public void refreshDetached(T entity, Long id)
{
    // Check for any OTHER instances already attached to the session since
    // refresh will not work if there are any.
    T attached = (T) session.load(getPersistentClass(), id);
    if (attached != entity)
    {
        session.evict(attached);
        session.lock(entity, LockMode.NONE);
    }
    session.refresh(entity);
}

首先调用merge()(更新持久实例),然后调用lock(LockMode.NONE)(附加当前实例,而不是merge()返回的实例)似乎在某些用例中是可行的。

对不起,似乎不能添加评论(还?)。

使用Hibernate 3.5.0-Final

尽管已经弃用了session# lock方法,但javadoc建议使用session# buildLockRequest(LockOptions)#lock(entity),如果您确保您的关联具有cascade=lock,惰性加载也不是问题。

我的attach方法看起来有点像

MyEntity attach(MyEntity entity) {
    if(getSession().contains(entity)) return entity;
    getSession().buildLockRequest(LockOptions.NONE).lock(entity);
    return entity;

初步测试表明它很有效。

所有这些答案都忽略了一个重要的区别。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());