Thursday 13 August 2009

JPA: Digging beyond the entities.



1. Introduction



The idea behind this article is to summarize a set of important concepts related to the Java Persistence API. I am not trying to reinvent the wheel writing something from the scratch. Therefore the text is based on the JSR 220.
2. JPA Concepts
Entities are managed by the entity manager. The entity manager is represented by javax.persistence.EntityManager instances. Each EntityManager instance is associated with a persistence context. A persistence context defines the scope under which particular entity instances are created, persisted, and removed.
A persistence context is a set of managed entity instances that exist in a particular data store. The EntityManager interface defines the methods that are used to interact with the persistence context.
The EntityManager API creates and removes persistent entity instances, finds entities by the entity’s primary key, and allows queries to be run on entities.
Until an entity manager is used, the entity is nothing more than a regular (non-persistent) Java object.
When an entity manager obtains a reference to an entity, either by having it explicitly passed in or because it was read from the database, that object is said to be managed by the entity manager. The set of managed entity instances within an entity manager at any given time is called its persistence context. Only one Java instance with the same persistent identity may exist in a persistence context at any time.
Entity managers are configured to be able to persist or manage specific types of objects, read and write to a given database, and be implemented by a particular persistence provider. It is the provider that supplies the backing implementation engine for the entire Java Persistence API, from the EntityManager through to Query implementation and SQL generation.
All entity managers come from factories of type EntityManagerFactory. The configuration for an entity manager is bound to the EntityManagerFactory that created it, but it is defined separately as a persistence unit. A persistence unit dictates either implicitly or explicitly the settings and entity classes used by all entity managers obtained from the unique EntityManagerFactory instance bound to that persistence unit. There is, therefore, a one-to-one correspondence between a persistence unit and its concrete EntityManagerFactory.




3. Container-managed entity manager
With a container-managed entity manager, an EntityManager instance’s persistence context is automatically propagated by the container to all application components that use the EntityManager instance within a single Java Transaction Architecture (JTA) transaction.
A container-managed entity manager is obtained by the application either using injection
@PersitenceContext EntityManager em;




or using JNDI lookup
@Stateless
@PersistenceContext(name=”OrderEM”)
Public class MySessionBean implements MyInterface {
@Resource SessionContext ctx;
Public void doSomething(){
EntityManager em = (EntityManager) ctx.lookup(“OrderEM”);
}
}
  • Container-managed Transaction-scoped Persistence Context
A new persistence context begins when the container-managed entity manager is invoked in the scope of an active JTA transaction, and there is no current persistence context already associated with the JTA transaction. The persistence context is created and then associated with the JTA transaction.
The persistence context ends when the associated JTA transaction commits or rolls back, and all entities that were managed by the EntityManager become detached.
If the entity manager is invoked outside the scope of a transaction, any entities loaded from the database will immediately become detached at the end of the method call.

Case 1
@Stateless
public class ShoppingCartImpl implements ShoppingCart {
@PersistenceContext
EntityManager em;
public Order getOrder(Long id) {
return em.find(Order.class, id);
}
public Product getProduct(String name) {
return (Product) em.createQuery("select p from Product p where p.name = :name")
.setParameter("name", name)
.getSingleResult();
}
public LineItem createLineItem(Order order, Product product, int quantity) {
LineItem li = new LineItem(order, product, quantity);
order.getLineItems().add(li);
em.persist(li);
return li;
}
}
  • Container-managed Extended Persistence Context
A Container-managed extended persistence context can only be initiated within the scope of a stateful session bean. It exists from the point at which the stateful session bean that declares a dependency on an entity manager of type PersistentContextType.EXTENDED is created, and is said to be bound to the stateful session bean.
The persistence context is closed by the container when @Remove method of the stateful session bean completes (or the stateful session bean instance is otherwise destroyed).
Case 1



@
Stateful
@Transaction(REQUIRES_NEW)
public class ShoppingCartImpl implements ShoppingCart {
@PersistenceContext(type = EXTENDED)
EntityManager em;
private Order order;
private Product product;
public void initOrder(Long id) {
order = em.find(Order.class, id);
}
public void initProduct(String name) {
product = (Product) em.createQuery("select p from Product p where p.name = :name")
.setParameter("name", name)
.getSingleResult();
}
public LineItem createLineItem(int quantity) {
LineItem li = new LineItem(order, product, quantity);
order.getLineItems().add(li);
return li;
}
}

4. Application-managed entity manager

With application-managed entity managers, on the other hand, the persistence context is not propagated to application components, and the life cycle of EntityManager instances is managed by the application.
Application-managed entity managers are used when applications need to access a persistence context that is not propagated with the JTA transaction across EntityManager instances in a particular persistence unit. In this case, each EntityManager creates a new, isolated persistence context. The EntityManager, and its associated persistence context, is created and destroyed explicitly by the application.
Applications create EntityManager instances in this case by using the createEntityManager method of javax.persistence.EntityManagerFactory.
To obtain an entity manager factory in a Java EE Container either using injection


@PersistenceUnit
EntityManagerFactory emf;


To obtain an entity manager factory in a Java SE Container:

EntityManagerFactory emf = javax.persistence.Persistence.createEntityManagerFactory(“Order”);
EntityManager em = emf.createEntityManager();

  • Application-managed persistent context
When an application-managed entity manager is used, the application interacts directly with the persistence provider's entity manager factory to manage the entity manager lifecycle and to obtain and destroy persistence contexts.
All such application-managed persistence contexts are extended in scope, and may span multiple transactions.
The EntityManager close and isOpen methods are used to manage the lifecycle of an application-managed entity manager and its associated persistence context.
The EntityManager.close method closes an entity manager to release its persistence context and other resources. If the close method is invoked when a transaction is active, the persistence context remains managed until the transaction completes.
The extended persistence context exists from the point at which the entity manager has been created using EntityManagerFactory.createEntityManager until the entity manager is closed by means of EntityManager.close. The extended persistence context obtained from the application-managed entity manager is a stand-alone persistence context—it is not propagated with the transaction.
When a JTA application-managed entity manager is used, if the entity manager is created outside the scope of the current JTA transaction, it is the responsibility of the application to associate the entity manager with the transaction (if desired) by calling EntityManager.joinTransaction.
Case 1 - Application-managed Persistence Context used in Stateles Session Bean




/*
* Container-managed transaction demarcation is used.
* Session bean creates and closes an entity manager in
* each business method.
*/
@Stateless
public class ShoppingCartImpl implements ShoppingCart {
@PersistenceUnit
private EntityManagerFactory emf;
public Order getOrder(Long id) {
EntityManager em = emf.createEntityManager();
Order order = (Order) em.find(Order.class, id);
em.close();
return order;
}
public Product getProduct() {
EntityManager em = emf.createEntityManager();
Product product = (Product) em.createQuery("select p from Product p where p.name = :name")
.setParameter("name", name)
.getSingleResult();
em.close();
return product;
}
public LineItem createLineItem(Order order, Product product, int quantity) {
EntityManager em = emf.createEntityManager();
LineItem li = new LineItem(order, product, quantity);
order.getLineItems().add(li);
em.persist(li);
em.close();
return li; // remains managed until JTA transaction ends
}
}

Case 2 - Application-managed Persistence Context used in Stateless Session Bean

/*
* Container-managed transaction demarcation is used.
* Session bean creates entity manager in PostConstruct
* method and clears persistence context at the end of each
* business method.
*/
@Stateless
public class ShoppingCartImpl implements ShoppingCart {
@PersistenceUnit
private EntityManagerFactory emf;
private EntityManager em;
@PostConstruct
public void init()
em = emf.createEntityManager();
}
public Order getOrder(Long id) {
Order order = (Order)em.find(Order.class, id);
em.clear(); // entities are detached
return order;
}
public Product getProduct() {
Product product = (Product) em.createQuery("select p from Product p where p.name = :name")
.setParameter("name", name)
.getSingleResult();
em.clear();
return product;
}
public LineItem createLineItem(Order order, Product product, int quantity) {
em.joinTransaction();
LineItem li = new LineItem(order, product, quantity);
order.getLineItems().add(li);
em.persist(li);
// persistence context is flushed to database;
// all updates will be committed to database on tx commit
em.flush();
// entities in persistence context are detached
em.clear();
return li;
}
@PreDestroy
public void destroy()
em.close();
}
}

Case 3 - Application-managed Persistence Context used in Stateful Session Bean


//Container-managed transaction demarcation is used
@Stateful
public class ShoppingCartImpl implements ShoppingCart {
@PersistenceUnit
private EntityManagerFactory emf;
private EntityManager em;
private Order order;
private Product product;
@PostConstruct
public void init() {
em = emf.createEntityManager();
}
public void initOrder(Long id) {
order = em.find(Order.class, id);
}
public void initProduct(String name) {
product = (Product) em.createQuery("select p from Product p where p.name = :name")
.setParameter("name", name)
.getSingleResult();
}
public LineItem createLineItem(int quantity) {
em.joinTransaction();
LineItem li = new LineItem(order, product, quantity);
order.getLineItems().add(li);
return li;
}
@Remove
public void destroy() {
em.close();
}
}

Case 4 - Application-managed Persistence Context with Resource Transaction
// Usage in an ordinary Java class
public class ShoppingImpl {
private EntityManager em;
private EntityManagerFactory emf;
private Order order;
private Product product;
public ShoppingCart() {
emf = Persistence.createEntityManagerFactory("orderMgt");
em = emf.createEntityManager();
}
public void initOrder(Long id) {
order = em.find(Order.class, id);
}
public void initProduct(String name) {
product = (Product) em.createQuery("select p from Product p where p.name = :name")
.setParameter("name", name)
.getSingleResult();
}
public LineItem createLineItem(int quantity) {
em.getTransaction().begin();
LineItem li = new LineItem(order, product, quantity);
order.getLineItems().add(li);
em.getTransaction().commit();
return li;
}
public void destroy() {
em.close();
emf.close();
}
}
5. Controlling Transactions

Depending on the transactional type of the entity manager, transactions involving EntityManager operations may be controlled either through JTA (JTA entity manager) or through use of the resource-local EntityTransaction API (resource-local entity manager), which is mapped to a resource transaction over the resource that underlies the entities managed by the entity manager.

The EntityTransaction interface is used to control resource transactions on resource-local entity managers. The EntityManager.getTransaction() method returns the EntityTransaction interface.
When a resource-local entity manager is used, and the persistence provider runtime throws an exception defined to cause transaction rollback, it must mark the transaction for rollback.
If the EntityTransaction.commit operation fails, the persistence provider must roll back the transaction.

A container-managed entity manager must be a JTA entity manager. JTA entity managers are only specified for use in Java EE containers.
An application-managed entity manager may be either a JTA entity manager or a resource-local entity manager.
An entity manager is defined to be of a given transactional type—either JTA or resource-local—at the time its underlying entity manager factory is configured and created.

Both JTA entity managers and resource-local entity managers are required to be supported in Java EE web containers and EJB containers.Within an EJB environment, a JTA entity manager is typically used.
In general, in Java SE environments only resource-local entity managers are supported.


Example
The following example illustrates the creation of an entity manager factory in a Java SE environment, and its use in creating and using a resource-local entity manager.
import javax.persistence.*;
public class PasswordChanger {
public static void main (String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("Order");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
User user = (User)em.createQuery("SELECT u FROM User u WHERE u.name=:name AND u.pass=:pass")
.setParameter("name", args[0])
.setParameter("pass", args[1])
.getSingleResult();
if (user!=null) {
user.setPassword(args[2]);
}
em.getTransaction().commit();
em.close();
emf.close ();
}
}


3 comments: