Most Java EE applications are stateless and there are good reasons for it. But not all user interactions can be implemented in that way. Sometimes you need to model a conversation that consists of multiple steps, like the typical shopping cart or complex update operations.
Doing this with JPA 2.0 always felt a little bit clunky. The PersistenceContext was always in sync with the transaction and all changes were propagated to the database as soon as the transaction got committed. Therefore the business tier had to handle the conversation and perform all changes to the database in the final step.
This got much easier with the introduction of the unsynchronized PersistenceContext in JPA 2.1. As the name already tells you, this one is not in sync with the transaction and changes get not propagated to the database until the joinTransaction() method gets called. Therefore you can create, change and delete entities in every step of the conversation without doing any changes to the database until the final step of the conversation joins the transaction.
If you want to learn more about the other features introduced in JPA 2.1, have a look at JPA 2.1 – 12 features every developer should know and make sure to download the New Features in JPA 2.1 cheat sheet.
The example entities
Before I get into the details about unsynchronized PersistenceContexts, lets have a look at the entities I will use in this post. As you can see in the following code snippets, both entities are quite simple. The Order entity has only two properties, an id and a List of Items, and the Item entity has an id property, a reference to the Order it belongs to and the name of a product.
Modeling the conversation
I will use these two entities to model a simple shopping cart conversation. The Order gets created as soon as the user starts browsing the site. Each item that she puts into the shopping cart will be added as an Item to the Order and everything gets persisted to the database as soon as the user decides to buy.
So the conversation consists of 4 different requests to the stateful ShoppingCart EJB which create the Order, 2 Items and trigger the persist to the database in the final request.
Implementing such a conversation with an unsynchronized PersistenceContext is quite easy. As I explained in the beginning, the unsynchronized PersistenceContext does not get synced to an ongoing transaction until the joinTransaction() method gets called. That also means that the changes to the PersistenceContext do not get propagated to the database until it is joined with the transaction. So you can create the Order entity in the first request and create 2 Item entities in the second and third one without propagating any changes to the database. The newly created entities will be written to the database in the fourth request, which calls the joinTransaction() method on the EntityManager which joins the PersistenceContext with the current transaction.
Lets have a more detailed look at different steps and check in the log file how the persistence provider (in my case Hibernate) interacts with the database.
Create an unsynchronized PersistenceContext
The first thing you have to do is obviously to create the unsychronized PersistenceContext. As usual in a Java EE environment, you can do this by annotating an EntityManager property with the @PersistenceContext annotation.
As you can see in the code snippet, I set the synchronization and type properties on the annotation. The first one defines that this PersistenceContext should not be synchronized with the transaction and the second one keeps the PersistenceContext alive after the transaction has finished. This is required because every step of the conversation will run within its own transaction.
OK, now we can use this PersistenceContext to implement the conversation.
Create an order
The Order entity gets created in the first request by instantiating an Order entity and providing it to the persist() method of the EntityManager.
As you can see in the log file, Hibernate selects a new value from a sequence to initialize the primary key but does not insert the Order entity into the database.
Add items to the order
In the next step, the user adds two Items to the Order. So the following method, which calls the persist() method of the EntityManager with a newly create Item entity, gets called twice.
The output in the log file is similar to the creation of the Order entity. Hibernate only selects the id value from the sequence but does not propagate the new entities to the database.
Propagate the changes
OK, the user now decides to buy the content of the shopping cart and we need to persist the Order and its Items to the database. Therefore the PersistenceContext needs to be joined to the current transaction by calling the joinTransaction() method on the EntityManager.
As you can see in the log file, this results in 3 insert statements which propagate the created entities to the database.
As you have seen in the example, the unsynchronized PersistenceContext makes it much easier to implement conversations with multiple requests. There is no need anymore to handle everything in the business tier and perform the changes of all steps during the last step of the conversation. You can now change entities in every step of the conversation and the database will not be updated until the PersistenceContext gets joined to the transaction.
What do you think about this feature? Have you already used it in one of your projects? Please leave me a comment, I would love to hear about it.
And don’t forget to download your New Features in JPA 2.1 cheat sheet. 😉