javax.persistence.Entitymanager: remove() method - java

Does remove(Object entity) method of EntityManager work only on those objects got from find() method?
I have following code snippet:
public void deletePerson() {
EntityManager em = getEntityManager();
Person p = new Person("x", "y", 200);
em.remove(p);
}
But it is not removing the particular entry from database.
When I tried something like below:
public void deletePerson() {
EntityManager em = getEntityManager();
Person p = em.find(Person.class, 200);
em.remove(p);
}
It's working fine.

Quoting from ObjectDB's manual on deleting JPA entity objects:
In order to delete an object from the database it has to first be
retrieved (no matter which way) and then in an active transaction, it
can be deleted using the remove method.
An IllegalArgumentException is thrown by remove if the argument is not
a an instance of an entity class or if it is a detached entity.
When creating object with new operator, it becomes a detached entity, you need to persist it if you want to remove it.
When retrieving entity, you are retrieving persistent entity.

Something to that direction. EntityManager.remove works only for managed entities. How you obtained these managed entities does not matter, it can be for example:
via JPQL query
via Criteria API query
find method in EntityManager
by following relationship from some other entity.
created new entity and persisted it
But simply creating new object and trying to remove it does not work, because this new object is not managed entity. Also entity should not be yet detached.
Life of entity is quite much as follows, all in same transaction (entities outside their transaction are not managed):
Entity ent = new Entity(1); //entity is in new state, EntityManager never know
//anything about it
em.persist(ent); //entity is managed as long as not disconnected
//from EntityManager
em.clear(); // all previously managed entities, including ent, are now detached
Entity same = em.find(1); //managed same
em.remove(same); // entity is removed

Yes in case of merge or remove operation you have to use a find() operation and then use the remove method on the retrieved entity.

With JPA, you can remove an entity without retrieving it by simply executing a delete statement:
javax.persistence.Query q= entityManager.createQuery("delete from A where id = :id");
q.setParameter("id", "value of id to delete");
int deletedRows = q.executeUpdate();

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.

JPA's bulk update works when changing the object?

Normally, if I change an object mapped with #Entity, it will be persisted at the end of transactional methods, even if I don't call any save methods.
I'm doing a bulk update for performance reasons using the EntityManager#CriteriaUpdate from JPA, but I need to trigger some events in the setters of the objects, so I set them, but don't call the save method.
What I want to know is if the bulk update is useful if I change the object, or each object will be persisted, even though the bulk update is executed?
PgtoDAO:
public void bulkUpdateStatus(List<Long> pgtos, Long newStatusId) {
CriteriaBuilder cb = this.manager.getCriteriaBuilder();
CriteriaUpdate<Pgto> update = cb.createCriteriaUpdate(Pgto.class);
Root e = update.from(Pgto.class);
update.set("status", newStatusId);
update.where(e.get("id").in(pgtos));
this.manager.createQuery(update).executeUpdate();
}
PgtoService:
#Transactional(readOnly = false)
public int changePgtosStatus(List<Pgto> pgtos, StatusEnum newStatus){
...
List<Long> pgtoIds = new ArrayList<Pgto>();
for(Pgto pgto : pgtos){
// Hibernate will persist each object here, individually?
pgto.setStatus(newStatus.id());
pgtoIds.add(pgto.getId());
}
pgtoDao.bulkUpdateStatus(pgtoIds, newStatus.id());
// I tried setting a different status here to the objects, but it did not persisted
}
Perhaps I should end the connection after the bulk update?
Criteria query and changed entities are treated separately. Criteria query is just executed, and managed (loaded via entity manager) changed entities are synchronized with database on transaction commit.
If you like to prevent this, you will have to detach those entities from entity manager. Then changes will be not propagated to database anymore

Entity must be managed to call remove when I try to delete an entity

I have this method to delete the entity selected in the list. but when called generates this error and I can not see why.
java.lang.IllegalArgumentException: Entity must be managed to call
remove: HP Envy 15, try merging the detached and try the remove again.
public void delete(Stock stock){
EntityManager em = ConnectionFactory.createEntityManager();
em.getTransaction().begin();
em.detach(stock);
em.remove(stock);
em.getTransaction().commit();
em.close();
}
I've read other related posts
Entity must be managed to call remove
IllegalArgumentException: Entity must be managed to call remove
You can't remove the entity if it is not attached. If the entity is still attached, you can remove it as-is. If it is no longer attached, you can re-attach it using merge:
if (!em.contains(stock)) {
stock = em.merge(stock);
}
em.remove(stock);
Very thanks guys
You helped me to heal my head ache
Here is the code after correcting the error
EntityManager em = ConnectionFactory.createEntityManager();
em.getTransaction().begin();
if (!em.contains(stock)) {
current = em.merge(stock);
}
em.remove(current);
em.getTransaction().commit();
em.close();
You detach an entity from a session, and then delete it. That won't work.
Try removing em.detach(stock); and pass some entity to the method which is guaranteed to be attached to the session, i.e. fetch something from DB and then delete it at once. If that works, you are using your method in a wrong way, most likely with detached or just-created entities.
remove the
em.detach(stock);
detach removes your entity from the entityManager
Why do you detach the object ? An IllegalArgumentException is thrown by detach if the argument is not an entity object.
If the argument stock is managed by entity manager, delete the detach line, else merge the entity.
Try this:
public void delete(Stock stock){
EntityManager em = ConnectionFactory.createEntityManager();
em.getTransaction().begin();
Stock mStock2 = em.merge(stock);
em.remove(mStock2);
em.getTransaction().commit();
em.close();
}

JPA, removing an entity which has found by different manager

Assume we have a simple entity bean, like above
#Entity
public class Schemes implements serializable{
...
#Id private long id;
...
}
I find a record using find method and it works perfect, the problem is I cannot manipulate it(remove) by another EntityManager later, for example I find it with a method, and later I want to remove it, what is the problem?! if I find it with same manager again I would remove it, but if object has found by another manager I cannot.
#ManagedBean #SessionScopped class JSFBean {
private Schemes s;
public JSFBean(){
....
EntityManager em;//.....
s=em.find(Schemes.class,0x10L);//okay!
....
}
public void remove(){//later
....
EntityManager em;//.....
em.getTransaction().begin();
em.remove(s);//Error! some weird error, it throws IllegalArgumentException!
em.getTransaction().commit();
....
}
}
many thanks.
You are probably getting a java.lang.IllegalArgumentException: Removing a detached instance.
The two EMs do not share a persistence context and for the second EM, your object is considered detached. Trying to remove a detached object will result in an IllegalArgumentException.
You can refetch the entity before the removal:
Schemes originalS = em.find(Schemes.class, s.getId());
em.remove(originalS);
EDIT You can also delete the entity without fetching it first by using parametrized bulk queries:
DELETE FROM Schemes s WHERE s.id = :id
Be aware that bulk queries can cause problems on their own. First, they bypass the persistence context, meaning that whatever you do with a bulk query will not be reflected by the objects in the persistence context. This is less an issue for delete queries than for update queries. Secondly, if you have defined any cascading rules on your entites - they will be ignored by a bulk query.

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