我遇到了这样的情况,我需要将分离的对象重新附加到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中我喜欢的几个方面之一,关于更改实体及其属性的不同附加选项。
我回到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());