J2EE - JPA and Stateless Session Bean (EJB) as Data Access Object (DAO)

Java Conceptuel Diagram

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





Discover More
Java Conceptuel Diagram
Java - Controller

A JPA controller class (DAO) is a wrapper for an entity class that provides clients with access to the database through the methods in the entity class. The JPA controller class contains the logic for...
Java Conceptuel Diagram
JavaServer Faces (JSF)

The request: means to maintain the state for the time of a request session: means to maintain the state for the time of a session (between page views) none: means that the bean will be created but...



Share this page:
Follow us:
Task Runner