EntityManager

EntityManager in the JPA provider maintains a cache of the entity instances. It is solely responsible for mapping the Java entity bean to and from the database, using the JPA provider solution.

Persistence context

Persistence context is associated with an entity manager. Its job is to allow the entity manager to reference entity beans that are created, updated, retrieved, and deleted. It also has another job, and that is to merge entity beans that are detached with the current instance from the underlying database.

The EntityManager methods

EntityManager has several methods to handle persistence of entity beans. The entity manager in JPA is always associated with one single persistence context. Inside the persistence context, there are zero or more entity bean instances, which must be unique in terms of storage to a database.

Developers program against the EntityManager API in order to create and remove the persistent entity instances, to find entities by their primary key, and to query over entities.

A simplified view of the EntityManager interface is as follows:

packagejavax.persistence;
import java.util.Map;
import javax.persistence.metamodel.Metamodel;
import javax.persistence.criteria.*;

public interface EntityManager {
  void persist(Object entity);
  <T> T merge(T entity);
  void refresh(Object entity);
  void remove(Object entity);
  
  void clear();
  void detach(Object entity);
  boolean contains(Object entity);
  
  void flush();
  void setFlushMode(FlushModeType flushMode);
  FlushModeType getFlushMode();
  
  <T> T find(Class<T> entityClass, Object primaryKey);
  <T> T find(Class<T> entityClass, Object primaryKey, Map<String, Object> properties);
  <T>T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode);
  <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode, Map<String, Object> properties);
  <T> T getReference(Class<T> entityClass, Object primaryKey);
  
  void lock(Object entity, LockModeType lockMode);
  void lock(Object entity, LockModeType lockMode,
  Map<String, Object> properties);
  
  void refresh(Object entity, Map<String, Object> properties);
  void refresh(Object entity, LockModeTypelockMode);
  void refresh(Object entity, LockModeType lockMode, Map<String, Object> properties);
  
  LockModeType getLockMode(Object entity);
  
  void setProperty(String propertyName, Object value);
  
  Map<String, Object>getProperties();
  
  Query createQuery(String qlString);
  Query createNamedQuery(String name);
  
  <T> TypedQuery<T>createQuery(CriteriaQuery<T>criteriaQuery);
  <T> TypedQuery<T>createQuery(String qlString, Class<T>resultClass);
  <T> TypedQuery<T>createNamedQuery(String name, Class<T> resultClass);
  
  Query createNativeQuery(String sqlString);
  Query createNativeQuery(String sqlString, Class resultClass);
  Query createNativeQuery(String sqlString, String resultSetMapping);
  
  voidjoinTransaction();
  void close();
  boolean isOpen();
  
  EntityTransactiongetTransaction();
  EntityManagerFactory getEntityManagerFactory();
  
  CriteriaBuildergetCriteriaBuilder();
  Metamodel getMetamodel();
  }

When you first look at this interface, you are probably thinking that this is a lot to take in. The truth is, you only need to know a dozen method calls, and the rest of the API will sink in as you start to make advanced applications, because you will start to ask more questions about how to write stuff to solve a business problem.

Persisting new instances

The EntityManager.persist() method is the most important, it calls the JPA provider to manage the current entity bean. If the current entity is unmanaged, it becomes managed. The entity P is saved into the database, or before a transaction commit, or as a result of the flush operation.

Given the entity bean P, the semantics of the save or insert operation are as follows:

  • If the entity bean P is managed, then the state is unchanged; it will still be managed. If those relationships are configured to cascade (cascade=PERSIST or cascade=ALL), then the persist operation will cause other beans relevant and referenced by the input bean to become managed by the persistence context.
  • If the entity bean P is removed, then the bean becomes managed again.
  • If the entity bean P is detached, then the bean can throw an EntityExistsException exception. The result appears because the client has not called merge on the current entity instance in a cache, or it can be that the cache is stale and out of the date, or similarly the entity already has a row with the primary or foreign key constraint violation.

The reader should particularly note that the method named EntityManager.persist() will not guarantee the immediate execution of an SQL INSERT instruction. The JPA provider and its persistence manager implementation decide exactly when saving to the database is carried out. Hence the reasoning behind waiting until just before the enclosing transaction commits, or flushing the persistence context cache directly through the call.

It is possible to find out if a particular entity bean is managed or not with the method EntityManager.contains().

Removing the existing instances

The EntityManager.remove() method is a signal to the persistence entity manager to evict the entity bean from its cache, and also to eventually delete the entity from the underlying database. The removal occurs immediately, before the transaction commits, or as a result of the flush operation.

Given an entity bean P, the removal operation semantics are as follows:

  • If the entity P is a new instance, then it is ignored by the remove operation. However, if P has references to other entities that are managed by the entity manager, and if those relationships are set to cascade (cascade=REMOVE or cascade=ALL), then those dependent elements are also set to be removed.
    Removing the existing instances
  • If the entity P is a managed instance, then it is switched to the remove state, a signal for the eventual deletion from the persistence storage. If P has references to other entities that are managed by the entity manager, and if those relationships are set to cascade (cascade=REMOVE or cascade=ALL), then those dependent elements are set as pending removal.
  • If the entity P is detached from a persistence context, then the JPA provider will raise an IllegalArgumentException exception.
  • If the entity P is already set for removal, then nothing happens, the behavior is a no-operation (no-op). The dependencies of P, if any, are considered for removal.

Refreshing the entity bean instances

The EntityManager.refresh() method causes an entity bean to be refreshed from the database. For a managed entity bean, calling this method causes data to be overridden from the table columns in the current database. The refresh operation can also cascade to other instances, if those instances are annotated with cascade=REFRESH or cascade=ALL.

If the entity bean is in the new, detached, or removed states, then the JPA provider will raise an IllegalArgumentException exception.

Detaching the entity bean instances

The entity bean is detached if and when the instance is disassociated from its corresponding persistence unit. Detachment can occur, if the entity bean is part of transaction, which is rolled back. The reference to the entity bean will no longer valid. Application call themselves invoking EntityTransaction.rollback(). The application can detach entity from the persistent unit by calling EntityManager.detach(). Finally, an entity becomes detached if it is serialized over the network from the server to the client, or simply removed from the entity manager EntityManager.remove().

Once the entity bean becomes detached, it will not track persistence context and the entity manager. Nor will there be any additional proxy operations applied to it. This means when accessing the detached bean with a dependent field or properties that reference other entities-especially if they are designated as LAZY-those references may be out-of-date or may even be null, that is they are simply not accessible. Rephrased in another way, the JPA provider is not able to lazily bind fields or accessor properties that were not bound before the entity detachment.

In this situation, if you want to reassociate a detached entity bean with a persistence context, then you must call the method EntityManager.merge(). The merge operation propagates the state of the detached entities into the persistent entities managed by the entity manager. It is important to note that the changes in the detached entity are not propagated immediately to the persistent storage. The changes are reflected in the entity instance in memory. The changes are saved to the database by calling EntityManager.persist() separately.

Given the entity bean P, the semantics of the merge operation are as follows:

  • If P is a detached bean, the state of P is copied onto a pre-existing managed instance P'.
  • If P is a new instance, the state of P is copied onto a new managed instance P'.
  • If P is a removed instance, the JPA provider will raise an IllegalArgumentException exception.
  • If P is a managed instance, JPA will ignore the merge operation. However, it will cascade the merge operation to any dependent entity beans, if they are annotated with cascade=MERGE or cascade=ALL.

The JPA provider does not merge the fields that have been marked Eager.LAZY, especially those fields that have not been fetched at the time of the call.

Note

Merging between the JPA provider implementations is certainly not guaranteed in the specification. If you truly want such a feature, then it might be best to look at marshaling the entity bean into the XML configuration or the JSON dump.

We will deal with several of the other important methods on the EntityManager interface in Chapter 5, Object-Relational Mapping with JPA.

Flushing the pending instances to the database

EntityManager.flush() causes the JPA provider and persistence context to attempt to save the current changing instances to the database. This call is useful for long transactions and when you would like to ensure that some SQL operations actually hit the database store. However, until the transaction commits (or rolls back), if the underlying database follows the ACID (Atomicity, Consistency, Isolation, and Durability) standards, then no other database sessions will be able to see the updated changes to the affected database tables and rows.

The standard does not mandate what and when the entity beans should be flushed to the database. Calling EntityManager.flush() is a hint to the JPA provider to initiate writing the SQL INSERT and SQL UPDATE statements to the database driver as soon as possible. It is certainly not an immediate call to action.

Transactional support

For the Java EE applications that execute inside a Java EE 7 application server product, the container provides transactions. This is known as container manager entity management. The instance of EntityManager and the associated persistence context is available to all the application components that either inject it or programmatically look it up and use the manager. The entity manager is associated with a single JTA transaction for the lifetime of the service request.

As we have already seen in the stateful bean example, SpyThrillerBean, we can rely on an upstream component to demarcate the boundaries of a transaction. The upstream component may be Servlet, another session bean, or even a web service, or a RESTful service endpoint.

Application managed transactions

It is useful to know how JPA transactions work in a standalone Java SE application. Custom transaction management is also useful in certain Java EE applications, where there is a business requirement to roll your own transaction.

The application is responsible for creating and destroying an EntityManager and associated persistence context. In order to create a new entity manager, the application retrieves a reference to the factory class of the JPA provider in a portable fashion. An application can inject javax.persistence.EntityManagerFactory into the component.

@PesistentUnit EntityManagerFactory emf;
EntityManager em;

public void createEntityManager() {
  em = emf.createEntityManager();
  }

Once we have a new entity manager in the bean, then we need an instance of the Java Transaction API user transaction instance. We can inject the instance of javax.transaction.UserTransaction in the application component, then we associate it to the entity manager in the business logic method.

An example source code fragment, which shows how to begin and end a user-defined transaction, using an injected JTA instance is as follows:

@PesistentUnit EntityManagerFactory emf;
EntityManager em;

@Resource UserTransaction utx;

public void createEntityManager() {
  em = emf.createEntityManager();
  }

public void performSomeWork() {
  em = emf.createEntityManager();
  try {
    utx.begin(); // Start TX
    
    em.persist(payrollEntity);
    em.merge(customerEntity);
    em.remove(trackingEntity);
    
    utx.commit(); // End TX
    } catch (Exception e) {
    utx.rollbaback(); // End TX
    }
  }

Because UserTransaction is a system wide component in that it is supplied by the application server, we inject it as a dependency using @javax.annotation.Resource.

The definition of UserTransaction for reference is as follows:

package javax.transaction;

public interface UserTransaction {
  void begin() throws NotSupportedException, SystemException;
  void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException;
  void rollback() throws IllegalStateException, SecurityException, SystemException;
  void setRollbackOnly() throws IllegalStateException, SystemException;
  int getStatus() throws SystemException;
  void setTransactionTimeout(int i) throws SystemException;
  }

You can find out more about transactions in Appendix C, Java EE 7 Transactions.

Retrieving an EntityManager by injection

We have already seen how a stateful session bean can obtain an EntityManager. We used the EJB container injection mechanism to do this as follows:

@Stateless
public class DepositAccountManager {
  @PersistenceContext("retail-north-west")
  private EntityManager em;
  
  /* ... */
  public void switchDepositToSaving( Sting acc) {
    Account account = em.createQuery("select d from Account d "+ "where d.account = :account");
    .setParameter("account", acc)
    .getResultList().get(0)
    account .setAccountType(AccountType.SAVING)
    em.persist(account);
    }
  }

Here we have a fictional stateless session bean DepositAccountManager with a transaction local persistence context injected into it. The method switchDepositToSaving() executes a JPQL query that retrieves one record from the database. We switch record to a saving account, and then save the record back to the store.

Notice how we are able to parameterize the account by supplying it as an argument to the query, and also the syntax for the tokenized arguments in the dynamic JPQL string.

Retrieving an EntityManager by factory

It is also possible to retrieve an EntityManager from EntityManagerFactory inside an application that runs inside an application server. Here is a new version of the spy thriller book stateful session bean that illustrates the technique of using a persistence unit factory in order to acquire the entity manager. The session bean named SpyThrillerBookWithFactoryCreationBean is as follows:

packageje7hb.basic.jpa;
import javax.annotation.*;
import javax.ejb.*;
import javax.persistence.*;
import java.util.List;
@Stateful
public class SpyThrillerBookWithFactoryCreationBean {
  @PersistenceUnit(unitName = "testDatabase")
  privateEntityManagerFactory factory;
  private EntityManager em;
  
  @PostConstruct
  public void init() {
    em = factory.createEntityManager();
    }
  
  @PreDestroy
  @Remove
  public void destroy() {
    em.close();
    }
  
  public void addBook(SpyThriller movie) throws Exception {
    em.persist(movie);
    em.flush();
    }
  
  public void deleteBook(SpyThriller movie)
  throws Exception {
    em.remove(movie);
    em.flush();
    }
  
  public List<SpyThriller> getBooks() throws Exception {
    Query query = em.createQuery("SELECT m from SpyThriller as m");
    return query.getResultList();
    }
  }

The annotation @javax.persistence.PersistenceUnit injects a reference to the persistence unit in the session bean.

We also add the lifecycle handle methods with annotations @PostConstruct, @PreDestroy, and @Remove to respectively create the entity manager, and close it after the user has finished with the bean.

In the preceding code, we compensated for the lack of a way to programmatically instantiate an EntityManager with an explicit extended transaction persistence context. Hence, we have extra calls to Entity.flush(), immediately after the save and delete operations.

Retrieving an EntityManager by the JNDI lookup

The final way of obtaining an EntityManager is programmatic. We can acquire an entity manager instance by using the JNDI lookup.

An example of using the JNDI lookup in a session bean, which is a rewrite of SpyThrillerBook from earlier, is as follows:

package je7hb.basic.jpa;
import javax.annotation.Resource;
import javax.ejb.*;
import javax.persistence.*;
import java.util.List;

@Stateful
@PersistenceContext(unitName = "testDatabase", name = "myLookupName", type = PersistenceContextType.EXTENDED)
public class SpyThrillerBookWithJNIDILookupBean {
  
  @Resource private SessionContext ctx;
  
  public EntityManager getEntityManager() {
    return (EntityManager)ctx.lookup("myLookupName");
    }
  public void addBook(SpyThriller movie) throws Exception {
    getEntityManager().persist(movie);
    }
  
  public void deleteBook(SpyThriller movie)
  throws Exception {
    getEntityManager().remove(movie);
    }
  
  public List<SpyThriller> getBooks() throws Exception {
    Query query = getEntityManager().createQuery("SELECT m from SpyThriller as m");
    return query.getResultList();
    }
  }

We apply the @PersistenceContext annotation of the type of the session bean; here the class is named SpyThrillerBookWithJNIDILookupBean. This annotation takes three arguments: the name of the persistence unit, testDatabase, the JNDI name that is exported, myLookupBean, and the persistent unit type, EXTENDED.

We also inject the javax.ejb.SessionContext object into the bean from the EJB container. It is this object that provides a method to look up the persistence unit. The entity manager is programmatically found by the exported JNDI name in the @PersistenceContext annotation.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.153.212