Joe7
Joe7

Reputation: 518

JPA: How can I ensure that attribute values of a managed entity are written to the database in a transactional way?

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?

Upvotes: 0

Views: 944

Answers (1)

Szarpul
Szarpul

Reputation: 1571

Since you run in EJB container and your transaction is managed by the container, every business method of your EJB bean is invoked inside a transaction, so as far as your isolation level of the transaction is no set to Read uncommitted, changes that where made in the method will be visible when the transaction commits, that is when the method finishes.

UPDATE:

As you don't use business methods it looks like your code is running without any transaction. In this case, every invocation of EnitytMangers method will create a new PersistenceContext. This is why you are getting exception. Entity that you get form find method is detached when next method of EntityManager is called (as new PersistenceContext is created).

Upvotes: 1

Related Questions