JPA EXTENDED Persistence Context

19 April 2015
By Gonçalo Marques
In this article we will cover the differences between TRANSACTION and EXTENDED Persistence Context (Entity Manager).

TRANSACTION vs. EXTENDED Persistence Context

A container managed Persistence Context is always associated with a specific scope among the following available scopes: TRANSACTION or EXTENDED.

The more common scenario is the TRANSACTION scope, which Persistence Context definition is like the following:


@PersistenceContext(type = PersistenceContextType.TRANSACTION)
private EntityManager entityManager;

Since the default Persistence Context scope is TRANSACTION, the following declaration - missing the explicit scope type definition - is equivalent to the above one:


@PersistenceContext
private EntityManager entityManager;

On the other hand, an EXTENDED Persistence Context must have the explicit type definition and is declared like the following:


@PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;

The main difference between a transaction scoped Persistence Context and an extended Persistence Context is that the life time of a transaction Persistence Context is exactly the same as the current single transaction to which the context is associated. As soon as the current transaction ends (commit or rollback), the entities which are currently managed by the Entity Manager will immediately become detached.

The aforementioned rule does not apply to an extended Persistence Context, which duration may spawn across multiple transactions until explicitly closed by the application (or the associated EJB is removed/destroyed).

Since the extended Persistence Context has a duration that spawns multiple transactions, and consequently keeps state across multiple EJB method calls (transactional or non-transactional), it only makes sense to be used with Stateful EJB's, which may be used to implement conversation style interaction with clients.

In this article we will cover the EXTENDED Persistence Context and compare it to the TRANSACTION Persistence Context, considering the following subjects:

  • Loading of entities
  • Lazy loading of entity relationships
  • Persisting entities outside of an active transaction
  • Concurrency
  • Entities directly bound to UI forms

As a side note to this article, an Application Managed Persistence Context scope is always EXTENDED (see Container vs Application Managed EntityManager).

Loading entities

This section will focus on entity loading without the presence an active transaction. Consider the following example:

TRANSACTION scoped Persistence Context without an active transaction

@Stateless
@TransactionAttribute(TransactionAttributeType.NEVER)
public class TestEJB {

  @PersistenceContext
  private EntityManager entityManager;

  public Employee loadEmployee(Long id) {

    Employee employee = entityManager.find(Employee.class, id);

    // false
    boolean contains = entityManager.contains(employee);

    // will issue a second query
    Employee other = entityManager.find(Employee.class, id);

    // false
    boolean equal = (employee == other);

    return employee;
  }

}

In this concrete case, our Persistence Context is TRANSACTION scoped and note that we are running outside of an active transaction (TransactionAttributeType.NEVER is defined at class level, so it is applied to all methods that do not override it). All find operations executed against a transaction scoped Entity Manager, and outside of a transaction, will return detached entities.

More specifically, a very short lived Persistence Context will be created which duration will be the single method call. As soon as the find method returns, the Persistence Context is destroyed and the entities are immediately detached. Subsequent queries for the same entity will always issue another query against the database, and the entity manager will never contain any managed entity. Remember that there is no active transaction in this method.

Now let's see what happens if we use an EXTENDED Persistence Context instead:

EXTENDED scoped Persistence Context without an active transaction

@Stateful
@TransactionAttribute(TransactionAttributeType.NEVER)
public class TestEJB {

  @PersistenceContext(type = PersistenceContextType.EXTENDED)
  private EntityManager entityManager;

  public void methodOne() {

    Employee employee = entityManager.find(Employee.class, 4);

    // true
    boolean contains = entityManager.contains(employee);

    // does not issue a second query
    Employee other = entityManager.find(Employee.class, 4);

    // true
    boolean equal = (employee == other);

  }

  public void methodTwo() {

    // does not issue a second query
    // if called after methodOne
    // since the entity is already managed
    Employee employee = entityManager.find(Employee.class, 4);

    // true
    boolean contains = entityManager.contains(employee);

  }

}

Note that we are now using a Stateful EJB, so the same EJB will be used across subsequent calls from the same client. The Persistence Context is now defined as extended and the couple of methods are also not transactional.

Let's see what happens if we call methodOne() and then methodTwo():Since we are using an extended Persistence Context, the entities returned by find() will be managed by the Entity Manager even if we are outside of an active transaction. Since the Persistence Context is extended, it will be active until the life time of the EJB, so if we happen to call methodTwo() after calling methodOne(), the entity we are fetching from the database in this example will already be managed by the Entity Manager in methodTwo(), so a query will not be issued against the database.

An entity returned by find() will remain managed, and subsequent calls to find() for the same entity in any method will always return the same already managed instance (except if we manually detach the entity, clear the Persistence Context, or execute any other operation that detaches the entity, but that will not be discussed in this article).

Lazy loading of relationships

Lazy loading of relationships is also different in what matters to Persistence Context scope. We start by the transactional scope:

TRANSACTION scoped Persistence Context and relationship lazy loading

@Stateless
@TransactionAttribute(TransactionAttributeType.NEVER)
public class TestEJB {

  @PersistenceContext
  private EntityManager entityManager;

  public void methodOne() {

    Department department = entityManager.find(Department.class, 5);

    // force relationship loading
    department.getEmployees().size();

  }

}

Same scenario as before: Stateless EJB and method being called without an active transaction. What happens here? An exception will be thrown because one may not lazy load relationships without an active transaction, using a transaction scoped Persistence Context.

Now let's see how an extended Persistence Context behaves in this scenario:

EXTENDED scoped Persistence Context and relationship lazy loading

@Stateful
@TransactionAttribute(TransactionAttributeType.NEVER)
public class TestEJB {

  @PersistenceContext(type = PersistenceContextType.EXTENDED)
  private EntityManager entityManager;

  public void methodOne() {

    Department department = entityManager.find(Department.class, 5);

    // Suppose that Employee with ID=4 is returned
    // in the employee list at index 0
    department.getEmployees().size();

    // does not issue a query
    // since the entity was already returned 
    // in the employees list above
    Employee employee = entityManager.find(Employee.class, 4);

    // true
    boolean same = (department.getEmployees().get(0) == employee);

  }

}

The relationship will be loaded and the returned entities will also become managed entities, even if we are outside of an active transaction as shown in this example. Supposing that Employee with ID=4 exists in the relationship, subsequent find() calls for that employee will return the already managed instance.

Persisting data

Now we will see how operations that change data behave with EXTENDED Persistence Context when executed outside of an active transaction (this time we will skip the TRANSACTION scoped Persistence Context because we already know that an exception will be thrown if we try to change data and there is no current active transaction).

Consider the following EJB:

EXTENDED scoped Persistence Context changing data

@Stateful
@TransactionAttribute(TransactionAttributeType.NEVER)
public class TestEJB {

  @PersistenceContext(type = PersistenceContextType.EXTENDED)
  private EntityManager entityManager;

  public void changeName() {
    // changes will not be committed
    // because there is no active transaction
    Employee employee = entityManager.find(Employee.class, 4);
    employee.setName("Mark");
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  public void commit() {
    // changes will be committed here because
    // there is an active transaction and
    // pending changes exist in managed entities
    // (Employee with ID=4 name was changed)
  }

}

If changeName() method is called, the employee with ID=4 will be loaded from the database, and then the employee instance name property will be changed to "Mark". Since this method is not executed within an active transaction, the changes will not be persisted to the database at the end of method execution. The pending changes will be queued and will only be flushed to the database when we happen to have an active transaction.

If we then call method commit(), which happens to run inside an active transaction (note the @TransactionAttribute annotation) the pending changes will be stored in the database.

This is actually a widely used pattern with the EXTENDED Persistence Context: A conversation is established between the client and the EJB, where the client will make intermediate changes to data by calling methods that run outside of an active transaction. The last step is to call an empty method that is actually associated with an active transaction and the pending changes are finally saved in the database (this commit method is usually triggered by a confirmation button in a user interface form).

Let's see another scenario:

EXTENDED scoped Persistence Context changing and retrieving data

@Stateful
@TransactionAttribute(TransactionAttributeType.NEVER)
public class TestEJB {

  @PersistenceContext(type = PersistenceContextType.EXTENDED)
  private EntityManager entityManager;

  public void methodOne() {

    // changes will not be flushed
    // because there is no active transaction
    Employee employee = entityManager.find(Employee.class, 4);
    employee.setName("Mark");

    // Even if the Entity Manager knows that 
    // there are pending changes that may interfere 
    // with this query result, the changes will not 
    // be flushed and the employee with ID=4 will 
    // not be returned in this query
    List<Employee> list = entityManager.createQuery("select e from Employee e where e.name = 'Mark'")
        .getResultList();

    // false
    boolean contains = list.contains(employee);
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  public void methodTwo() {

    // Now there is an active transaction, and the 
    // Entity Manager knows that there are pending 
    // changes that may affect this query, so it will 
    // flush the data before executing the query, 
    // and the employee with ID=4 will be returned
    List<Employee> list = entityManager.createQuery("select e from Employee e where e.name = 'Mark'")
        .getResultList();

    // no query is executed because the
    // employee is already managed
    Employee employee = entityManager.find(Employee.class, 4);

    // true
    boolean contains = list.contains(employee);
  }

}

We are basically doing something similar with the previous example, but now we are also issuing queries that - under normal circumstances - should trigger a flush of pending changes before query execution. This is because we are querying for data that may be influenced by those pending changes (more specifically, we are executing queries that involve the employee name property, that has been changed to "Mark").

What happens here is that if we call methodOne(), the resulting list will not contain the employee with ID=4, even if its name has just been changed to "Mark" (it would match the query criteria). There is no active transaction in this method, so the changes will not be flushed to the database, and the employee will not be contained in the query result list.

If we execute methodTwo() afterwards, and now we have an active transaction, the pending changes made earlier in methodOne() will be flushed before the query executes. This is because the Entity Manager knows that the changes may have impact in the query result, so the data will be flushed and the employee will be contained in the query result list.

Persistence Context disposal

The container managed EXTENDED Persistence Context is closed when the associated EJB is destroyed or explicitly removed by the client. A client may remove a Stateful EJB may calling a method which is annotated with @Remove:

Stateful EJB remove method

@Remove
public void remove() {
}

If our Persistence Context is application managed and consequently always EXTENDED, as mentioned in Container vs Application Managed EntityManager, the application may close the Persistence Context by calling the close() method on the Entity Manager.

Concurrency

The Entity Manager is known for not being thread-safe. We are not going to deeply cover EJB concurrency management in this article, but we will mention that it is possible to configure a Stateful EJB to allow simultaneous method executions on the same EJB instance.

If we define the lock type of a Stateful EJB as LockType.READ at class level, it means that the container will allow simultaneous method executions from a given client against its associated EJB instance:

Stateful EJB allowing concurrent access to the same Entity Manager

@Stateful
@Lock(LockType.READ)
public class TestEJB {

  @PersistenceContext(type = PersistenceContextType.EXTENDED)
  private EntityManager entityManager;

  public void methodOne(Employee employee) {
    entityManager.persist(employee);
  }

  public void methodTwo(Employee employee) {
    entityManager.persist(employee);
  }

}

By default, the lock type of an EJB is LockType.WRITE so the container will guarantee that EJB methods execution is exclusive for a given instance, but this is a scenario one must consider if lock type is changed to READ for a given Stateful EJB (or any of its methods individually). The Entity Manager must not be accessed at the same time by two or more concurrent threads.

Remember that this is only a problem if the Persistence Context scope is EXTENDED (it will be the same for the entire EJB life time). If the scope is TRANSACTION, each single method will have it's own Persistence Context so the problem does not occur.

Binding Entities to UI forms

If we are dealing with Local EJBs, we already know that method parameters will be passed by reference. This means that, if we are dealing with a Stateful EJB with EXTENDED Persistence Context, it may be dangerous to directly bind managed JPA entities to UI forms.

If the user happens to change some entity in the UI form, and the entity was not manually detached, we are actually changing the managed entity that is currently being managed by the Persistence Context. As soon as we enter a transactional method, the changes will be flushed and committed to the database. While this could actually be the desired result, one must be aware of this behavior.

Additionally, and still in this same scenario, if one happens to be using Bean managed transactions in the Stateful EJB, and intentionally kept the transaction open during user interaction with the form, the changed entities may be persisted at any given time, even without any explicit user action that should have saved the data (the data would not be committed until explicitly instructed by the application, but this must be also taken into consideration in order to avoid unexpected results).

This very specific case is not exclusive to the EXTENDED Persistence Context. It also happens with the TRANSACTION Persistence Context, if we keep an opened transaction in our Local Stateful EJB and the user is freely changing managed entities which are bound to a user interface form.

Related Articles

Comments

About the author
Gonçalo Marques is a Software Engineer with several years of experience in software development and architecture definition. During this period his main focus was delivering software solutions in banking, telecommunications and governmental areas. He created the Bytes Lounge website with one ultimate goal: share his knowledge with the software development community. His main area of expertise is Java and open source.

GitHub profile: https://github.com/gonmarques

He is also the author of the WiFi File Browser Android application: