Monday, September 28, 2009

How to Use Java Persistence API without other EJB Stuff

In EJB 3.0, the Entity beans are replaced by the Java Persistence API entities. An application can use JPA without touching any EJB stuff. The following is such an example about how to use JPA in a web application.

JPA Configuration

The JPA needs a file persistence.xml. Note that the transaction-type is "RESOURCE_LOCAL"
<?xml version="1.0"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
 version="1.0">
 <persistence-unit name="myService"
  transaction-type="RESOURCE_LOCAL">
  <class>examples.ejb.ejb30.domain.Item</class>
  <properties>
   <property name="xyz.ConnectionURL" value="..." />
   <property name="xyz.ConnectionDriverName" value="..." />
   <property name="xyz.ConnectionUserName" value="examples" />
   <property name="xyz.ConnectionPassword" value="examples" />
   <property name="xyz.jdbc.SynchronizeMappings"
    value="refresh" />
  </properties>
 </persistence-unit>
</persistence>

Web Module Configuration

In web.xml, a listener is needed:
<listener>
  <listener-class>example.ejb30.web.MyContextInitializer</listener-class>
</listener>
When initialized, the initializer will create an EntityManagerFactory and put it into the ServletContext. The code can be like the following. Notice that there is no object injection and JNDI lookup here.
public class MyContextInitializer implements ServletContextListener {
  private EntityManagerFactory emf;

  public void contextInitialized(ServletContextEvent servletContextEvent) {
    emf = Persistence.createEntityManagerFactory("myService");
    ......
    ServletContext context = servletContextEvent.getServletContext();
    context.setAttribute("coolService",emf);
}

  public void contextDestroyed(ServletContextEvent servletContextEvent) {
    //close EMF to free these resources maintained by EntityManagerFactory instances.
    emf.close();
  }
}

Using the JPA in Application Code

In a jsp file, it can be used as follows:
<%
EntityManagerFactory emf = (EntityManagerFactory) application.getAttribute("coolService");
MyServiceImpl service = new MyServiceImpl(emf);

service.doSomeBusiness();

%>
The method doSomeBusiness() needs to manage the transaction. The following is a sample method:
public Artist newArtist(String name) {
  if (name == null || name.trim().length() == 0)
   throw new RuntimeException("[" + name + "] can not be an Artist's name");
  EntityManager em = getEntityManager();
  beginTransaction();
  Artist artist = getPerson(Artist.class, name);
  if (artist == null) {
    artist = new Artist(name);
    em.persist(artist);
    commit();
  } else {
    rollback();
    throw new ObjectExistedException("Artist named [" + name + "] exists");
  }
  return artist;
}

protected final EntityManager beginTransaction() {
  EntityManager em = getEntityManager();
  if (!_isManaged && !em.getTransaction().isActive())
    em.getTransaction().begin();
  return em;
}

protected final void commit() {
  if (_isManaged)
    return;
  EntityManager em = getEntityManager();
  if (em.getTransaction().isActive())
    em.getTransaction().commit();
}

protected final void rollback() {
  if (_isManaged)
    return;
  EntityManager em = getEntityManager();
  if (em.getTransaction().isActive())
    em.getTransaction().rollback();
}
The tricky thing is how the method getEntityManager() is implemented by MyServiceImpl. The
weblogic 10 ejb3 example uses the following:
private final EntityManager getEM() {
  if (_em != null)
    return _em;
  if (_thread == null)
    _thread = new ThreadLocal<EntityManager>();
  EntityManager em = _thread.get();
  if (em != null)
    return em;
  if (_emf == null)
    return null;
  em = _emf.createEntityManager();
  _thread.set(em);
  return em;
}
Notice that ThreadLocal is used here. I think this is to ensure that only one EntityManager is used in the thread. And this is to resolve the thread safety issue for EntityManager because EntityManager is not thread-safe.

No comments:

Post a Comment