我遇到了这样的情况,我需要将分离的对象重新附加到hibernate会话,尽管会话中可能已经存在相同标识的对象,这将导致错误。
现在,我可以做两件事之一。
getHibernateTemplate()。更新(obj)
当且仅当对象在hibernate会话中还不存在时,这才有效。当我以后需要它时,抛出异常,声明具有给定标识符的对象已经存在于会话中。
getHibernateTemplate()。合并(obj)
当且仅当hibernate会话中存在对象时,此操作才有效。如果稍后使用此方法,则在需要对象处于会话中时抛出异常。
对于这两种场景,我如何将会话附加到对象?我不想使用异常来控制这个问题解决方案的流程,因为一定有更优雅的解决方案……
会话。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中我喜欢的几个方面之一,关于更改实体及其属性的不同附加选项。
所有这些答案都忽略了一个重要的区别。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()来代替…
我回到JavaDoc的org.hibernate.Session,发现如下:
通过调用save()、persist()或
saveOrUpdate()。通过调用delete()可以使持久实例成为瞬态实例。get()或load()方法返回的任何实例都是持久的。分离的实例可以通过调用update()、saveOrUpdate()、lock()或replication()来持久化。通过调用merge(),也可以将瞬态或分离实例的状态持久化为新的持久化实例。
因此update(), saveOrUpdate(), lock(), replication()和merge()是候选选项。
update():如果存在具有相同标识符的持久实例,将抛出异常。
saveOrUpdate():保存或更新
锁():弃用
replication():持久化给定分离实例的状态,重用当前标识符值。
merge():返回具有相同标识符的持久对象。给定的实例不会与会话关联。
因此,不应该直接使用lock(),可以根据功能需求选择其中的一个或多个。
如果您确定您的实体没有被修改(或者您同意任何修改都将丢失),那么您可以将其重新绑定到带锁的会话。
session.lock(entity, LockMode.NONE);
它不会锁定任何东西,但它会从会话缓存中获取实体,或者(如果在那里没有找到)从DB中读取它。
当你从一个“旧的”实体(例如HttpSession)中导航关系时,防止LazyInitException是非常有用的。首先“重新附加”实体。
使用get也可以工作,除非你映射了继承(这已经会在getId()上抛出异常)。
entity = session.get(entity.getClass(), entity.getId());