| |

Unsynchronized PersistenceContext – How to model conversations with JPA


Take your skills to the next level!

The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.


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.

@Entity(name="my_order")
public class Order {
	   
	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE)
	@Column(name = "id", updatable = false, nullable = false)
	private Long id;

	@OneToMany(mappedBy = "order")
	private List<Item> items = new ArrayList<Item>();
	
	...
}
@Entity
public class Item {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "id", updatable = false, nullable = false)
	private Long id;
	
	@Column
	private String product;

	@ManyToOne
	@JoinColumn(name = "fk_order")
	private Order order;
	
	...
}

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.

// 1. create the Order
cart.start();

// 2. add one Item to the Order
cart.addItem("myFirstProduct");
// 3. add additional Item to the Order
cart.addItem("mySecondProduct");

// 4. write Order with its Items to the database
cart.persist();

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.

@Stateful
public class ShoppingCart implements ShoppingCartRemote {

	Logger log = Logger.getLogger(this.getClass().getSimpleName());
	
	@PersistenceContext(synchronization = SynchronizationType.UNSYNCHRONIZED, type = PersistenceContextType.EXTENDED)
	EntityManager em;
	Order o;
	
	@Override
	public void start() {
		log.info("Create order");
		this.o = new Order();
		this.em.persist(o);
	}
	
	@Override
	public void addItem(String product) {
		log.info("Create item for product "+product);
		Item i = new Item();
		i.setProduct(product);
		i.setOrder(o);
		o.getItems().add(i);
		this.em.persist(i);
	}
	
	@Override
	public void persist() {
		log.info("Join transaction and write changes to the database");
		this.em.joinTransaction();
	}
}

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.

@PersistenceContext(synchronization = SynchronizationType.UNSYNCHRONIZED, type = PersistenceContextType.EXTENDED)
EntityManager em;

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.

public void start() {
	log.info("Create order");
	this.o = new Order();
	this.em.persist(o);
}

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.

14:30:17,454 INFO  [ShoppingCart] (default task-1) Create order
14:30:17,464 DEBUG [org.hibernate.SQL] (default task-1) 
    select
        nextval ('hibernate_sequence')

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.

@Override
public void addItem(String product) {
	log.info("Create item for product "+product);
	Item i = new Item();
	i.setProduct(product);
	i.setOrder(o);
	o.getItems().add(i);
	this.em.persist(i);
}

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.

14:30:17,539 INFO  [ShoppingCart] (default task-1) Create item for product myFirstProduct
14:30:17,540 DEBUG [org.hibernate.SQL] (default task-1) 
    select
        nextval ('hibernate_sequence')
        
14:30:17,548 INFO  [ShoppingCart] (default task-1) Create item for product mySecondProduct
14:30:17,548 DEBUG [org.hibernate.SQL] (default task-1) 
    select
        nextval ('hibernate_sequence')

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.

public void persist() {
	log.info("Join transaction and write changes to the database");
	this.em.joinTransaction();
}

As you can see in the log file, this results in 3 insert statements which propagate the created entities to the database.

14:30:17,694 INFO  [ShoppingCart] (default task-1) Join transaction and write changes to the database
14:30:17,728 DEBUG [org.hibernate.SQL] (default task-1) 
    insert 
    into
        my_order
        (id) 
    values
        (?)

14:30:17,739 DEBUG [org.hibernate.SQL] (default task-1) 
    insert 
    into
        Item
        (fk_order, product, id) 
    values
        (?, ?, ?)

14:30:17,743 DEBUG [org.hibernate.SQL] (default task-1) 
    insert 
    into
        Item
        (fk_order, product, id) 
    values
        (?, ?, ?)

Summary

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. 😉

5 Comments

  1. Avatar photo Allan Jun Li says:

    Nice post, very clear.

    But given the example, why do we even need to use this feature?
    Define cascade type in your relationship mapping and don’t call em.persist in start() and addItem() method, only call em.persist(o) in persist() method, it would achieve the same result.

    To demo why unsynchronized em is useful, better not keep Order object in your SFSB, instead pass in order id when adding item.

    1. Avatar photo Thorben Janssen says:

      The main idea of the unsynchronized persistence context is to allow you to perform operations on the context without interacting with the database. That allows you to store the Order object in a stateful bean, use it in multiple calls (client calls backend multiple times) without storing anything in the database and give you the control to trigger the flush operation.

      Normally, the persistence context gets closed at the end of the transaction (when you return a response to the client). That’s why you can’t use cascading. It would write all changes to the database when it commits the transaction before it returns the response to the client.

  2. Hi Thorben

    Nice post ! I do have a question though.

    Is it not possible to achieve this without unsynchronized persistence context i.e. using Java EE 5/6 itself ? By forcing the start and addItem method to be invoked without a transaction by using @javax.ejb.TransactionAttributeType.NOT_SUPPORTED annotation and having the @javax.ejb.TransactionAttributeType.REQUIRES_NEW annotation on the persist method which would automatically kick start the transaction and flush Entity Manager state

    Just a thought. Let me know what you think! Like your blog 🙂

    1. Avatar photo Thorben Janssen says:

      Hi Abhishek,

      that approach should also work. One drawback would be that everything that you do within the non transactional methods would run without a transaction (obviously 😉 ). That can be an issue, if you want to do anything that requires a transaction, like user tracking in this example.

      But in general yes, you should be able to achieve the same with a good combination of non transactional and transactional methods. The unsynchronized persistence context might be a little bit easier.

Comments are closed.