em.merge does not work while em.persist does?
Hello,
I have a stateless session bean with code snippets below.
@PersistenceContext
private EntityManager em;
...
publicvoid setList(ContainerList list,int key){
ContainerList stored = (ContainerList)em.find(ContainerList.class, key);
list.setId(key);
if (stored ==null){ em.persist(list);}
else{
em.merge(list);// 1st alternative
//stored.copy(list); // 2nd alternative
}
}
...
When I call the method setList for a non existing key, the entity is stored in the database. When I call the method for an existing key, the old value is not updated by the new, i.e. the merge method has no effect. If I comment out the 2nd alternative, it has no effect either. Does anyone has an idea why this does not work? I am using Netbeans 5.5 and 5.5.1 release candidate 1. (With the included application server.)
# 1
Apparently the problem is somewhere else,
I tracked it down to the methods annotated with
@PrePersist, @PreUpdate, @PostPersist, @PostUpdate, @PostLoad.
Apparently invoking the em.merge method calls the @PostLoad method rather than the @PreUpdate and @PostUpdate methods. Is this a bug?
# 3
I found out that there was an error in my code. The EJB persistence specification is ambiguous.
em.merge doesn't do much. All it does is load the attached entity from the database with the same primary key as the detached entity that is given as argument, but it doesn't do any updating at all. If you want to update the database, you have to update the returned attached entity.
So correct code to update the value of an entity in a database is:
Entity attached = em.merge(detached);
attached.copy(detached);
# 5
Hello,
I will try to summarise my knowledge of the JAVA persistence API as the documentation is not always clear and may be misleading.
In SQL you have 4 main statements: INSERT, SELECT, UPDATE and DELETE.
with
@PersistenceContext
private EntityManager em;
some documentation appears to make the following associations:
em.persist(Object entity) -> INSERT
em.find(Class entityClass, Object primaryKey) -> SELECT
em.merge(Object entity) -> UPDATE
em.remove(Object entity) -> REMOVE
However, practice shows that this is NOT the case!
em.merge(Object entity) does exactly the same thing as em.find(Class entityClass, Object primaryKey)! The only difference is that the em.merge(Object entity) method takes a detached object of the entity class as argument with the same primary key as the attached object you are loading.
Detached objects are objects of the entity class the database manager is not aware of. Attached objects are objects in the database and are managed by the persistence context. Every newly created object of an entity class is detached. You can attach them to the database with the em.persist(Object entity) method call. Attached objects can be retrieved with the em.find(Class entityClass, Object primaryKey) or with the em.merge(Object entity) method. All changes made to attached objects are automatically stored in the database. There thus is no explicit method for the UPDATE statement. You just make changes to attached objects and the entity manager copies the changes to the database (at the end of the persistence context). An easy way to make all the changes you need at once is to have a detached object that contains the right values and then copy all these values to the associated attached object with a self defined copy method in the entity class as in
Entity detached;
// --> begin persistence context
Entity attached = em.merge(detached);
attached.copy(detached);
// <-- end persistence context
If you are working with session beans, the persistence context standard begins with the beginning of a transaction and ends with the end of a transaction. A transaction standard begins with the start of a session bean method and ends with the session bean method. If you choose to, you can make the persistence context of type extended with
@PersistenceContext(type=PersistenceContextType.EXTENDED)
private EntityManager em;
In this case, the persistence context ends when the session bean is removed from the EJB container. (Can be useful to keep attached entities in the state of an stateful session bean.)
Without session beans, you have to declare explicitely the start and end of a persistence context or transaction, but I refer to existing documentation for this, as I do not actively know how to handle things without session beans.
Finally, the methods em.persist(Object entity) and em.merge(Object entity) use a detached object as argument, while em.remove(Object entity) uses an attached object as argument (using a detached object results in an Exception thrown).
I hope this makes some things clear