How many hits to database does entityManager.find produce? [duplicate] - java

We are using Toplink implementation of JPA + Spring + EJB. In one of our EJBs we have something like this:
public void updateUser(long userId, String newName){
User u = em.get(User.class, userId);
u.setName(newName);
// no persist is invoked here
}
So, basically this updateUser() method is supposed to update the name of a user with the given userId.
But the author of this method forgot to invoke em.persist(u).
And the strangest thing is that it works fine. How can it be? I was 100% sure that
without invoking em.persist() or em.merge() there is no way that changes could have been saved into database. Could they? Is there any scenario when this could happen?

You're working with a managed entity. If the entity does not become detached because its entity manager is closed, all changes done to the entity are reflected to the database when the session is flushed/closed and the transaction commited.
From the Java EE tutorial:
The state of persistent entities is
synchronized to the database when the
transaction with which the entity is
associated commits.
Edit for clarity and explanation: So there are three distinct modes that an entity could be in during its lifecycle:
Unsaved: The entity has been instantiated, but persist() has not been called yet.
Managed: The entity has been persisted using persist(), or loaded from the database, and is associated with an entity manager session. All changes to the entity are reflected to the database when the entity manager session is flushed.
Detached: The entity's entity manager session was closed. Changes to the entity will not be reflected to the database automatically, but can be merged explicitly using the merge() command.

Related

JPA flushing to Database before #PreUpdate is called

I am trying to capture the entity data in the database before the save is executed, for the purpose of creating a shadow copy.
I have implemented the following EntityListener in my Spring application:
public class CmsListener {
public CmsListener() {
}
#PreUpdate
private void createShadow(CmsModel entity) {
EntityManager em = BeanUtility.getBean(EntityManager.class);
CmsModel p = em.find(entity.getClass(), entity.getId());
System.out.println(entity);
}
}
The entity does indeed contain the entity object that is to be saved, and then I inject the EntityManager using another tool, which works fine - but for some reason, the entity has already been saved to the database. The output of CmsModel p = em.find(...) results in identical data which is in entity.
Why is JPA/hibernate persisting the changes before #PreUpdate is called? How can I prevent that?
I would assume this is because em.find doesn't actually query the database but fetches the object from cache, so it actually fetches the same object entity refers to (with changes already applied).
You could check your database log for the query that fetches the data for entity.id to verify this is indeed the case or you could add a breakpoint in createShadow() and have a look at the database entry for entity at the time the function is called to see for yourself if the changes are already applied to the database at that time.
To actually solve your problem and get your shadow copy you could fetch the object directly from database via native query.
Here is an untested example of what this could look like:
public CmsModel fetchCmsModelDirectly(){
Query q = em.createNativeQuery("SELECT cm.id,cm.value_a,cm.value_b FROM CmsModel cm", CmsModel.class);
try{
return q.getSingleResult();
}catch(NoResultException e){
return null;
}
}
Do you check if the entity is really updated to database? My suspect is that the change is only updated to the persistence context (cache). And when the entity is query back at the listener, the one from the cache is returned. So they are identical.
This is the default behavior of most of the ORM (JPA in this case) to speed up the data lookup. The ORM framework will take care of the synchronizing between the persistence context and the database. Usually when the transaction is committed.

How to make HSQL not auto update entity when change entity's field

I have an entity and whenever I edit its field, HSQL immediately updates the entity.
The code below
System.out.println(userService.findById(user.getId()).getPassword());
user.setPassword("password");
System.out.println(userService.findById(user.getId()).getPassword());
userService.update(user);
System.out.println(userService.findById(user.getId()).getPassword());
prints out in the console
qwerty
password
password
which means that even before I use update(), the entity is already updated.
How can I stop it? So that entity can be only updated invoking the method, but not when you change its field.
Hibernate config:
jdbc.driverClassName=org.hsqldb.jdbc.JDBCDriver
jdbc.url=jdbc:hsqldb:mem:myDb
jdbc.username=sa
jdbc.password=sa
hibernate.dialect=org.hibernate.dialect.HSQLDialect
hibernate.hbm2ddl.auto=create
I'm assuming you have a transaction open around the following code:
System.out.println(userService.findById(user.getId()).getPassword());
user.setPassword("password");
System.out.println(userService.findById(user.getId()).getPassword());
userService.update(user);
System.out.println(userService.findById(user.getId()).getPassword());
What happens is Hibernate fetches the entity and since it's still within a transaction the entity is a hibernate managed entity so ANY changes to the entity (e.g. user.setPassword(...) will result in those changes being flushed to the DB.
If you instead only make the userService transactional (i.e. remove the transaction from surrounding the example code and annotate the userService as #Transactional (assuming you're using Spring)) then when you do the user.setPassword(...) it will be OUTSIDE of a transaction and the changes won't persist.
Alternatively you can force the entity to detach (so that it's no longer managed) by calling the EntityManager::detach method.
For additional context read section 3.5 in the following link: https://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/objectstate.html

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.

What does it mean to be "associated with persistence context"?

In this documentation http://docs.oracle.com/javaee/5/tutorial/doc/bnbqw.html, it says
Managed entity instances have a persistent identity and are
associated with a persistence context.Detached entity instances have a
persistent identify and are not currently associated with a
persistence context.
So I am trying to understand what "asscoiated with a persistence context mean"?
You can treat a Persistence Context as a container that has an ability to store entities that can be managed and synchronized with a database by using Entity Manager. The following diagram depicts dependencies between JPA classes and interfaces:
A process of association an entity with a persistence context can be realized by executing:
EntityManager.persist method on a newly created entity
EntityManager.merge method on a detached entity
EntityManager.find method with the given entity type and its primary key
EntityManager.createNativeQuery, EntityManager.createNamedQuery and EntityManager.createQuery methods with SQL / JPQL / CriteriaAPI based queries
In case of transaction-scoped container-managed persistence context these methods need to be invoked within the scope of a transaction.
#Entity
public class Employee {
#Id
private int id; //determines persistent identity
public Employee(int id) { this.id = id; }
}
Persistent identity allows to locate the entity across all entities of the same type by its primary key and to synchronize to the database in case of flushing or committing the persistence context (either manually by flush/commit or automatically).
Employee emp = new Employee(1);
em.persist(emp);
boolean isManaged = em.contains(emp); //true, managed, id=1
boolean isFound = Objects.equals(emp, em.find(Employee.class, 1)); //true
Persisting the entity causes it to become managed (associated with persistence context). It dos not mean that the entity is synchronized to the database.
A process of disassociation an entity with a persistence context can be realized by executing:
EntityManager.detach or EntityManager.remove methods on a managed entity; the difference between them is that a detached entity remain in the underlying database since removed one is not
EntityManager.commit, EntityManager.rollback, EntityManager.clear, EntityManager.close methods which affects persistence context
serialization as an effect of passing it to another tier or sending through a remote interface
...
em.detach(emp);
isManaged = em.contains(emp); //false, detached, id=1
isFound = Objects.equals(emp, em.find(Employee.class, 1)); //false
Although detached entity still has assigned persistent identity it's no longer associated with the persistence context thus synchronized with the database. As a consequence any changes to a detached entity will not be persisted and committed unless it's merged into the persistence context. In such case it's possible that entity has been overridden by another party hence becoming stale, therefore id of entity being merged needs to be cleared/reassigned.
The persistence context is the code responsible for synchronizing the state of any attached entities with the state of the database. The EntityManager class is used to interact with the persistence context. Queries are executed via the EntityManager (which interacts with the persistence context) and keeps the entity state in sync with the database contents. When an entity is detached from the persistence context (for example by calling the detach method on the EntityManager) that entity's state is no longer kept in sync with the database.

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.

Categories

Resources