Enterprise Java

Pre Java EE 7 alternative to JPA 2.1 unsynchronized persistence context

Unsynchronized persistence context in Java EE 7

JPA 2.1 introduced the concept of unsynchronized persistence context which allows fine grained control over flushing of the JPA Entity Manager i.e. by explicitly calling EntityManager#joinTransaction. Previously, this was defaulted to end of JTA transaction e.g. in a typical Stateless EJB, the entity manager would flush its state to the DB at the end of a method (which starts and ends a transaction by default). You can read more about this, here and here.

Possible in the pre Java EE 7 era as well (both EE 5 and EE 6)

Java EE 5 and 6 have can be tweaked to achieve the same result as attained by the Unsynchronized Persistence Context in Java EE 7

Imagine a use case where customer details are being edited in a sequential manner (using a wizard like flow) e.g. address info in screen 1, contact info in screen 2 etc. You would want to save the state of the each category as and when the customer enters is but do not wish to push the entire state to the DB until the process is complete i.e. info for all the categories is entered by the user

package com.abhirockzz.conversationalee;

import com.abhirockzz.conversationalee.entity.Customer;
import java.util.Date;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;

@Stateful
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class CustomerEditorFacade{
 
  @PersistenceContext(type = PersistenceContextType.EXTENDED)
  EntityManager em;
  
  @Inject //this won't work in Java EE 5
  Principal authenticatedUser;
  
  private Customer customer;
  
  @PostConstruct
  public void init(){
      System.out.println("CustomerEditorFacade created at " + new Date().toString());   
  }
  
  @PreDestroy
  public void destroy(){
      System.out.println("CustomerEditorFacade destroyed at " + new Date().toString());   
  }
  
  //step 1
  public void updateCity(String custID, String city){
    String custID = authenticatedUser.getName(); //assume we have an authenticated principal which is the same as the customer ID in the Database
    Customer customerFromDB = em.find(Customer.class, Integer.valueOf(custID)); //obtain a 'managed' entity
    customerFromDB.setCity(city); //no need to call em.persist
    customer = customerFromDB; //just switch references
    
    //Customer state will NOT be pushed to DB
  }

  //step 2
  public void updateEmail(String email){
    customer.setEmail(email); //not pushed to DB yet
  }
  
  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void save(){
    //dummy method to trigger transaction and flush EM state to DB
  }
  
  @Remove
  public void finish(){
    //optional method to provide a way to evict this bean once used
    //not required if this is session scoped
  }

}

The code comments are self explanatory (hopefully)

Cheers!

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button