I'm having problems with a NonUniqueObjectException
thrown by Hibernate.
Reading the docs, and this blog post, I replaced the call from update()
to merge()
, and it solved the problem.
I believe I understand the reason for the exception, and why changing the method fixed the problem, in terms of disconnected objects and session boundaries.
My question is : given that merge()
开发者_如何学Go will always resolve to the session object, or retrieve it if it doesn't exist, is calling merge() generally a safer alternative than update()
?
What is the downside of using merge()
over update()
?
Is calling merge() generally a safer alternative than update() ?
As a way to avoid NonUniqueObjectException, yes. I think it explains why JPA does not allow an update method.
What is the downside of using merge() over update() ?
An unadvised user may think he or she has a fresh managed entity. Something like
// myEntity (passed as parameter does not become managed)
// Only the one returned by the merge operation is a managed entity
session.merge(myEntity);
// "newValue" is not commited because myEntity is not managed
myEntity.setMyProperty("newValue");
And if your persistence context does not contain your entity, maybe you do not want select-before-updating default behavior. But it can be avoided
- Add a version (@Version) column. 0 or NULL version indicates that an instance is new and has to be inserted, not updated
- Use a Hibernate interceptor
- If you are sure you want to update instead of inserting, you can use the following approach
...
public void updateMyEntity(MyEntity updateableMyEntity);
// load does not hit the database
MyEntity myEntity = (MyEntity) session.load(MyEntity.class, updateableMyEntity.getId());
BeanUtils.copyProperties(myEntity, updateableMyEntity);
}
This way you can update your entity without merge or update method. See this question for more information: Best way to update some fields of a detached object on Hibernate ?
Use update() if you are sure that the session does not contain an already persistent instance with the same identifier and merge() if you want to merge your modifications at any time without consideration of the state of the session. In other words update() is usually the first method you would call in a fresh session ensuring that reattachment of your detached instances is the first operation that is executed.
SessionFactory factory = cfg.buildSessionFactory();
Session session1 = factory.openSession();
Student s1 = null;
Object o = session1.get(Student.class, new Integer(101));
s1 = (Student)o;
session1.close();
s1.setMarks(97);
Session session2 = factory.openSession();
Student s2 = null;
Object o1 = session2.get(Student.class, new Integer(101));
s2 = (Student)o1;
Transaction tx=session2.beginTransaction();
session2.merge(s1);
Explanation
See from line numbers 4–7, we just loaded one object s1 into session1 cache and closed session1 at line number 7, so now object s1 in the session1 cache will be destroyed as session1 cache will expires when ever we say session1.close()
.
Now s1 object will be in some RAM location, not in the session1 cache. Here s1 is in detached state, and at line number 8 we modified that detached object s1, now if we call update()
method then hibernate will throws an error, because we can update the object in the session only.
So we opened another session [session2] at line number 10, and again loaded the same student object from the database, but with name s2. So in this session2, we called session2.merge(s1);
now into s2 object s1 changes will be merged and saved into the database
精彩评论