Extended Persistence Contexts

Usually, an EntityManager in JBoss EJB 3.0 lives and dies within a JTA transaction. Once the transaction is finished, all persistent objects are detached from the EntityManager and are no longer managed. Any local caching the EntityManager instance had done is lost. JBoss EJB 3.0 allows you to define long-living EntityManagers that live beyond the scope of a JTA transaction. This is called an Extended Persistence Context.

When you specify that an injected EntityManager is an extended persistence context, all object instances remain managed. Extended persistence contexts can only be used within Stateful session beans. Take a look at ShoppingCartBean.

@Stateful
@Remote(ShoppingCart.class)
public class ShoppingCartBean implements ShoppingCart
{
   @PersistenceContext(type=PersistenceContextType.EXTENDED) EntityManager em;

   @EJB StatelessLocal stateless;

   private Customer customer;

   public long createCustomer()
   {
      customer = new Customer();
      customer.setName("William");
      em.persist(customer);
      return customer.getId();
   }

   public void update()
   {
      customer.setName("Bill");
   }
...
}

To inject an extended persistence context, you use the type() attribute and set it to EXTENDED. If you look at the createCustomer() method you notice that it is persisting a Customer object and storing a reference to that create object within a member variable of the stateful bean. When the update() method is called, you see that the customer's state is modified. Since the entity manager used is EXTENDED, the customer member variable remains managed by the entity manager and the modified state will be synchronized with the database.

Converations

An even more interesting use case is when you combine extended persistence contexts with non-transactional methods. If you interact with an extended persistence context outside of a transaction, the inserts, updates, and deletes will be queued until you access the persistence context within a transaction. This means that any persist(), merge(), or remove() method you call will not actually result in a JDBC execution and thus an update of the database until you manually call EntityManager.flush().

Why is this useful? Consider the usecase of a Setup Wizard on a website. The Wizard has seven steps, seven web pages to enter stuff in. It is extremely unwise to have a JTA transaction that spans multiple http requests, yet you do not want to commit anything to the database until all steps are complete. Your code can interact with the EntityManager as it wants and you do not have to worry about updates or writing a lot of gluecode in your application to do all the entity manager's work in the final step of the wizard. Efficient and highly performant. Because the managed persistent objects remain attached to the persistent context, all optmistic versioning checks can also be maintained within the application transaction. here's an example on how to do this.

@Stateful
@Remote(ShoppingCart.class)
public class ShoppingCartBean implements ShoppingCart
{
   @PersistenceContext(type=PersistenceContextType.EXTENDED) EntityManager em;

   @EJB StatelessLocal stateless;

   private Customer customer;

   public long createCustomer()
   {
      customer = new Customer();
      customer.setName("William");
      em.persist(customer);
      return customer.getId();
   }

   @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
   public void never()
   {
      customer.setName("Bob");
   }



   @Remove
   public void checkout()
   {
   }
}

Calling the never() method will update the managed customer object reference, but will not result in a database update until checkout() is called. The spec requires that any time you invoke a transactional method of a stateful bean, that the EntityManager join the transaction. Therefore, our never() update will be committed at the end of the checkout() method.

Building and Running

To build and run the example, make sure you have ejb3.deployer installed in JBoss 4.0.x and have JBoss running. See the reference manual on how to install EJB 3.0.
Unix:    $ export JBOSS_HOME=<where your jboss 4.0 distribution is>
Windows: $ set JBOSS_HOME=<where your jboss 4.0 distribution is>
$ ant
$ ant run

Buildfile: build.xml

prepare:

compile:

ejbjar:

run:
     [java] Created customer: William
     [java] Customer's name should still be William as pc was not yet flushed:  Customer.getName() == William
     [java] Now that the pc has been flushed name should be 'Bob': Customer.getName() == Bob
     [java] Created customer: William
     [java] ShoppingCartBean.customer should stay managed because we're in an extended PC: Customer.getName() == Bill
     [java] Extended persistence contexts are propagated to nested EJB calls: Customer.getName() == Bill Jr.