I saw to types of update operation:
First:
getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) {
session.flush();
session.setCacheMode(CacheMode.IGNORE);
SomeObject ss = (SomeObject) session.get(SomeObject.class, id);
long next = ss.getAndIncrement();
session.update(ss);
session.flush();
return null;
}
});
and secondly
SomeObject ss = loadSomeObject();
long next = ss.getAndIncrement();
getHibernateTemplate.merge(ss);
These two method do the same. I want to know which one is better and safe and why. Thank you.
In the first operation the object ss is attached to the session. where as in the second operation its detached. So if you have an attached objects you can use update. If you have a detached objects then use merge which first attaches the object to the session then will do an update.
EDIT: For your information on attached(persistent) and detached objects :
Hibernate defines and supports the following object states:
Transient - an object is transient if it has just been instantiated using the new operator, and it is not associated with a Hibernate Session. It has no persistent representation in the database and no identifier value has been assigned. Transient instances will be destroyed by the garbage collector if the application does not hold a reference anymore. Use the Hibernate Session to make an object persistent (and let Hibernate take care of the SQL statements that need to be executed for this transition).
Persistent - a persistent instance has a representation in the database and an identifier value. It might just have been saved or loaded, however, it is by definition in the scope of a Session. Hibernate will detect any changes made to an object in persistent state and synchronize the state with the database when the unit of work completes. Developers do not execute manual UPDATE statements, or DELETE statements when an object should be made transient.
Detached - a detached instance is an object that has been persistent, but its Session has been closed. The reference to the object is still valid, of course, and the detached instance might even be modified in this state. A detached instance can be reattached to a new Session at a later point in time, making it (and all the modifications) persistent again. This feature enables a programming model for long running units of work that require user think-time. We call them application transactions, i.e., a unit of work from the point of view of the user.
Whats an API without any code examples?
SessionFactory sf = ctx.getBean("hibernateSessionFactory",SessionFactory.class);
Session session = sf.openSession();
Transaction t = session.beginTransaction();
try {
Session s2 = sf.openSession();
Organization org = (Organization)s2.get(Organization.class,100624l);//1
org.setOrgName("org");
s2.close();//2
Organization org1 = (Organization)session.get(Organization.class,100624l);//3
org.setOrgName("testOrg");
org1.setOrgName("org");//a
session.merge(org);//4
System.out.println(org == org1);//b
t.commit();
} catch (HibernateException e) {
t.rollback();
throw e;
}finally{
session.close();
}
1st instance is loaded and made persistent.
The instance is detached.
Another instance is loaded
At the point of executing this operation, there are 2 instances of the same object
in the session(org and org1) – org is detached and org1 is persistent
If we do an update() or saveOrUpdate() there, we get the below exception:
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.spring.model.Organization#100624]
a. We do a merge here which:
I. Merges the state of the detached object + persistent object.
II. In case of conflict, the object which is merged wins, like in this case, the value saved will be : testOrg
III. Had we merged on org1, we would have got org.
b. This will always return false, meaning post merge, org was still in DETACHED state
I hope the diff. is clear now.
Summary :
saveOrUpdate() or update() will throw an exception if there are 2 instances of the same object in the session(one detached and one persistent)
merge() will not throw the exception, but will save the object while merging the changes.
Merge Does Following
Merge has intelligence. It has lot of pre-checks before it go actual merge(if required)
if Object is transient, It simply fires INSERT query makes object persistent(attached to session)
if Object is detached, fires select query to check whether data modified or not
if modified, fires UPDATE query otherwise just ignore merge task.
where as session.update
throws exception if object is transient.
if Object is detached, it simply fires UPDATE query irrespective of data changes to object.
session.merge is expensive than update
The basic difference is:
Merge() is not concerned about sessions whether persistent or detached...it will just update without considering the sessions.
In case of update() it will throw an exception like org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session.
It's explained here with a good example:
http://www.java4developer.com/difference-between-update-and-merge-in-hibernate/
Both update() and merge() methods in hibernate are used to convert the object which is in detached state into persistence state. But there is little difference. Let us see which method will be used in what situation.
Let Us Take An Example
SessionFactory factory = cfg.buildSessionFactory();
Session session1 = factory.openSession();
Employee s1 = null;
Object o = session1.get(Employee.class, new Integer(101));
s1 = (Student)o;
session1.close();
s1.setSSN(97);
Session session2 = factory.openSession();
Employee s2 = null;
Object o1 = session2.get(Employee.class, new Integer(101));
s2 = (Student)o1;
Transaction tx=session2.beginTransaction();
session2.merge(s1);
SessionFactory factory = cfg.buildSessionFactory();
Session session1 = factory.openSession();
Employee s1 = null;
Object o = session1.get(Employee.class, new Integer(101));
s1 = (Employee)o;
session1.close();
s1.setMarks(97);
Session session2 = factory.openSession();
Employee s2 = null;
Object o1 = session2.get(Employee.class, new Integer(101));
s2 = (Employee)o1;
Transaction tx=session2.beginTransaction();
session2.merge(s1);
Hope you are clear…, actually update and merge methods will come into picture when ever we loaded the same object again and again into the database, like above.
Related
I had an app with the following code working just fine until I upgraded hibernate (5.3.2 to 5.4.10 )
List<UserRole> roles = entity.getRoles();
for(UserRole r : roles) {
Em.get().remove(r);
}
roles.clear();
for(RoleEnum r : selectedRoles) {
UserRole role = new UserRole(entity, r);
Em.get().persist(role);
}
Em.get().merge(entity);
Em.get().flush();
So, then I started getting an exception
Caused by: org.hibernate.TransientPropertyValueException: object
references an unsaved transient instance - save the transient instance
before flushing : WEBPIECESxPACKAGE.base.libs.UserRole.user ->
WEBPIECESxPACKAGE.base.libs.UserDbo
This would happen when I 'add' a new user entity. If I edit an old user(it uses the same exact code), then it would be fine.
I changed to Em.get().persist(entity) instead and that works for adding a new entity to DB and for editing an old one.
BUT the documentation still says what old JPA/hibernate used to do for persist which is
#throws EntityExistsException if the entity already exists.
Is everyone using persist now as the add or edit function? (ie. having one function that saves or edits as I don't really care which is very very nice AND hibernate can tell from the DB id existing or not whether it is an add or an edit so there is no reason to not have a single call for both).
I am NOW using em.persist() which is working for UPDATE or SAVE...weird
It can be seen on line 110 here
https://github.com/deanhiller/webpieces/blob/master/webserver/webpiecesServerBuilder/templateProject/WEBPIECESxAPPNAME/src/main/java/webpiecesxxxxxpackage/web/crud/CrudUserController.java
I am using Hibernate 5.4.10
thanks,
Dean
Possible Duplicate of Update Vs Merge
Whats happening here is:
Edit Mode :
List<UserRole> roles = entity.getRoles(); //Gets Existing Roles from DB
for(UserRole r : roles) {
Em.get().remove(r); //Removes Roles to existing user
}
roles.clear(); // Clean up local memory
for(RoleEnum r : selectedRoles) { // User Input Roles
UserRole role = new UserRole(entity, r); // New Entity with existing user
Em.get().persist(role); // Role Entity Referenced to existing user object, saved
}
Em.get().merge(entity); // ?? No Need in edit unless roles are stored in user table
Em.get().flush();
New User Mode :
List<UserRole> roles = entity.getRoles(); // New Detached User Entity Roles
for(UserRole r : roles) { // Probably Empty Roles Array
Em.get().remove(r); // Removed roles
}
roles.clear(); // Clean up Memory
for(RoleEnum r : selectedRoles) { // Copy from App Roles
UserRole role = new UserRole(entity, r); //Create new role
Em.get().persist(role); //Save Role to DB
}
Em.get().merge(entity); // Trying to merge non existing Entity <-- This is where error appears
Em.get().flush();
The persist method works because it has decides when to use insert or update command. Since new user entity has no ID set to it, it has no idea what to do with it, while it may have worked in past, actual behavior of mergig is very well explain in this thread merging a detached or new entity with an existing entity in hibernate/jpa best practice question
See for yourself :
If your entity is a detached entity the only thing u really need to do
is to invoke entityManager.merge(user). You dont need to exec any
finder method. If your entity is not detached but rather new (it does
not have id specified) you should find appropriate entity in the
database prior performing any modification operations on that entity
and merge it afterwards.
Another detailed reference is given here : persist() and merge() in JPA and Hibernate
Here is the reference from docs :
Serializable
save(Object object) throws HibernateException
Persist the given transient instance, first assigning a generated identifier. (Or using the current value of the identifier property if the assigned generator is used.) This operation cascades to associated instances if the association is mapped with cascade="save-update".
Parameters:
object - a transient instance of a persistent class
Returns:
the generated identifier
Throws:
HibernateException
persist
void persist(String entityName,
Object object)
throws HibernateException
Make a transient instance persistent. This operation cascades to associated instances if the association is mapped with cascade="persist".
The semantics of this method are defined by JSR-220.
Parameters:
object - a transient instance to be made persistent
Throws:
HibernateException
merge
Object merge(String entityName,
Object object)
throws HibernateException
Copy the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance. The given instance does not become associated with the session. This operation cascades to associated instances if the association is mapped with cascade="merge".
The semantics of this method are defined by JSR-220.
Parameters:
object - a detached instance with state to be copied
Returns:
an updated persistent instance
Throws:
HibernateException
save() and persist() result in an SQL INSERT, delete() in an SQL
DELETE and update() or merge() in an SQL UPDATE. Changes to persistent
instances are detected at flush time and also result in an SQL UPDATE.
saveOrUpdate() and replicate() result in either an INSERT or an
UPDATE.
Conclusion: Functions are behaving as they are intended.
I understand that when does NonUniqueObjectException occurs and why does it occurs.
I saw many example on the internet for NonUniqueObjectException , each has same thing.
object is first detached from session1 so session1 is closed and cleared also and then object with same identifier is update or SaveorUpdate in session2.
Code snippet:
Session session = sessionFactory1.openSession();
Transaction tx = session.beginTransaction();
Item item = (Item) session.get(Item.class, new Long(1234));
tx.commit();
session.close(); // end of first session, item is detached
item.getId();// The database identity is "1234" item.setDescription("my new description");
Session session2 = sessionFactory.openSession();
Transaction tx2 = session2.beginTransaction();
Item item2 = (Item) session2.get(Item.class, new Long(1234));
session2.update(item);// Throws NonUniqueObjectException
tx2.commit();
session2.close();
My question is even if seesion1 is closed , why does hibernate keeps detached object in session1 though not managing it. when does all the object be removed from session1.
Hibernate does not keep a reference to a detached object, i.e. detached objects are not part of any session, and their garbage collection is not impeded in any way.
The cause of a NonUniqueObjectException is that two different objects for the same database row have become associated with the same session. That's bad because Hibernate automatically detects changes to objects in the session, and writes these changes back to the database. If several objects for the same row are in the same session, it is ambiguous which object's state should be written. Because this would result in hard to find bugs, Hibernate refuses such a situation.
Usually, Hibernate ensures that all queries for a row in a given session return the same object, so this situation can not arise. However, if you use an object obtained from a different session with a new session, it becomes associated with the new session, which can fail if the new session already contains an object for that row.
This is why the newer EntityManager API no longer features an update method, that associates a pre-existing object with the session, but a merge method, that copies the contents of the object into the object associated with the session.
To understand, have a look here :
Item item2 = (Item) session2.get(Item.class, new Long(1234));
This will turns in persistent state item2 with id 1234 in the session2.
session2.update(item);
item is in detached state, and update() will turn item in persistent state on session2 >>> Hibernate throws NonUniqueObjectException, because there are two objects Item with same id in the same session !
Do it like this :
Session session2 = sessionFactory.openSession();
Transaction tx2 = session2.beginTransaction();
Item item2 = (Item) session2.get(Item.class, new Long(1234));
item2.setDescription("my new description");
session2.update(item2);
tx2.commit();
When should I commit a transaction in hibernate after saving it. Is it before or after I call session.evict(obj). Currently my code looks like this(only required parts).
Session session = connector.getSession();
Transaction tx = session.beginTransaction();
try {
Criteria crit = session.createCriteria(ST_CODE_SETTINGS_STORE.class).add(Restrictions.eq("TYPE", "issueno"));
List<ST_CODE_SETTINGS_STORE> ls = crit.list();
if (ls.size() < 1) {
session.save(st_code_settings_store);
session.evict(st_code_settings_store);
msg = "insert";
}
else {
Long Id = null;
ST_CODE_SETTINGS_STORE st_code_settings_store1 = ls.get(0);
Id = st_code_settings_store1.getCODE_ID();
Object o = session.get(ST_CODE_SETTINGS_STORE.class, Id);
ST_CODE_SETTINGS_STORE update = (ST_CODE_SETTINGS_STORE) o;
session.update(update);
}
tx.commit();
} catch (Exception e) {
if (tx != null) {
tx.rollback();
}
System.out.println("Error: " + e.getMessage());
connector.printStack(e);
throw e;
} finally {
session.close();
}
Sometimes if I commit after evicting, the data does not get saved in the database. Is it the right way to code??
The method evict() removes a single object from Session cache. So
before you call evict() the object should be there in the Session
cache. Therefore if you save the object first time, you have to save
the object via Session.save(object). Subsequent update calls should
follow through session.saveOrUpdate(object) or session.update(object)
before calling evict() to remove the loaded object from the cache.(reference )
From Hibernate Docs
Ending a Session usually involves four distinct phases:
flush the session
commit the transaction
close the session
handle exceptions
Do not use the anti-patterns session-per-user-session or
session-per-application (there are, however, rare exceptions to this
rule). Some of the following issues might also arise within the
recommended patterns, so ensure that you understand the implications
before making a design decision:
A Session is not thread-safe. Things that work concurrently, like HTTP
requests, session beans, or Swing workers, will cause race conditions
if a Session instance is shared. If you keep your Hibernate Session in
your HttpSession (this is discussed later in the chapter), you should
consider synchronizing access to your Http session. Otherwise, a user
that clicks reload fast enough can use the same Session in two
concurrently running threads.
An exception thrown by Hibernate means
you have to rollback your database transaction and close the Session
immediately (this is discussed in more detail later in the chapter).
If your Session is bound to the application, you have to stop the
application. Rolling back the database transaction does not put your
business objects back into the state they were at the start of the
transaction. This means that the database state and the business
objects will be out of sync. Usually this is not a problem, because
exceptions are not recoverable and you will have to start over after
rollback anyway.
The Session caches every object that is in a
persistent state (watched and checked for dirty state by Hibernate).
If you keep it open for a long time or simply load too much data, it
will grow endlessly until you get an OutOfMemoryException. One
solution is to call clear() and evict() to manage the Session cache,
but you should consider a Stored Procedure if you need mass data
operations.
I would like to create unit tests for the persistence tier of my project to ensure that entities are being loaded lazily. I am using hibernate with springsource.
What is a basic unit test that can guarantee that I can create an Assertion to check that?
The challenge here for me at least is that while in the transaction I cannot tell if the entities are being fetched lazily and loaded upon demand or eagerly fetched.
Thanks
If you have a detached object, when you try to access the property, hibernate will throw a LazyInitializationException.
#Test(expected=LazyInitializationException.class)
public void lazyLoadTest() {
//get a session object
Session session = dao.getSession();
//load object
Foo foo = dao.findById(1);
//if you have a detached object, this would be unnessary
session.close();
//if lazy loading is working, an exception will be thrown
//note: If you don't try to access the collection (.size(), the data will not be fetched)
foo.getBars().size();
}
You can also use Hibernate.isInitialized
#Test
public void anotherLazyLoadTest() {
//get a session object
Session session = dao.getSession();
//load object
Foo foo = dao.findById(1);
//if you have a detached object, this would be unnessary
session.close();
boolean isInitialized = Hibernate.isInitialized(foo.getBars());
assertFalse(isInitialized);
}
I'm trying to reattach to the session an object from the HTTP session that was originally retrieved from the DB, I do this by calling session.lock(object, LockMode.None) and even though lock does not cascade this works all right for me because it does not push updates to the DB like merge does (the lock is required to open a detail view in a pop-up and the actual saving would occur later on the main window). Now to my surprise I have found that if my entity has a one to many relation any changes on that collection would cause a HibernateException "Reassociated object has dirty collection".
How am I supposed to reattach objects to the session without updating the DB or discarding the changes on the object then?
Here is the situation as code
EntityA t = createAnEntityA();
Session sess = factory.openSession();
sess.beginTransaction();
sess.save(t);
sess.getTransaction().commit();
sess.close();
// t is now saved on the DB but in dettached state
// change a simple property
sess = factory.openSession();
sess.beginTransaction();
t.setPropertyB("B");
sess.lock(t, LockMode.NONE);
// t is attached again, you won't get LazyInitializationException
// by calling its properties, although you have to be careful
// because the reattachment does not cascade to children
sess.getTransaction().commit();
sess.close();
// no updates went to the DB because setPropertyB was called
// when t was still dettached
// now change a collection
EntityC c = createAnEntityC();
t.getCollectionPropertyC().add(c);
sess = factory.openSession();
sess.beginTransaction();
sess.lock(t, LockMode.NONE);
// Exception is thrown :-(
sess.getTransaction().commit();
sess.close();
I'm afraid currently it's not possible without hitting DB.
Apparently, the exception is happening because of lock command.
There's a Jira reporting this behavior.
https://hibernate.atlassian.net/browse/HHH-511
It has 2 patches to fix the problem. You could try those patches.
But if you problem is only Lazy loading of collection, you could consider using Open Session In View pattern. (maybe not the best pattern, but it can work for your case)