Hibernate merge vs. persist. Were there changes? - java

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.

Related

Does calling multiple times save() method of hibernate with same object insert new record in DB?

for(int index=0; index<10; index++) {
Session session = hibernateTemplate.getSessionFactory().openSession();
session.save(object);
}
Does this code store the passed object in save(object) in DB 10 times or it will be overridden every time?
It depends on the object's state.
If you create a new object each time, new object is in the transient state: it is not mapped to a database record and not managed by any persistence context. So, calling Hibernate's save() method will create a new record in the database.
But if you call save() method with a managed object which is already attached to the current persistence context and mapped to a database record: the same object will be updated.
save method in hibernate:
*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"
Accept parameters :#param object a transient instance of a persistent class
Return Prameters : #return the generated identifier*
In summary, the save() method saves records into the database by INSERT SQL query, Generates a new identifier, and returns the Serializable identifier back. So you will have 10 Object record in your database with different Ids
Read more: https://javarevisited.blogspot.com/2012/09/difference-hibernate-save-vs-persist-and-saveOrUpdate.html#ixzz6F8Hiy8fF
Hope it helps.

Hibernate updating the record upon calling the setter methods of the bean class

I am new with this language. I have some rows in employee table and the bean class is Employee. I have fetched one record
Employee employee=this.employeeDaoImpl.getEmployeeObject(employeeId);
This is the CONTROLLER
#Transactional
#RequestMapping(value="/revise_payroll")
public String revise_payroll(HttpServletRequest req,HttpServletResponse resp, Model model,RedirectAttributes redirect){
System.out.println("in revise payroll");
String employeeId=req.getParameter("employeeId");
System.out.println("E_ID for revise:"+employeeId);
List<IncrementDecrementPayrollTemp> tempPayrollList=this.employeeDaoImpl.getTemporaryPayroll(employeeId);
//get employee object from session
List<Employee> empList=this.employeeDaoImpl.getCurrentCTC(employeeId);
System.out.println("empList has: "+empList.toString());
Employee employee=this.employeeDaoImpl.getCurrentCTCasObject(employeeId);
System.out.println(("in controller employee hashcode: "+employee.toString()));
int count=0;
// this will run for only one time
for(IncrementDecrementPayrollTemp tempPayroll:tempPayrollList){
employee.setCtc(tempPayroll.getCtct());
employee.setBasicMonthly(tempPayroll.getBasicMonthlyt());
employee.setBasicAnnual(tempPayroll.getBasicAnnualt());
employee.setDaMonthly(tempPayroll.getDaMonthlyt());
employee.setDaAnnual(tempPayroll.getDaAnnualt());
employee.setHouserentMonthly(tempPayroll.getHouserentMonthlyt());
employee.setHouserentAnnual(tempPayroll.getHouserentAnnualt());
employee.setConveyanceMonthly(tempPayroll.getConveyanceMonthlyt());
employee.setConveyanceAnnual(tempPayroll.getConveyanceAnnualt());
employee.setMedicalMonthly(tempPayroll.getMedicalMonthlyt());
employee.setMedicalAnnual(tempPayroll.getMedicalAnnualt());
employee.setSpecialMonthly(tempPayroll.getSpecialMonthlyt());
employee.setSpecialAnnual(tempPayroll.getSpecialAnnualt());
employee.setPfMonthly(tempPayroll.getPfMonthlyt());
employee.setPfAnnual(tempPayroll.getPfAnnualt());
employee.setEsiMonthly(tempPayroll.getEsiMonthlyt());
employee.setEsiAnnual(tempPayroll.getEsiAnnualt());
employee.setMonthlySalary(tempPayroll.getMonthlySalaryt());
}
return new ModelAndView ("IncrementDecrementStatus");
}
Now, when I am just calling the setter methods on employee object, its updating the sql records, in the controller itself. I am not yet in DAO layer using session.save or any update function.
This is DAO Layer
Session session=this.sessionFactory.getCurrentSession();
String p=employeeId.trim();
String hql="From Employee e where e.employeeId=?";
Query query=session.createQuery(hql);
query.setString(0, p);
List<Employee> employeeList=(List<Employee>)query.list();
System.out.println("dao list has "+employeeList.toString());
// to update the existing records
for(Employee emp:employeeList){
int id=emp.getId();
System.out.println("id got: "+id);
Employee empl=(Employee) session.get(Employee.class, id);
String version=empl.getVersion();
System.out.println("version is: "+version);
int intVersion=Integer.valueOf(version);
intVersion=intVersion+1;
version=String.valueOf(intVersion);
empl.setVersion(version);
System.out.println("version and ctc in empl is: "+empl.getVersion()+" , "+empl.getCtc());
System.out.println("hash code in loop: "+empl.toString());
session.update(empl);
}
// this is to save new record
Integer i=(Integer)session.save(sessionEmployee);
System.out.println("save returned: "+i.toString());
}
Things I want to achieve is, I want to update the existing records already in sql table and then save the employee object with some new set of values as a new record. Please suggest me where I am wrong. Thank you!
Let me tell you the lifecycle states of an entity which can make you more clear about this behaviour.
An entity can exist in three states - Transient, Persistent and Detached.
Transient - When you create an object but do not associate it with Hibernate session, then it is in Transient state. Any modifications to such object using setter methods doesn't reflect the change in the database.
Persistent - Here the object is attached to the Hibernate session. So now the Hibernate session manages this object. Any changes made to this object gets reflected in the database. Because Hibernate designed it in such way that, if any modifications is made to a Persistent object, it automatically gets updated in the database, when the session is flushed. (This is Hibernate's capability).
Detached - This state is similar to Transient. The only difference is that an object in detached state was previously in the session(i.e. in persistent state ). But now this is out of the session, because of either closing of the session or calling the evict(Object) method of session.
So coming to your case, once you have loaded the object from database, the object is associated with the session, and thus is in persistent state. As this object is in Persistent state, and you made changes to a Persistent object, the changes are reflected back to database.
Coming to your requirement, (Dividing the problem into parts)
You want to get an existing record from the table - Use Employee empl=(Employee) session.get(Employee.class, id);
Now you want to make changes to this object but not to the database. So use session.evict(empl); to bring the object to detached state. Then after this, you can make modifications to the detached empl object.
Now you want to save this set of new values as a new record. So make sure you change the "id" property of the empl object, as you can't violate unique constraint of the id value. You can't have two records with the same id value in the table.
Don't forget to commit the transaction.
That's normal behaviour. If you load an entity and modify it while it's still managed by the EntityManager, it will propagate all changes back to the database.
You can use evict(employee) to make the bean unmanaged.
Chang performed on any attached entity , hibernate automatically detect and commit to DB. either you can detached loaded entity by evict(entity) or create transient entity by clone of attached entity to use it further in you code.

DTO entity mapping with hibernate #Version control

I am using #Version annotation to provide version control in hibernate. My question is regarding the proper mapping of data from DTO to Entity.
What I feel is the right way is as follows but I want to know if there is a better way or this is how everybody does it.
call comes to my service
i load the entity to be updated (assume AddressEntity with version = 1)
i map the AddressDTO values to AE, including sub-collections if any
after all mapped, i detach the entity AE (only to be detached after Lazy sub collections mapped too)
now i map the version from DTO to AE (as hibernate does not allow to update version in managed entity)
now i call merge to update this detached AE entity
1) Is this the right way semantics and logic wise ?
2) (bit out of context) is there an overhead for hibernate to merge an object already in context and managed ie can i use merge for all updates safely irrespective or managed/unmanaged or Only merge+flush for unmanaged and flush for managed after updating some properties ?
Let me try to answer your question stepwise:
Suppose you have loaded an AddressEntity (having id=123 and version=1). Set the property values from AddressEntity to AddreeDto including the id and version values. Send the AddressDto to UI.
Changes made to AddresDto. Call has come to your service. Create an instance of AddressEntity and set the values from AddressDto including the id and version values. This new AddressEntity has now turned into a detached instance, as it has a persistent identity, but its state is not guaranteed to be synchronized with database state.
Hibernate lets you reuse this Addressentity instance in a new transaction by reassociating it with a new persistence manager.This detached instance can be reassociated with a new Session by calling update(). You don't need to load the entity again.The update() method forces an update to the persistent state of the object in the database.
Set the addressEntity properties:
addressEntity.setId(dto.getId());
addressEntity.setVersion(dto.getVersion());
Attach addressEntity to a new session:
Transaction tx = sessionTwo.beginTransaction();
sessionTwo.update(addressEntity);
tx.commit();
sessionTwo.close();
The session.update will execute an SQL similar to this:
update ADDRESS_ENTITY set ... , VERSION=2
where ID=123 and VERSION=1
If another application transaction would have updated the same ADDRESS_ENTITY since it was loaded, the VERSION column would not contain the value 1, and the row would not be updated, and you will receive a stale object state exception. You can catch the exception and inform the User about the stale data.
after all mapped, i detach the entity AE (only to be detached after Lazy sub collections mapped too)
Assuming you are performing this in a single transaction. Any persistent object that you have retrieved from DB is associated with the current session and transaction context. If it is modified in the same transaction, its state will be automatically synchronized with the DB. This mechanism is called automatic dirty checking. It means Hibernate will track and save the changes made to an object inside a session.
Transaction tx = session.beginTransaction();
int addressEntityID = 1234;
AddressEntity addressEntity = (AddressEntity) session.get(AddressEntity.class, new Long(addressEntityID));
// set the values from AddressDTO to AddressEntity
tx.commit();
session.close();
The object is retrieved from DB, it is modified and the modifications are propagated to DB on transaction commit.You don't need to detach and reattach an entity to perform an update.
now i map the version from DTO to AE (as hibernate does not allow to update version in managed entity)
The managed versioning is used to implement optimistic locking and the versioning of the entities is managed by Hibernate. The version number is just a counter value, it does not have any useful information that you should keep in your DTO.You don’t need to set the value of the version yourself. Hibernate will initialize the value when you first save an AddressEntity, and increment or reset it whenever the object is modified.
If another application transaction(T2) updates the persistent instance the same item since it was read by the current application transaction(T1), the T2 transaction will change the version value for this entity. Now when T1 tries to make an update, Hibernate will throw a stale object state exception, as the version of the entity has been changed. You can catch the exception and inform the User about the stale data. In particular, versioning prevents the lost update problem. You don't need to map the version from DTO to AE or from AE to DTO, as it does not have any meaningful information which can be used in contexts other than to implement optimistic locking.

Refresh entities in JPA

I'm confused about how I should refresh the state of entity that is already in the database. Being more specific, suppose I have "entity" persisted with a code like this:
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(entity);
em.getTransaction().commit();
entityManager.close();
Since I closed the EntityManager my entity instance is detached. Now suppose that I have other objects using this instance of entity. If I want to fetch the new state of this entity from the database, I pretty much can't use em.refresh() because the entity is detached. The em.merge() method returns a managed instance, and since is not the same instance of my object this can be a problem. I can foresee two solutions:
create a new method in my entity object that updates its state using a given entity instance.
not close the entity manager (implications !??)
So, what I should do in this case? How can I refresh the state of my entity object without losing all the references from other objects to it? Ideas?
If entity A references entity B that is detached, merging B returns B', and refresh B'. If you merge A, A will change its reference of B to B'.
A ---> B --(merge)--->B'
(refresh)
/
merge A -----------/
To avoid the changes being made to an entity by refreshing & getting detached after persisting, can implement the Cloneable interface & then processing the cloned entity accordingly.
//---
XEntity cloneX = (XEntity) entity.clone();
cloneX = entityManager.merge(cloneX);/* Persisting & getting synchronized copy */
// entityManager.refresh(cloneX); /* not need */
cloneX.copyTo(entity); // Add required changes back to entity if any
//---

How can you replicate Hibernate's saveOrUpdate in JPA?

In JPA, is there any way you can replicate Hibernate's saveOrUpdate behavior,
saveOrUpdate
public void saveOrUpdate(Object object)
throws HibernateException
Either save(Object) or update(Object) the given instance, depending upon resolution of the unsaved-value checks (see the manual for discussion of unsaved-value checking).
This operation cascades to associated instances if the association is mapped with cascade="save-update".
Parameters:
object - a transient or detached instance containing new or updated state
Throws:
HibernateException
See Also:
save(Object), update(Object)
which essentially checks to see if the object already exists in the database and either updates that object as need be or saves a new instance of the object.
JPA transcationless reads are nice, but I am really missing this method from Hibernate. How do experienced JPA developers handle this?
Try using the EntityManager.merge method - this is very similar.
There is an excellent description of the differences in Xebia's blogpost: "JPA Implementation Patterns: Saving (Detached) Entities."
The problem with the method outlined in the article that Pablojim linked to, is that it doesn't handle auto generated primary keys very well.
Consider the creation of a new ORM entity object, you can give this the same data as an existing row in the database table, but unless I am mistaken, the entity manager does not recognize them as the same row until they have the same primary key, which in a entity that uses auto generated keys, you can't get until you go up to the database.
Here is my current work around for that situation;
/**
* Save an object into the database if it does not exist, else return
* object that exists in the database.
*
* #param query query to find object in the database, should only return
* one object.
* #param entity Object to save or update.
* #return Object in the database, whither it was prior or not.
*/
private Object saveOrUpdate(Query query, Object entity) {
final int NO_RESULT = 0;
final int RESULT = 1;
//should return a list of ONE result,
// since the query should be finding unique objects
List results = query.getResultList();
switch (results.size()) {
case NO_RESULT:
em.persist(entity);
return entity;
case RESULT:
return results.get(0);
default:
throw new NonUniqueResultException("Unexpected query results, " +
results.size());
}
}

Categories

Resources