从问题中的链接中找出海报离开的地方,这是我如何设法重新附加的大致/相关代码。下面是正在发生的事情的概要。
@Repository
public abstract class DaoHibernate<T> implements Dao<T> {
@Override
public T reattach(T entity) {
if (getCurrentSession().contains(entity)) {
return entity;
}
if (entity instanceof User) {
return (T) reattachedUser((User) entity);
}
if (entity instanceof Content) {
Content content = (Content) entity;
User user = content.getUser();
if (!currentSession().contains(user)) {
content.setUser(reattachedUser(user));
}
content.setAttributes(persistentAttributesMap(content.getId(), content.getAttributes(), Content.class);
getCurrentSession().lock(content, LockMode.NONE);
return entity;
}
throw new UnsupportedOperationException("reattach is not supported for entity: " + entity.getClass().getName());
}
private User reattachedUser(User user) {
user.setAttributes(persistentAttributesMap(user.getId(), user.getAttributes(), User.class));
getCurrentSession().lock(user, LockMode.NONE);
return user;
}
@SuppressWarnings ("unchecked")
private Map<String, String> persistentAttributesMap(long id, Map<String, String> attributes, Class clazz) {
SessionFactory sessionFactory = getSessionFactory();
Session currentSession = sessionFactory.getCurrentSession();
String role = clazz.getName() + ".attributes";
CollectionPersister collectionPersister = ((SessionFactoryImplementor) sessionFactory).getCollectionPersister(role);
MapType mapType = (MapType) collectionPersister.getCollectionType();
PersistentMap persistentMap = (PersistentMap) mapType.wrap((SessionImplementor) currentSession, attributes);
persistentMap.setOwner(id);
persistentMap.setSnapshot(id, role, ImmutableMap.copyOf(attributes));
persistentMap.setCurrentSession(null);
return persistentMap;
}
...
}
浏览
如您所见,我们必须确保永远不会尝试重新附加当前会话中已经存在的实体,否则休眠将引发异常。这就是为什么我们必须在reattach() 中执行getCurrentSession().contains(entity)。这里必须小心使用contains(),因为hibernate 不会使用entity.hachCode() 来查找实体,而是使用System.identityHashCode(entity),这不仅确保它是一个等价的实例,而且精确可能已经在会话中的相同实例。换句话说,您必须适当地管理重用实例。
只要关联实体标有Cascade.ALL,hibernate 就应该做正确的事。也就是说,除非你有一个像我们的@ElementCollection 属性映射这样的休眠托管集合。在这种情况下,我们必须手动创建一个PersistentCollection(准确地说是PersistentMap)并为其设置正确的属性,如persistentAttributesMap,否则hibernate会抛出异常。总之,在PersistentMap上,我们要:
- 将所有者和快照键设置为拥有实体的 ID
- 将快照角色设置为完全限定的 entity.property 名称,正如 hibernate 所看到的那样
- 将快照
Serializable 参数设置为现有集合的不可变副本
- 将会话设置为
null,这样hibernate 就不会认为我们尝试将其附加到现有会话两次
要完成重新附加,请致电session.lock(entity, LockMode.NONE)。在这一点上,据我的测试可以看出,当您调用 saveOrUpdate() 时,hibernate 尊重此实体并正确保留所有更改。
注意事项
我意识到这不是适用于所有情况的通用解决方案。这只是对我的特定问题的快速解决方案,其他人希望可以利用和改进。软件是迭代的。