Java 在Hibernate中重新连接分离对象的正确方法是什么?
在这种情况下,我需要将分离的对象重新连接到hibernate会话,尽管会话中可能已经存在具有相同标识的对象,这将导致错误 现在,我可以做两件事中的一件Java 在Hibernate中重新连接分离对象的正确方法是什么?,java,hibernate,spring,session,detach,Java,Hibernate,Spring,Session,Detach,在这种情况下,我需要将分离的对象重新连接到hibernate会话,尽管会话中可能已经存在具有相同标识的对象,这将导致错误 现在,我可以做两件事中的一件 getHibernateTemplate().update(obj) 当且仅当对象在hibernate会话中不存在时,此方法才有效。当我以后需要时,抛出异常,声明会话中已存在具有给定标识符的对象 getHibernateTemplate().merge(obj) 当且仅当hibernate会话中存在对象时,此操作才有效。如果我以后使用它,当我需要
getHibernateTemplate().update(obj)
当且仅当对象在hibernate会话中不存在时,此方法才有效。当我以后需要时,抛出异常,声明会话中已存在具有给定标识符的对象getHibernateTemplate().merge(obj)
当且仅当hibernate会话中存在对象时,此操作才有效。如果我以后使用它,当我需要对象在会话中时会抛出异常在这两种情况下,如何将会话一般附加到对象?我不想使用异常来控制此问题解决方案的流程,因为必须有一个更优雅的解决方案…尝试getHibernateTemplate().replicate(实体,ReplicationMode.Last_版本)非爆破性回答:您可能正在寻找扩展的持久性上下文。这是…背后的主要原因之一。。。如果您特别难以在Spring中使用Hibernate,请查看Seam的文档
try getHibernateTemplate().saveOrUpdate()
外交回答:这在中有描述。如果您需要更多的说明,请参阅“使用分离对象”的第9.3.2节。如果您使用Hibernate进行的不仅仅是CRUD,我强烈建议您购买本书。如果您确信您的实体未被修改(或者如果您同意任何修改都将丢失),然后可以使用锁将其重新连接到会话
session.lock(entity, LockMode.NONE);
它不会锁定任何内容,但会从会话缓存中获取实体,或者(如果没有找到)从数据库中读取实体
当您从“旧”实体(例如从HttpSession)导航关系时,防止LazyInitException非常有用。首先“重新附着”实体
使用get也可以工作,除非您映射了继承(这将在getId()上引发异常)
因此,似乎没有办法在JPA中重新附加一个过时的分离实体
merge()
将陈旧状态推送到数据库,
并覆盖任何中间更新
无法对分离的实体调用refresh()
无法对分离的实体调用lock()
,
即使它可以,而且它确实重新连接了实体,
使用参数“LockMode.NONE”调用“lock”
暗示您正在锁定,但未锁定,
是我见过的最违反直觉的API设计
所以你被卡住了。
有一个detach()
方法,但没有attach()
或resattach()
。
对象生命周期中的一个明显步骤对您不可用
从有关JPA的类似问题的数量来看,
看来即使JPA声称有一个连贯的模型,
它肯定不符合大多数程序员的思维模式,
他们被诅咒浪费了很多时间试图理解
如何让JPA做最简单的事情,并最终得到缓存
管理代码遍布其应用程序
看来唯一的办法就是抛弃你陈旧的分离实体
并使用相同的id执行一个查找查询,该查询将命中L2或DB
Mik首先调用merge()(更新持久实例),然后调用lock(LockMode.NONE)(附加当前实例,而不是merge()返回的实例)似乎适用于某些用例。在最初的帖子中,有两种方法,
更新(obj)
和合并(obj)
,它们都提到过要工作,但情况正好相反。如果这是真的,那么为什么不先测试对象是否已经在会话中,然后调用update(obj)
如果已经在会话中,否则调用merge(obj)
会话中是否存在的测试是session.contains(obj)
。因此,我认为以下伪代码可以工作:
if (session.contains(obj))
{
session.update(obj);
}
else
{
session.merge(obj);
}
我提出了一个解决方案,从持久性存储中“刷新”一个对象,该对象将考虑可能已附加到会话的其他对象:
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);
}
所有这些答案都忽略了一个重要的区别。update()用于(重新)将对象图附加到会话。传递给它的对象是被管理的对象 merge()实际上不是(重新)附件API。注意merge()有一个返回值吗?这是因为它会返回托管图,而托管图可能不是您传递给它的图。merge()是一个JPA API,其行为由JPA规范控制。如果传入merge()的对象已被管理(已与会话关联),则Hibernate将使用该图;传入的对象与从merge()返回的对象相同。但是,如果传递到merge()的对象已分离,Hibernate将创建一个新的受管对象图,并将分离的图中的状态复制到新的受管图中。同样,这都是由JPA规范规定和管理的 就“确保该实体被管理,或使其被管理”的一般策略而言,这取决于您是否也要考虑尚未插入的数据。假设你这样做了,使用类似
if ( session.contains( myEntity ) ) {
// nothing to do... myEntity is already associated with the session
}
else {
session.saveOrUpdate( myEntity );
}
注意,我使用了saveOrUpdate()而不是update()。如果您不想在此处处理尚未插入的数据,请改用update()。我在C#和NHibernate中就是这样做的,但在Java中也应该是这样:
public virtual void Attach()
{
if (!HibernateSessionManager.Instance.GetSession().Contains(this))
{
ISession session = HibernateSessionManager.Instance.GetSession();
using (ITransaction t = session.BeginTransaction())
{
session.Lock(this, NHibernate.LockMode.None);
t.Commit();
}
}
}
对每个对象调用第一个锁,因为Contains始终为false。问题是NHibernate按数据库id和类型比较对象。Contains使用equals
方法,如果未被覆盖,则通过引用进行比较。使用equals
方法,它可以毫无例外地工作:
public override bool Equals(object obj)
{
if (this == obj) {
return true;
}
if (GetType() != obj.GetType()) {
return false;
}
if (Id != ((BaseObject)obj).Id)
{
return false;
}
return true;
}
我回到了
org.hiberna的JavaDoc
public override bool Equals(object obj)
{
if (this == obj) {
return true;
}
if (GetType() != obj.GetType()) {
return false;
}
if (Id != ((BaseObject)obj).Id)
{
return false;
}
return true;
}
MyEntity attach(MyEntity entity) {
if(getSession().contains(entity)) return entity;
getSession().buildLockRequest(LockOptions.NONE).lock(entity);
return entity;
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);
}
Object obj = em.find(obj.getClass(), id);
em.refresh(obj)
Example :
Lot objAttach = em.merge(oldObjDetached);
objAttach.setEtat(...);
em.persist(objAttach);
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
doInJPA(entityManager -> {
Book book = entityManager.merge(_book);
LOGGER.info("Merging the Book entity");
assertFalse(book == _book);
});
SELECT
b.id,
b.author AS author2_0_,
b.isbn AS isbn3_0_,
b.title AS title4_0_
FROM
book b
WHERE
b.id = 1
-- Merging the Book entity
UPDATE
book
SET
author = 'Vlad Mihalcea',
isbn = '978-9730228236',
title = 'High-Performance Java Persistence, 2nd edition'
WHERE
id = 1
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
doInJPA(entityManager -> {
Session session = entityManager.unwrap(Session.class);
session.update(_book);
LOGGER.info("Updating the Book entity");
});
-- Updating the Book entity
UPDATE
book
SET
author = 'Vlad Mihalcea',
isbn = '978-9730228236',
title = 'High-Performance Java Persistence, 2nd edition'
WHERE
id = 1
@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {
//Code omitted for brevity
}
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(book);
return book;
});
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
try {
doInJPA(entityManager -> {
Book book = entityManager.find(
Book.class,
_book.getId()
);
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(_book);
});
} catch (NonUniqueObjectException e) {
LOGGER.error(
"The Persistence Context cannot hold " +
"two representations of the same entity",
e
);
}
org.hibernate.NonUniqueObjectException:
A different object with the same identifier value was already associated with the session : [com.vladmihalcea.book.hpjp.hibernate.pc.Book#1]
at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:651)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:284)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:227)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:92)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:682)
at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:674)