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

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.

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 many hits to database does entityManager.find produce? [duplicate]

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.

merge vs find to update entities JPA

From the book Pro EJB3 JPA:
The most common strategy to handle this (-update entities-) in Java EE application that uses JPA is to place the results of the changes into detached entity instances and merge the pending changes into a persistence context so that they can be written to the database
Example:
The emp param is a detached entity
#Stateless
public class EmployeeServiceBean {
#PersistenceContext
EmtityManager em;
public void updateEmployee(Employee emp){
if(em.find(Employee.class, emp.getId()) == null){
throw new IllegalArgumentException("Unknown Employee id")
}
em.merge(emp);
}
}
Then, says:
If the amount of information being udated is very small, we can avoid the detached object and merge() operation entirely by locating the managed version and manually copying the changes into it.
Example:
Here the emp is attached
public void updateEmployee(int id, String newName, long newSalary) {
Employee emp = em.find(Employee.class, id);
if(emp==null){
throw new IllegalArgumentException("Unknown Employee id")
}
emp.setEmpName(newName);
emp.setSalary(newSalary);
}
So, looks like for small updates and create operations the strategy find() and then set new values one by one is convenient. But!, for big updates of data (i.e collections) is preferred have a detached entity and all it's relations (with CascadeType.Merge) and do a big merge().
OK, but why?
Because if your bean has a lot of attributes, JPA will check one by one in the merge process, for all attributes, if you're dealing with a detached object.
Now, if you have a bean with 200 atrributes and want to change only 1 field, it´s easier for JPA to just get the managed version (internally, JPA knows when one field of a managed entity is "dirty" or not), then it will only deal with that specific attribute.

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
//---

Categories

Resources