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

> Procedural Languages > Java

1 - About

Advertising

3 - Transaction Type

3.1 - 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).

4 - Steps

4.1 - DAO

4.1.1 - 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);
 
}

4.1.2 - 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

Advertising

4.2 - 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.

4.2.1 - 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();
  }
 
} 

4.2.2 - 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();
	} 
 
}
Advertising

4.3 - 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);
 
	}
 
}

5 - Documentation / Reference

lang/java/jpa_dao.txt · Last modified: 2018/09/19 13:20 by gerardnico