It seems like a pretty basic question to me, so probably I'm either lacking the right search terms or I'm completely missing something about the way how managed entites work, but nevertheless I was unable to find out how to do this: Writing new attribute values of a managed entity to the database in a transactional way, meaning I want to set a bunch of values to an entity bean and have them persisted all at once and without other threads seeing a „dirty“ intermediate state of the bean or interrupting the writing process.
This is the entity class:
@Entity
public class MyEntityClass
{
...
private String status;
private String value;
...
public void setStatus(String status)
{
...
public void setValue(String vlaue)
{
...
}
I'm using it here:
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class MyService
{
@PersistenceContext
protected EntityManager entityManager;
...
MyEntityClass entity = entityManager.find(entityClass, id);
//Now I'd like to set some attributes in a transactional way
entity.setStatus(newStatus);
//what if the new status got persisted and another thread reads from the database now
entity.setValue(newValue);
//flushing, just to make sure that at least from here on the database is in a consistent state
entityManager.flush();
}
I need to ensure that no other thread can see the entity in a „half-written“ state, i.e. with the new status already persisted but the old value still present in the database. I tried using a lock:
...
MyEntityClass entity = entityManager.find(entityClass, id);
entityManager.lock(entity, LockModeType.WRITE);
entity.setStatus(newStatus);
entity.SetValue(newValue);
entityManager.flush();
entityManager.lock(entity, LockTypeMode.NONE);
...
But this throws:
java.lang.IllegalArgumentException: entity not in the persistence context
because the transaction used for reading the entity ends after the entityManager.find()
is completed and therefore also the persistence context is gone, as I learned from this answer.
Also, I read here that I cannot manually create transactions with an EntityManager that uses container-managed transactions. But isn't there any way now to manually ensure that the entity's attributes are persisted together (or not at all)? Or is this somehow already done automatically?