从问题中的链接中找出海报离开的地方,这是我如何设法重新附加的近似/相关代码。下面是正在发生的事情的概述。
@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;
}
...
}
走过
如您所见,我们必须确保我们永远不会尝试重新附加当前会话中已经存在的实体,否则休眠将引发异常。这就是为什么我们必须getCurrentSession().contains(entity)
在reattach()
. 此处必须小心使用contains()
,因为 hibernate 不会使用entity.hachCode()
来查找实体,而是使用 来System.identityHashCode(entity)
确保它不仅是等效实例,而且与会话中可能已经存在的完全相同的实例。换句话说,您必须适当地管理重用实例。
只要关联实体标有Cascade.ALL
,hibernate 就应该做正确的事情。也就是说,除非你有一个像我们@ElementCollection
的属性映射这样的休眠托管集合。在这种情况下,我们必须手动创建一个PersistentCollection
(PersistentMap
准确地说) 并在其上设置正确的属性,如 in persistentAttributesMap
,否则 hibernate 将抛出异常。简而言之,在 上PersistentMap
,我们必须:
- 将所有者和快照键设置为拥有实体的 id
- 将快照角色设置为完全限定的 entity.property 名称,正如 hibernate 所看到的那样
- 将快照
Serializable
参数设置为现有集合的不可变副本
- 将会话设置为
null
休眠状态不会认为我们尝试将其附加到现有会话两次
要完成重新连接,请调用session.lock(entity, LockMode.NONE)
。在这一点上,据我的测试可知,hibernate 尊重这个实体,并在你调用saveOrUpdate()
.
注意事项
我意识到这不是适用于所有情况的通用解决方案。这只是对我的特定问题的快速解决方案,其他人希望可以利用和改进。软件是迭代的。