Table of Contents

About

Stateless EJB session beans as Data Access Object (DAO) implementation with JPA.

Transaction Type

RESOURCE_LOCAL

You must use the EntityManagerFactory to get an EntityManager

The resulting EntityManager instance is a PersistenceContext/Cache

An EntityManagerFactory can be injected via the @PersistenceUnit annotation only (not @PersistenceContext)

You are not allowed to use @PersistenceContext to refer to a unit of type RESOURCE_LOCAL

You must use the EntityTransaction API to begin/commit around every call to your EntityManger Calling entityManagerFactory.createEntityManager() twice results in two separate EntityManager instances and therefor two separate PersistenceContexts/Caches.

It is almost never a good idea to have more than one instance of an EntityManager in use (don't create a second one unless you've destroyed the first).

Steps

DAO

Interface

Interface definition with the JPA Entity

package com.oracle.ticketsystem.dao;

import java.util.List;
import com.oracle.ticketsystem.beans.Ticket;

/**
 * A DAO that handles the ticket related requests, 
 * for example, adding a new ticket, updating an 
 * existing ticket, getting a specific ticket, and 
 * getting list of tickets by specific state.
 *
 */

public interface ITicketDao {

         /**
	 * Adds the given ticket.
	 * 
	 * @param ticket to add
	 * @return <code>Ticket<code> reference to ticket being added
	 */

         public Ticket add(Integer productId, 
			String customerName, String customerEmail, 
			String title, String description);

	/**
	 * Updates the ticket specified with the given ID.
	 * <p>
	 * Ignores the update request (and returns null), if could not find 
	 * Ticket for the given ID.
	 * <p>
	 * It updates the current state of the ticket and also adds an entry 
	 * for the ticket history.
	 * 
	 * @param ticketId 
	 * ID of the ticket to update
	 * @param technicianId
	 * ID of technician who is updating the ticket
	 * @param comment
	 * string comment about the update
	 * @param state
	 * state to update for the ticket
	 * @return the updated <code>TicketType<code> 
	 */
	public Ticket update(Integer ticketId, String technicianId, 
				 String comment, String state);

	/**
	 * Returns <code>Ticket<code> reference for the given ticket ID, 
	 * or null if does not found.
	 * 
	 * @param ticketId
	 * ID of ticket to retrieve
	 * @return <code>Ticket<code> reference for the given ticket ID if found, 
	 * otherwise null.
	 */
	public Ticket get(Integer ticketId);

	/**
	 * Returns <code>List<code> of tickets assigned to technician with the given ID.
	 * 
	 * @param technicianId get tickets assigned to technician with the given ID
	 * @return <code>List<code> of tickets.
	 */
	public List<Ticket> getTicketsOwnedByTechnician(String technicianId);

	/**
	 * Returns <code>List<code> of open tickets i.e. either NEW or OPEN
	 * 
	 * @return a list of open tickets i.e. either NEW or OPEN
	 */
	public List<Ticket> getOpenTickets();

	/**
	 * Removes the ticket with the given ticket ID.
         * @param ticketId 
	 * the ID of the ticket to be removed
	 */
	public void remove(Integer ticketId);

}

Implementation

package com.oracle.ticketsystem.dao.impl;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import com.oracle.ticketsystem.beans.Product;
import com.oracle.ticketsystem.beans.Technician;
import com.oracle.ticketsystem.beans.Ticket;
import com.oracle.ticketsystem.beans.Tickethistory;
import com.oracle.ticketsystem.dao.ITicketDao;

public class TicketJPADao extends BaseJPADao implements ITicketDao {

        /**
	 * Date pattern used across the application.
	 */
	public static final String DATE_PATTERN = "MM/dd/yyyy hh:mm:ss aa"; //$NON-NLS-1$

	/**
	 * Default no-arg constructor
	 */
	public TicketJPADao() {

	}

	@Override
	public Ticket add(Integer productId, 
			String customerName, String customerEmail, 
			String title, String description) {

		Product product = getEntityManager().find(Product.class, productId);

		if(product == null) {

			throw new RuntimeException("While adding a new ticket, " +

					"could not find reference to the given product Id: " + productId);

		}

		Ticket ticket = new Ticket();

		ticket.setProduct(product);

		ticket.setCustomername(customerName);

		ticket.setCustomeremail(customerEmail);

		ticket.setTitle(title);

		ticket.setDescription(description);

		ticket.setState("NEW"); // always NEW state

		SimpleDateFormat dtFormat = new SimpleDateFormat(DATE_PATTERN);

		ticket.setSubmissiondate(dtFormat.format(new Date()));


		Long maxId = getMaxId("SELECT max(t.id) FROM Ticket t");

		// setting the ticket Id
		ticket.setId( (int) ((maxId == null) ? 0 : maxId + 1));

		EntityTransaction t = getEntityManager().getTransaction();

		t.begin();

		getEntityManager().persist(ticket);

		t.commit();

		return ticket;

	}


	@Override
	public Ticket get(Integer ticketId) {

		return getEntityManager().find(Ticket.class, ticketId);

	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Ticket> getTicketsOwnedByTechnician(String technicianId) {

                Query query = getEntityManager().createQuery("SELECT t from Ticket t "+
                            "WHERE t.technician.id = :technicianId");
		query.setParameter("technicianId", technicianId);

		return query.getResultList();

	}

	@Override
	public Ticket update(Integer ticketId, String technicianId, String comment,
			String state) {

		EntityTransaction t = getEntityManager().getTransaction();

		t.begin();

        	Ticket ticket = get(ticketId);

		if(ticket == null) {

			return null;

		}

		ticket.setState(state);

		Technician technician = null;

		if(technicianId != null) {

			technician = getEntityManager().find(Technician.class, technicianId);

			if(technician == null) {

				throw new RuntimeException("No technician found for the ID '" + 
                                  technicianId + "'");

			}


		}

		ticket.setTechnician(technician);

		Tickethistory ticketHistory = new Tickethistory();

		Long maxTicketHistoryId = getMaxId("SELECT max(h.id) FROM Tickethistory h");


		// setting the ticketHistory Id

		ticketHistory.setId( (int) ((maxTicketHistoryId == null) ? 0 : maxTicketHistoryId + 1));

		if(technician != null) {

			ticketHistory.setTechnician(technician);

		}

		ticketHistory.setState(state);

		ticketHistory.setComments(comment);

		ticketHistory.setTicket(ticket);

		SimpleDateFormat dtFormat = new SimpleDateFormat(DATE_PATTERN);

		ticketHistory.setUpdatedate(dtFormat.format(new Date()));

		getEntityManager().persist(ticketHistory);

		ticket.getTicketHistory().add(ticketHistory);

		t.commit();

		return ticket;

	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Ticket> getOpenTickets() {

         	Query openTicketsQuery = getEntityManager().createQuery(
			"SELECT t FROM Ticket t " +
			"WHERE t.state = :newState " +
			"OR t.state = :openState");

		openTicketsQuery.setParameter("newState", "NEW");

		openTicketsQuery.setParameter("openState", "OPEN");

		return openTicketsQuery.getResultList();

	}

	@Override
	public void remove(Integer ticketId) {

		Ticket ticket = get(ticketId);

		if(ticket != null) {

			EntityTransaction trx = getEntityManager().getTransaction();

			trx.begin();


			for(Tickethistory ticketHistory : ticket.getTicketHistory()) {

				getEntityManager().remove(ticketHistory);

			}

			ticket.getTicketHistory().clear();

			getEntityManager().remove(ticket);

			trx.commit();

		}

	}

	

	private long getMaxId(String maxQuery) {

		Query maxIdQuery = getEntityManager().createQuery(maxQuery);

		Long maxId = 1L;

		if( (maxIdQuery.getResultList() != null) && (maxIdQuery.getResultList().size() > 0) ) {

			maxId = (Long)maxIdQuery.getResultList().get(0);

		}

		return maxId;

	}

}

where BaseJPADao is the following base class to call the EntityManager

EntityManager

JPA - Entity Manager

A persistence context is a set of entity instances in which for any persistent entity identity there is a unique entity instance. Within the persistence context, the entity instances and their lifecycle are managed.

Factory

This class creates singleton instances of:

  • EntityManagerFactory
  • and EntityManager

using the persistence unit name as defined in the persistence.xml.

It has also a method to close the EntityManagerFactory for necessary clean-up during the application shutdown.

package com.oracle.ticketsystem.dao.impl;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

/**
 * A JPA DAO factory for providing reference to EntityManager.
 *
 */
public class JPADaoFactory {

  private static final String PERSISTENCE_UNIT_NAME = "TroubleTicketSystemServer";
 
  private static EntityManagerFactory entityManagerFactory;
  private static EntityManager entityManager;

  /**
   * Returns reference to EntityManager instance. If null then create it
   * using the persistence unit name as defined in the persistence.xml
   *
   * @return EntityManager
   */
  public static EntityManager createEntityManager() {
  
   if(entityManager == null) {
     entityManagerFactory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
     entityManager = entityManagerFactory.createEntityManager();
   }
   return entityManager;
  }

  public static void close() {
   entityManager.close();
   entityManagerFactory.close();
  }
 
} 

Base class

A base class for JPA DAO implementation classes.

The BaseJPADao class provides an accessor method for the EntityManager.

package com.oracle.ticketsystem.dao.impl;

import javax.persistence.EntityManager;

public class BaseJPADao {


	/**
	 * Returns JPA EntityManager reference.
	 * @return
	 */
	public EntityManager getEntityManager() {
	  return JPADaoFactory.createEntityManager();
	} 

}

JUnit Test

Junit Test:

package com.oracle.ticketsystem.dao.tests;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import com.oracle.ticketsystem.beans.Ticket;
import com.oracle.ticketsystem.dao.ITicketDao;
import com.oracle.ticketsystem.dao.impl.TicketJPADao;

public class TicketDaoTest {

	private ITicketDao ticketDao = null;
	private static Integer testTicketId = 0;

	@Before
	public void setUp() throws Exception {
		ticketDao = new TicketJPADao();
	}

	@Test
	public void testAdd() {
		
		// adding a test ticket
		Ticket ticket = ticketDao.add(1001, "Mark", "[email protected]", "TestTicket", 
                          "Ticket about the test");
		
		assertTrue("Could not add test ticket.", (ticket.getId() > 0));
		
		testTicketId = ticket.getId();
		
	}

	@Test
	public void testGet() {
		
		// finding a test ticket
		Ticket ticket = ticketDao.get(testTicketId);
		
		assertNotNull("Could not find test ticket.", ticket);
		
	}

	@Test
	public void testUpdate() {
		
		// finding a test ticket
		Ticket ticket = ticketDao.update(testTicketId, "peter", "Getting the test ticket", "ASSIGNED");
		
		assertNotNull("Could not update the test ticket.", ticket.getTechnician());
		assertTrue("Could not add history to test ticket.", (ticket.getTicketHistory().size() > 0));
		
	}

	@Test
	public void testGetTicketsOwnedByTechnician() {
		
		// finding a test ticket
		List<Ticket> tickets = ticketDao.getTicketsOwnedByTechnician("peter");
		
		assertTrue("Could not find tickets assigned to technician 'peter'", (tickets.size() > 0));
		
	}

	@Test
	public void testGetOpenTickets() {
		
		// finding a test ticket
		List<Ticket> tickets = ticketDao.getOpenTickets();
		
		assertTrue("Could not find open tickets.", (tickets.size() > 0));
		
	}

	@Test
	public void testRemove() {
		
		// finding a test ticket
		ticketDao.remove(testTicketId);
		
		// the ticket has been removed
		assertTrue(true);
		
	}
	
}

Documentation / Reference