Showing posts with label JPA. Show all posts
Showing posts with label JPA. Show all posts

Saturday, December 26, 2009

CRUD controller with JSF

(belongs to this series of articles)

This is a proposal for a lightweight controller layer that would be built on top or the CRUD services layer described in the previous article. Just like we could generalize the DAO and Services layer for CRUD (create-retrieve-update-delete) operations on domain objects (or entity objects, extending DomainObject and its subclasses), we can create a parametrised framework class that implements the CRUD functionality for presentation layer. This class would have the role of Controller in the Model-View-Controller pattern; thus, the main functions of such a class would be calling the corresponding Service when a CRUD operation on a domain object is being performed. This class would be the common part of all controllers of an application that perform CRUD on domain objects, and contain all the functionality that can be generalized for all domain objects: retrieving, saving, and deleting an entity, and returning a collections of entities to create scrolling lists.

Most of the functionality is implemented by simply passing control to the Services layer; however, the scrolling lists and error handling require some logic on the controller level.

One possible approach is using NetBeans JSF CRUD generator. The disadvantages of that approach would be the dependence on one-way generator (re-generating loses all changes) and NetBeans (it's best when the project does not depend on any IDE). I submit that creating a framework class (possibly complemented by facelets and custom tags) is a more flexible approach.

The root class of the proposed controller hierarchy is trying to stay agnostic of the particular view technology being used. It passes control for most of the real operations to the service layer (via injected reference to the Service), and delegates view framework dependent operations (like displaying error messages) to its subclasses:

package com.example.crud.controller;

import java.io.Serializable;
import java.io.ObjectInputStream.GetField;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.Callable;

import javax.management.RuntimeErrorException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.example.crud.service.GenericCrudService;
import com.example.domain.DomainObject;

/**
 * Common session controller to implement CRUD actions. Can be used with
 * different view technologies.
 * 
 * Does <b>not</b> make the assumption that the object type is
 * {@link DomainObject}, so needs subclasses to implement
 * {@link #getCurrentId()}: only subclasses know how to extract the ID.
 * 
 * @author maxim
 * 
 * @param <T>
 *            domain object type
 * @param <PK>
 *            primary key
 */
public abstract class GenericCrudController<T extends DomainObject<PK>, PK extends Serializable> {

 public static final String SUCCESS = "success";

 public static final String ERROR = "error";

 public static final String EDIT = "edit";

 public static final String CANCEL = "cancel";

 protected static Log logger = LogFactory
   .getLog(GenericCrudController.class);

 protected Class<T> type;
 protected T currentRecord;

 protected transient GenericCrudService<T, PK> service;

 private int pgSize = 5;

 private String order;

 private boolean sortAscending;

 private int firstRow = 0;

 /**
  * Default constructor. Tries to discover Class<T>
  */
 @SuppressWarnings("unchecked")
 public GenericCrudController() {
  type = (Class<T>) ((ParameterizedType) getClass()
    .getGenericSuperclass()).getActualTypeArguments()[0];
  if (logger.isDebugEnabled())
   logger.debug("creating new " + this.getClass().getSimpleName());
 }

 /**
  * Subclasses add end user messages according to view technology
  * 
  * @param message
  *            text message
  * @param e
  *            exception or null if none
  */
 protected abstract void addMessage(String message, Throwable e);

 /**
  * Subclasses add end user messages according to view technology
  * 
  * @param message
  *            text message
  */
 protected abstract void addMessage(String message);

 /**
  * Subclass will reset the page view (e.g. after delete)
  */
 public abstract void resetPage();

 public Collection<T> getList() {
  try {
   return service.getAll();
  } catch (Exception e) {
   addMessage("Error retrieving page", e);
   logger.error("error retrieving page:", e);
   return new ArrayList<T>();
  }
 }

 /**
  * This is where the controller operations are really performed. Single spot
  * to implement error handling and returning standard outcomes and messages.
  * 
  * @param controllerAction
  *            a {@link Callable} that does the actions
  * @return string outcome
  */
 protected String successAndErrorAction(Callable<String> controllerAction) {
  try {
   String ret = controllerAction.call();
   logger.info("Action succeeded: " + this.getClass().getSimpleName());
   return null == ret ? SUCCESS : ret;
  } catch (Exception e) {
   logger.error("Exception in controller "
     + this.getClass().getSimpleName(), e);
   addMessage(e.getMessage());
   return ERROR;
  }
 }

 public GenericCrudService<T, PK> getService() {
  return service;
 }

 public void setService(GenericCrudService<T, PK> service) {
  this.service = service;
 }

 public void setCurrentRecord(T update) {
  currentRecord = update;
 }

 /**
  * @return id of the current record
  */
 protected PK getCurrentId() {
  return null==getCurrentRecord()?null:getCurrentRecord().getId();
 }

 /**
  * 
  * @return new current record (just uses the default constructor of T)
  */
 protected T newCurrentRecord() {
  try {
   if (logger.isDebugEnabled())
    logger.debug("new current record" + type.getSimpleName());
   return (T) type.newInstance();
  } catch (Exception e) {
   logger.error("exception creating CRUD currentRecord", e);
   return null;
  }
 }

 /**
  * Getting records to display on a page
  * 
  * @param pageSize
  *            page size
  * @param firstRecord
  *            first record number (starts from 0)
  * @param order
  *            field name for order, or null
  * @param asc
  *            true if ascending order, else descending
  * @return collection, size <= pageSize
  */
 protected Collection<T> getRecords(int pageSize, int firstRecord,
   String order, boolean asc) {
  if (pageSize <= 0) {
   return service.getAll();
  } else {
   return service.getPage(pageSize, firstRecord, order, asc);
  }
 }

 /**
  * 
  * @return collection of records based on current pagination helper
  */
 public Collection<T> getRecords() {
  return getRecords(getPageSize(), getFirstRow(), getOrder(),
    isSortAscending());
 }

 /**
  * 
  * @return the current record (e.g. the one being edited)
  */
 public T getCurrentRecord() {
  return this.currentRecord;
 }

 /**
  * Called from the list view to delete selected record
  * 
  * @return standard outcome from {@link #successAndErrorAction(Callable)}
  * @see #successAndErrorAction(Callable)
  */
 public String delete() {
  return successAndErrorAction(new Callable<String>() {
   @Override
   public String call() throws Exception {
    setCurrentRecord(getSelectedRecord());
    service.delete(getCurrentId());
    addMessage("Record deleted successfully");
    resetPage();
    return null;
   }
  });
 }

 /**
  * 
  * @return record selected in subclass
  */
 protected abstract T getSelectedRecord();

 /**
  * Called from list view to show the editing view; subclasses can override
  * to do something more interesting.
  * 
  * @return "edit" to show the editing view (without curent record this means
  *         create new)
  * @see #edit()
  */
 public String create() {
  currentRecord = newCurrentRecord();
  return EDIT;
 }

 public int getPageSize() {
  return pgSize;
 }

 public void setPageSize(int pageSize) {
  pgSize = pageSize;
 }

 private boolean reReadOnEdit=false;
 
 /**
  * Called from the list view to display the record for editing: "edit" means
  * show the editing view
  * 
  * The current record is pointed to by {@link #dataTable}.
  * 
  * The content of the record is (optionally) re-read, so that the lazy loading
  * collections could be loaded by the DAO on findById(), if the DAO implements this
  * method.
  * 
  * 
  * @return
  */
 public String edit() {
  T currentRecord = getSelectedRecord();
  if (null != currentRecord) {
   if(reReadOnEdit){
    currentRecord = service.get(currentRecord.getId());
   }
   setCurrentRecord(currentRecord);
  }
  return EDIT;
 }

 /**
  * Save action called from edit record view to add or save a record
  * 
  * @see #successAndErrorAction(Callable)
  * @return standard outcome from {@link #successAndErrorAction(Callable)}
  */
 public String save() {
  return successAndErrorAction(new Callable<String>() {
   @Override
   public String call() throws Exception {
    if (null != getCurrentId()) {
     setCurrentRecord(service.update(getCurrentRecord()));
    } else {
     service.insert(getCurrentRecord());
    }
    addMessage("Record saved successfully");
    return null;
   }
  });
 }

 public String getOrder() {
  return order;
 }

 public void setOrder(String order) {
  this.order = order;
 }

 public boolean isSortAscending() {
  return sortAscending;
 }

 public int getFirstRow() {
  return firstRow;
 }

 public void setFirstRow(int firstRow) {
  this.firstRow = firstRow;
 }

 public void setSortAscending(boolean sortAscending) {
  this.sortAscending = sortAscending;
 }

 public void setReReadOnEdit(boolean reReadOnEdit) {
  this.reReadOnEdit = reReadOnEdit;
 }

 public boolean isReReadOnEdit() {
  return reReadOnEdit;
 }

} 

The choice of view technology: why JSF?

JSF seems to be a natural choice of view technology for a framework that implements CRUD using JPA (or any other object-relational mapping framework, e.g. Hibernate), because it provides for easy mapping of properties of entities to view UI. This is especially important when the properties in question are collections of dependent beans (master-detail relationship) and we want to build a table-type edit form  (as shown, for example, here). As opposed to other technologies, JSF requires surprisingly little coding to map a collection of objects to an (editable and scrolling) data table on the web page.

Hence, an extension of GenericCrudController that uses JSF to implement abstract methods:


package com.example.crud.controller.jsf;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIData;
import javax.faces.component.html.HtmlDataTable;
import javax.faces.context.FacesContext;
import javax.faces.model.DataModel;

import org.apache.myfaces.renderkit.html.util.AddResource;
import org.apache.myfaces.renderkit.html.util.AddResourceFactory;

import com.example.crud.controller.GenericCrudController;
import com.example.crud.service.GenericCrudService;
import com.example.domain.DomainObject;

/**
 * Common controller type for CRUD using {@link GenericCrudService}.
 * 
 * Extending this class guarantees consistency in JFS outcomes and error
 * handling in controller operations.
 * 
 * Standard actions used by list and edit page templates are {@link #edit()},
 * {@link #create()}, {@link #save()}, {@link #delete()}.
 * 
 * {@link #getList()} or {@link #getPage()} is used to show the scrolling table.
 * 
 * {@link #getDataTable()} is the scrolling table model.
 * 
 * @author maxim
 * 
 * @param <T>
 *            currentRecord type that the controller works with
 * @param <PK>
 *            primary key type
 * 
 * @see GenericCrudService
 */
public class GenericCrudControllerJsf<T extends DomainObject<PK>, PK extends Serializable>
  extends GenericCrudController<T, PK> implements Serializable {

 private static final long serialVersionUID = -1;

 private Map<T, Boolean> recordSelections = new HashMap<T, Boolean>();

 private String popupOptions = "dependent=yes, menubar=no, toolbar=no, height=400, width=600";

 private transient UIData dataTable;
 private transient FacesContext facesContext;

 public GenericCrudControllerJsf() {
  if (logger.isDebugEnabled())
   logger.debug("constructed " + this.getClass());
 }

 public void submit() {
  logger.debug("void submit method");
 }

 /**
  * @return the dataTable
  */
 public UIData getDataTable() {
  return dataTable;
 }

 /**
  * @param dataTable
  *            the dataTable to set
  */
 public void setDataTable(UIData dataTable) {
  this.dataTable = dataTable;
 }

 /**
  * Add message via {@link FacesContext}
  * 
  * @param message
  * @param e
  */
 protected void addMessage(String message, Throwable e) {
  getFacesContext().addMessage(
    "controller",
    new FacesMessage(FacesMessage.SEVERITY_ERROR, message,
      null == e ? message : e.getMessage()));
 }

 /**
  * Add message via {@link FacesContext}
  * 
  * @param message
  */
 protected void addMessage(String message) {
  getFacesContext().addMessage("controller",
    new FacesMessage(FacesMessage.SEVERITY_INFO, message, message));
 }

 public void setFacesContext(FacesContext context) {
  this.facesContext = context;
 }

 public FacesContext getFacesContext() {
  if (null != facesContext) {
   return facesContext;
  } else {
   return FacesContext.getCurrentInstance();
  }
 }

 private AddResource addResource;

 public void setAddResource(AddResource addResource) {
  this.addResource = addResource;
 }

 public AddResource getAddResource() {
  if (null != addResource) {
   return addResource;
  } else {
   return AddResourceFactory.getInstance(getFacesContext());
  }
 }

 private List<ChoiceListener> choiceListeners = new ArrayList<ChoiceListener>();

 /**
  * Add listener to be called when we make a selection choice (e.g. check
  * some records and click "choose selected" or some such)
  * 
  * @param listener
  */
 public void addChoiceListener(ChoiceListener listener) {
  choiceListeners.add(listener);
 }

 /**
  * That's where we get control when the choice has been made in a pop-up
  * registered listeners are called; if the parent window exists and has a
  * "submitMainForm" element, we will close the current window and "click"
  * that element in the parent window.
  */
 public void choose() {
  for (T record : recordSelections.keySet()) {
   if (null != recordSelections.get(record)
     && recordSelections.get(record)) {
    for (ChoiceListener listener : choiceListeners) {
     listener.processChoice(record);
    }
   }
  }
  recordSelections.clear();
  dataTable = null;
  // The following kludge fires click event for "submitMainForm" element
  // in the main window,
  // if one is defined.
  String javaScriptText = "   window.close(); "
    + "   if(window.opener && window.opener.document.getElementById('submitMainForm')){"
    + "    var fireOnThis = window.opener.document.getElementById('submitMainForm');"
    + "    if (window.opener.document.createEvent) {"
    + "        var evObj = window.opener.document.createEvent('MouseEvents');"
    + "        evObj.initEvent( 'click', true, false );"
    + "        fireOnThis.dispatchEvent(evObj);"
    + "    } else if (window.opener.document.createEventObject){"
    + "         fireOnThis.fireEvent('onclick');" + "      }"
    + "   }";
  getAddResource().addInlineScriptAtPosition(getFacesContext(),
    AddResource.HEADER_BEGIN, javaScriptText);
 }

 /**
  * Method called by views and other controllers to open selection pop-up for
  * the domain objects in question
  * 
  * @param event
  */
 public void openPopup(String actionUrl) {
  dataTable = null;
  String javaScriptText = "window.open('" + actionUrl
    + "', 'popupWindow', '" + popupOptions + "');";
  getAddResource().addInlineScriptAtPosition(getFacesContext(),
    AddResource.HEADER_BEGIN, javaScriptText);
 }

 /**
  * @return
  */
 @Override
 @SuppressWarnings("unchecked")
 public T getSelectedRecord() {
  return null != dataTable ? ((T) dataTable.getRowData()) : null;
 }

 public Map<T, Boolean> getRecordSelections() {
  return recordSelections;
 }

 public void setRecordSelections(Map<T, Boolean> recoordSelections) {
  this.recordSelections = recoordSelections;
 }

 public void setPopupOptions(String popupOptions) {
  this.popupOptions = popupOptions;
 }

 public String getPopupOptions() {
  return popupOptions;
 }

 public void setChoiceListeners(List<ChoiceListener> choiceListeners) {
  this.choiceListeners = choiceListeners;
 }

 public List<ChoiceListener> getChoiceListeners() {
  return choiceListeners;
 }

 private transient DataModel pagedList;

 @Override
 public void resetPage() {
  dataPage = null;
  pagedList = null;
  setDataTable(null);
 }

 private DataPage<T> dataPage;
 
 /**
  * 
  * @return paginating lazy-reading collection
  */
 public DataModel getPage() {
  if (null == pagedList) {
   pagedList = new PagedListDataModel<T>(getPageSize(), getFirstRow()) {
    @Override
    public DataPage<T> fetchPage(int startRow, int pageSize) {
     final int start = getFirstRow();
     setFirstRow(startRow);
     if (logger.isDebugEnabled())
      logger.debug("new page: " + startRow + " old=" + start
        + " pageSize=" + pageSize);
     if (null == dataPage || startRow != dataPage.getStartRow()) {
      dataPage = new DataPage<T>(getService().getTotal(),
        startRow, (List<T>) getService().getPage(
          pageSize, startRow, getOrder(),
          isSortAscending()));
     }
     return dataPage;
    }
   };
  }
  return pagedList;
 }
}

The interesting part is the pagination logic that uses special class to implement lazy reading of records to fill pages.

Another part that needs clarification is how to code facelets or JSPs using this controller as a backing bean.

To be continued...

Saturday, October 24, 2009

Reusable DAOs: CRUD operations with domain objects

(belongs to this series of articles)

Trying to create reusable DAO objects, following the article "Don't repeat the DAO" for the reusable domain objects proposed earlier in this series.

It is proposed to follow the recipe from the IBM site referred to above:

package com.example.crud.dao;

import java.io.Serializable;
import java.util.Collection;

import com.example.domain.DomainObject;

/**
 * Generic DAO interface - usable for CRUD and more.
 * 
 * Throws unchecked exceptions for any errors. The exception type depends on the
 * implementation.
 * 
 * @author maxim
 * 
 * @param <T>
 *            domain object type
 * @param <PK>
 *            primary key type
 */
public interface GenericDao<T extends DomainObject<PK>, PK extends Serializable> {

 /**
  * Persist the newInstance object into database
  **/
 void create(T newInstance);

 /**
  * Retrieve an object that was previously persisted to the database using
  * the indicated id as primary key
  */
 T findById(PK id);

 /**
  * 
  * @return all instances of T as a collection
  */
 Collection<T> findAll();

 /**
  * Main paging method
  * 
  * @param pageSize
  *            number of records (page size)
  * @param firstRecord
  *            start from this record (inclusive)
  * @param order
  *            field name to sort
  * @param asc
  *            sort ascending if true, descending otherwise
  * 
  * @return all instances that belong to the page
  */
 Collection<T> getPage(int pageSize, int firstRecord, String order,
   boolean asc);

 /**
  * Save changes made to a persistent object.
  * 
  * @return TODO
  */
 T update(T transientObject);

 /**
  * Remove an object from persistent storage in the database
  */
 void remove(T object);

 /**
  * Remove an object from persistent storage in the database by ID
  */
 void remove(PK id);

 /**
  * 
  * @return total number of entities in the DB
  */
 Long getTotalEntities();

 /**
  * Overload of "getPage" with a possibility to apply dynamic criteria.
  * Service layer could use this to implement find by related record, for
  * example
  * 
  * @see #getPage(int, int, String, boolean)
  * 
  * @param pageSize
  *            page size
  * @param firstRecord
  *            first record number, starts from 0
  * @param order
  *            sort order defined by field name, e.g. "id"
  * @param asc
  *            true for ascending sort
  * @param queryString
  *            string to add to query, e.g. "where o.id=?", or null
  * @param parameters
  *            objects to use the query with
  * @return up to <code>pageSize</code> of objects or empty collection
  */
 Collection<T> getPage(int pageSize, int firstRecord, String order,
   boolean asc, String queryString, Object... parameters);
}

And the JPA implementation of this:

/**
 * 
 */
package com.example.crud.dao.jpa;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;

import javax.persistence.EntityManager;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.example.crud.dao.GenericDao;
import com.example.domain.DomainObject;

/**
 * Implements {@link GenericDao} using JPA.
 * 
 * Works in managed environment using the injected {@link EntityManager}
 * 
 * @author maxim
 * 
 */
public class GenericDaoJpa<T extends DomainObject<PK>, PK extends Serializable>
  implements GenericDao<T, PK> {


 private static Log logger = LogFactory.getLog(GenericDaoJpa.class);

 
 @PersistenceContext
 public transient EntityManager em;

 public Class<T> type;

 @SuppressWarnings("unchecked")
 public GenericDaoJpa() {
  type = (Class<T>) ((ParameterizedType) getClass()
    .getGenericSuperclass()).getActualTypeArguments()[0];
 }

 /*
  * (non-Javadoc)
  * 
  * @see
  * com.example.prototype.persistence.GenericDao#create(java.lang.Object )
  */

 public void create(T newInstance) {
  em.persist(newInstance);
 }

 /*
  * (non-Javadoc)
  * 
  * @seecom.example.struts2.crud.persistence.GenericDao#findById(java.io.
  * Serializable)
  */

 public T findById(PK id) {
  T entity = em.find(type, id);
  postFindById(entity, em);
  return entity;
 }

 /**
  * Override in subclasses to do something here: this is where we could force
  * the loading of lazy loading collections, for example.
  * 
  * The rationale is that we typically need this for individual records, but
  * not for collections; so getPage() and the like return only eager-loading
  * properties, whereas findById() and the like should return fully loaded
  * objects.
  * 
  * @param entity
  *            the entity retrieved by find -- call methods on for lazy
  *            loading objects and collections to force loading
  * @param em
  */
 protected void postFindById(T entity, EntityManager em) {
 }

 /*
  * (non-Javadoc)
  * 
  * @see
  * com.example.prototype.persistence.GenericDao#remove(java.lang.Object )
  */

 public void remove(PK id) {
  remove(em.getReference(type, id));
 }

 /*
  * @see
  * com.example.prototype.persistence.GenericDao#remove(java.lang.Object
  */

 public void remove(T object) {
  em.remove(object);
 }

 /*
  * (non-Javadoc)
  * 
  * @see
  * com.example.prototype.persistence.GenericDao#update(java.lang.Object )
  */

 public T update(T transientObject) {
  try {
   return em.merge(transientObject);
  } catch (OptimisticLockException e) {
   logger.error("Record is modified by another process", e);
   throw new RuntimeException(
     "Error saving record: modified by another process");
  }
 }

 @Override
 public Collection<T> findAll() {
  Query query = em.createQuery("select o from " + type.getSimpleName()
    + " o");
  return findAll(query);
 }

 @SuppressWarnings("unchecked")
 protected Collection<T> findAll(Query q) {
  return q.getResultList();
 }

 @Override
 public Collection<T> getPage(int pageSize, int firstRecord, String order,
   boolean asc) {
  return getPage(pageSize, firstRecord, order, asc, null, (Object) null);
 }

 @SuppressWarnings("unchecked")
 public Collection<T> getPage(int pageSize, int firstRecord, String order,
   boolean asc, String queryString, Object... parameters) {
  final String whereString = queryString == null ? "" : queryString;
  final String ascending = asc ? "asc" : "desc";
  final String orderString = null == order || order.trim().equals("") ? ""
    : "order by " + order + " " + ascending;
  final Query q = em.createQuery("select o from " + type.getSimpleName()
    + " o " + whereString + " " + orderString);
  if (null != parameters && null != queryString) {
   for (int i = 0; i < parameters.length; i++) {
    q.setParameter(i + 1, parameters[i]);
   }
  }
  if (0 < pageSize) {
   q.setMaxResults(pageSize);
  }
  q.setFirstResult(firstRecord);
  return q.getResultList();
 }

 /**
  * 
  * @return the total number of entities
  */
 public Long getTotalEntities() {
  Query query = em.createQuery("select count(o) as count from "
    + type.getSimpleName() + " o");
  return (Long) query.getSingleResult();
 }

 public EntityManager getEm() {
  return em;
 }

 public void setEm(EntityManager em) {
  this.em = em;
 }

}

The only original twist in the above is the "postFindById()" method. It is a hack for something that would be normally out of scope for this article -- the problem of lazy loading collections in an entity object that is used outside of the transaction scope. This is the reason for the widespread error: "org.hibernate.LazyInitializationException: failed to lazily initialize a collection ... no session or session was closed" that would happen in the following widespread case:

(1) display a list of objects
(2) display the details of one of them, part of those details being lazy-loading collections of dependent objects

If the object is a JPA/Hibernate entity, then the ORM session would indeed be closed between (1) and (2). There are many ways to code around this, but one that I find the most generic is to make DAO methods that retrieve the entity by ID (and by definition work with a single entity, as opposed to a list) force the loading of lazy-loading collections. Which collections need to be loaded can't be determined in the generic DAO, hence the delegating of this to subclasses using an abstract method.

Saturday, October 3, 2009

JPA and Common Domain Objects

(belongs to this series)

The idea is to create a hierarchy of classes that all domain objects can derive from. These classes are the single point where we implement the following:

  • Methods for Collections: we need all domain objects to be comparable, hashable and to equal on ID (primary key)
  • Primary key: either auto-numbering or GUID
  • Optimistic concurrency

What could be added if needed:

  • Timestamp
  • Auditing: the user id of last update

All domain objects can extend these; we could also benefit from all domain objects following the same pattern (e.g. having a "getId()" method), when implementing the generic DAOs.

If we add entity classes, and configure the project as suggested below, then, at this stage, we would achieve the following:
  • The database schema will be generated by Hibernate using the entity classes that extend the generic domain object classes suggested here; the configuration suggested below actually provides for dropping and the tables and re-generating them each time the build runs.

  • Entity manager and transaction manager will be configured by Spring and ready for the next stage -- implementing the Data Access Objects

  • The unit tests can be implemented with the assumption that the database schema is up to date -- i.e. following the entity classes -- for every build (see "execution" clause in Hibernate plug-in configuration suggested below)

Libraries and Versions


... as expressed by Maven dependencies:

....

<dependency>
 <groupid>org.springframework</groupid>
 <artifactid>spring</artifactid>
 <version>2.5.6</version>
</dependency>
<dependency>
 <groupid>org.springframework</groupid>
 <artifactid>spring-core</artifactid>
 <version>2.5.6</version>
</dependency>
<dependency>
 <groupid>org.springframework</groupid>
 <artifactid>spring-context</artifactid>
 <version>2.5.6</version>
</dependency>
<dependency>
 <groupid>org.springframework</groupid>
 <artifactid>spring-test</artifactid>
 <version>2.5.6</version>
</dependency>
<dependency>
 <groupid>org.springframework</groupid>
 <artifactid>spring-web</artifactid>
 <version>2.5.6</version>
</dependency>
...
  
<dependency>
 <groupid>javax.persistence</groupid>
 <artifactid>persistence-api</artifactid>
 <version>1.0</version>
</dependency>
<dependency>
 <groupid>org.hibernate</groupid>
 <artifactid>hibernate-entitymanager</artifactid>
 <version>3.3.2.GA</version>
</dependency>
<dependency>
 <groupid>javax.transaction</groupid>
 <artifactid>jta</artifactid>
 <version>1.1</version>
</dependency>
<dependency>
 <groupid>${jdbc.groupId}</groupid>
 <artifactid>${jdbc.artifactId}</artifactid>
 <version>${jdbc.version}</version>
</dependency>
...

Using Hibernate Maven Plugin to generate the schema


Let's start with the "build" section of the POM, where we define how the resources get filtered (in Maven parlance, this means resolving variables in them using property values from the POM):

...
  <resources>
   <resource>
    <directory>${project.basedir}/src/main/resources</directory>
    <filtering>true</filtering>
   </resource>
  </resources>
  <testresources>
   <testresource>
    <directory>${project.basedir}/src/test/resources</directory>
    <filtering>true</filtering>
   </testresource>
  </testresources>      


Where the resources we are filtering as shown above are used? It is mostly in the jdbc.properties file quoted below. This is the file that is referenced from the Hibernate section of the POM, and also in the Spring configuration quoted below. This way we are making sure that all the database properties are specified in the POM.

The reason we are choosing POM as the single place of specifying the parameters for the project is because Maven has the ability to implement logic switching sets of values depending on the environment where the build is performed (test, development, QA, production). The other possible places (.properties files, Spring configuration) lack this flexibility.

The idea was borrowed from this Maven archetype


#
# These properties are used at build time and run-time;
# they define the DB type and location; POM defines several
# sets of values for different environments (test, QA, ...)
#
jdbc.driverClassName=${jdbc.driverClassName}
jdbc.url=${jdbc.url}
jdbc.username=${jdbc.username}
jdbc.password=${jdbc.password}
jpa.database=${jpa.database}
jpa.showSql=${jpa.showSql}


# Needed by Hibernate3 Maven Plugin defined in pom.xml
hibernate.dialect=${hibernate.dialect}
hibernate.connection.username=${jdbc.username}
hibernate.connection.password=${jdbc.password}
hibernate.connection.url=${jdbc.url}
hibernate.connection.driver_class=${jdbc.driverClassName}


The property values that will be used at build time to resolve variables in the configuration file quoted above are specified in the POM -- in the "profiles" section -- as follows:

<properties>
  <dbunit.dialect>org.dbunit.ext.mysql.MySqlDataTypeFactory</dbunit.dialect>
  <hibernate.dialect>org.hibernate.dialect.MySQLDialect</hibernate.dialect>
  <jdbc.groupid>mysql</jdbc.groupid>
  <jdbc.artifactid>mysql-connector-java</jdbc.artifactid>
  <jdbc.version>5.1.6</jdbc.version>
  <jdbc.driverclassname>com.mysql.jdbc.Driver</jdbc.driverclassname>
  <jdbc.username>example</jdbc.username>
  <jdbc.password> </jdbc.password>
  <jpa.database>MYSQL</jpa.database>
  <jpa.showsql>true</jpa.showsql>
</properties>


Hibernate plug-in configuration references the jdbc.properties file that is getting receiving the values of properties specified in the POM as described above:

...
   <plugin>
    <groupid>org.codehaus.mojo</groupid>
    <artifactid>hibernate3-maven-plugin</artifactid>
    <version>2.2</version>
    <executions>
     <execution>
      <phase>process-classes</phase>
      <goals>
       <goal>hbm2ddl</goal>
      </goals>
     </execution>
    </executions>
    <configuration>
     <components>
      <component>
       <name>hbm2ddl</name>
       <implementation>jpaconfiguration</implementation>
      </component>
     </components>
     <componentproperties>
      <persistenceunit>example</persistenceunit>
      <outputfilename>schema.ddl</outputfilename>
      <propertyfile>target/classes/jdbc.properties</propertyfile>
      <drop>true</drop>
      <create>true</create>
      <export>true</export>
      <update>false</update>
      <format>true</format>
     </componentproperties>
    </configuration>
    <dependencies>
     <dependency>
      <groupid>${jdbc.groupId}</groupid>
      <artifactid>${jdbc.artifactId}</artifactid>
      <version>${jdbc.version}</version>
     </dependency>
    </dependencies>

   </plugin>
      ...


And persistence.xml is nearly empty (all mappings are handled by Hibernate, and entity manager is supplied by Spring):

<persistence version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/persistence" xsi:schemalocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
 <persistence-unit name="example" transaction-type="RESOURCE_LOCAL">
 </persistence-unit>
</persistence>

Finally, what binds together JPA and Hibernate is the Spring container; note the database parameters that reference the properties file:

<beans xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
   http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd ">

    <context:property-placeholder location="classpath:/jdbc.properties">
    </context:property-placeholder>
   
   
    <context:annotation-config>
    </context:annotation-config>

    <tx:annotation-driven>
    </tx:annotation-driven>

    <jee:jndi-lookup id="dataSource" jndi-name="jdbc/Database">
    </jee:jndi-lookup>

   <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory" p:datasource-ref="dataSource" p:jpavendoradapter-ref="jpaAdapter">
    <property name="loadTimeWeaver">
     <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver">
    </bean>
   </property>

   <bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager" p:entitymanagerfactory-ref="entityManagerFactory">

      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" id="jpaAdapter" p:database="${jpa.database}" p:showsql="${jpa.showSql}">
      </bean>
    </bean>
  </bean>
  </beans>


Root class of reusable domain object hierarchy:


Implements comparison, equality and hash code, the primary key is left for the subclasses:

package com.example.domain;

import java.io.Serializable;

import javax.persistence.MappedSuperclass;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PrePersist;
import javax.persistence.Transient;
import javax.persistence.Version;

import org.apache.commons.lang.ObjectUtils;

/**
 * Root class for all entities; implements comparison and what goes with it.
 * Also has a "version" field for optimistic concurrency checking.
 * 
 * @author maxim
 *
 * @param <PK> primary key type
 * 
 * @see AutoNumberedDomainObject
 * @see SurrogateIdDomainObject
 */
@MappedSuperclass
abstract public class DomainObject<PK> implements
  Serializable, Comparable<DomainObject<PK>>, Cloneable {
 
 @Transient
 private boolean justCreated=true;
 
 @PostPersist
 @PostLoad
 public void saved(){
  justCreated=false;
 }
 
 static private  long serialVersionUID = 1L;
 
 @Version
 protected int version;

 @Override
 public String toString() {
  return "DomainObject ("+this.getClass().getSimpleName()+")";
 }

 
 @SuppressWarnings("unchecked")
 @Override
 public boolean equals(Object o) {
  if (o == this)
   return true;
  if (o == null)
   return false;
  return (o.getClass().isAssignableFrom(this.getClass()))
    && (this.getClass().isAssignableFrom(o.getClass()))
    && ObjectUtils.equals(((DomainObject<PK>) o).getId(), getId());
 }

 @Override
 public int hashCode() {
  if (getId() == null)
   return 0;
  return getId().hashCode();
 }

 /**
  * A simple stable ordering based on Id
  */
 @SuppressWarnings("unchecked")
 public int compareTo(DomainObject<PK> o) {
  if (getId() == null)
   return -1;
  if (o.getId() == null)
   return 1;
  return ((Comparable<PK>) getId()).compareTo(o.getId());
 }

 @Override
 public Object clone() {
  try {
   return super.clone();
  } catch (CloneNotSupportedException e) {
   throw new RuntimeException(e);
  }
 }
 
 /**
  * 
  * @return lable to display in selection lists; override in subclasses to produce something better
  */
 public String getSelectionLabel(){
  return getId().toString();
 }

 /**
  * @param id
  *            the id to set
  */
 public abstract void setId(PK id);

 /**
  * @return the id
  */
 public abstract PK getId();

 public int getVersion() {
  return version;
 }

 public void setVersion(int version) {
  this.version = version;
 }


 public void setJustCreated(boolean notSaved) {
  this.justCreated = notSaved;
 }


 public boolean isJustCreated() {
  return justCreated;
 }

}



Domain object with auto-numbered ID


This class extends DomainObject to add JPA auto-numbered ID fields.

package com.example.domain;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

/**
 * Extends {@link DomainObject} to support auto-numbered IDs via JPA
 * {@link GeneratedValue} with {@link GenerationType} =
 * {@link GenerationType#TABLE}
 * 
 * Does <b>not</b> force a unique constraint on the ID: add this in subclasses
 * if performance hit is accessible.
 * 
 * @param PK
 *            the primary key class for auto-numbering; the choice is usually
 *            between Long and Integer
 * 
 * @author maxim
 * 
 */
@MappedSuperclass
public abstract class AutoNumberedDomainObject<PK extends Number> extends
  DomainObject<PK> {

 private static  long serialVersionUID = 1L;

 @Override
 public String toString() {
  return "AutoNumberedDomainObject ("+this.getClass().getSimpleName()+") [id=" + id + "]";
 }
 
 @Id
 @GeneratedValue(strategy = GenerationType.TABLE)
 protected PK id;

 @Override
 public  PK getId() {
  return this.id;
 }

 @Override
 public  void setId(PK id) {
  this.id = id;
 }

}


Domain object with Surrogate Id


Another (and, arguably, more usable) flavour of DomainObject generates surrogate primary keys:

package com.example.domain;

import java.util.UUID;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

/**
 * DomainObject that has a surrogate ID calculated as a time-based GUID.
 * 
 * Does <b>NOT</b> force a unique constraint on the ID: add this in subclasses
 * if performance hit is acceptable.
 * 
 * @author maxim
 * 
 * @see UUID
 * 
 */
@MappedSuperclass
public class SurrogateIdDomainObject extends DomainObject<String> {

 @Override
 public String toString() {
  return "SurrogateIdDomainObject ("+this.getClass().getSimpleName()+") [id=" + id + "]";
 }

 /**
  * Default constructor; generates new ID
  */
 public SurrogateIdDomainObject() {
  super();
  this.id = newId();
 }

 /**
  * 
  * @param generateId specify false to <b>not</b> generate the ID
  */
 public SurrogateIdDomainObject(boolean generateId) {
  super();
  if (generateId) {
   this.id = newId();
  }
 }

 private static final long serialVersionUID = 1L;

 /**
  * "e5d302ed-0501-4a3e-abfc-a336124fe2b5" 
  */
 @Id
 @Column(name = "ID",length=36)
 private String id;

 /**
  * Creates new and assigns a surrogate ID
  */
 protected void initializeAsNew() {
  setId(newId());
 }

 public  void setId(String id) {
  this.id = id;
 }

 public  String getId() {
  return id;
 }

 /**
  * 
  * @return new UUID-based ID, e.g. "e5d302ed-0501-4a3e-abfc-a336124fe2b5" 

  * @see UUID
  */
 public static String newId() {
  return UUID.randomUUID().toString();
 }

}

Using the generic domain object classes


Having defined all of the above, we can benefit from it defining new domain objects as follows:

...
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@Entity
public class User extends SurrogateIdDomainObject {

 private String name;
...

The XML (actually, JAXB) annotations will come into play when we will be passing this object to and from Web Services.

Hibernate, JPA, JAX-WS, Spring AOP, JSF, Facelets, Transactions with Maven and Continuous Integration.

Starting what will hopefully become a series of articles showing how to use the widespread technologies in a web application started from scratch and intended to be deployed on a simple servlet container (Tomcat, Jetty,...) lacking support for advanced J2EE.

In other words, this is sharing simple home-made recipes and hoping to make them better by sharing.

The problem: create a slim and maintainable Java application including a lightweight framework that facilitates CRUD operations. This is a template that would be the starting point of many different projects, and eventually would lead to  creation of a Maven archetype.

What is known: performance is not critical; we should use free software (database and application servers), but not exclude a transition to other technologies in the future. The resulting project should lend itself easily to agile development and continuous integration (this is where things like DBUnit come into the picture).

The set of Java classes thus created can form the core of a lightweight framework that could be used to build different applications using modern Java technologies.

The content was a result of a recent design and development effort, and producing it involved a lot of googling, and very little original effort. Enjoy and comment!
Next articles (work in progress):


Some notes on the choice of technologies
... for those who need that.
  • JPA and Hibernate: first of all, JPA seemed to be the best for a project that starts from scratch as a Java application, because it's a Java-centric technology. It would allow to do domain modeling in terms of Java objects and make the database schema follow that model and the changes in it automatically; performance considerations were written off as "premature optimization", because the application is supposed to be small-scale -- in the best spirit of agile development we would care about scaling it up when we get to that point.
  • Spring: nothing better for wiring all the rest together; also, Spring's declarative transactions via AOP enable us to use this aspect of J2EE without placing extra requirements on the container (the target platform is Tomcat, representing the lowest common denominator among Java application servers)
  • JSF: as the CRUD-type functionality requires sophisticated editing forms with in-line fields for dependent records, JSF and Facelets seemed the best technology for creating the edit forms and making them maintainable through using Facelet compositions; another aspect of Facelets is that this technology provides for using compositions for reusable parts of views, and for giving all the pages a common structure.

  • Spring MVC is a good supplement to the rather heavy view technology that is JSF: one might -- and usually does -- want to provide a simple controller or two for some special screens or features (AJAX/JSON, PDF, etc.) and not jump through JSF hoops to do that, while using a general-purpose MVC framework; Spring MVC can also provide for JSF navigation definitions' lack of scalability.

  • Spring Web Flow  is really something that one is almost forced to use given the previous choices (Spring + Facelets). JSF on its own is, sadly,  disappointing in everything except view composition mechanics. JSF/Facelets do not seem to scale as a complete view technology: its view-centric approach means it is unnecessarily difficult to provide for initialization, its navigation definition syntax is too verbose to maintain without heavy tools, and, finally, the view needs a mechanism for saving state that makes objects survive longer, than a single request-response, but not for the whole duration of a user session; a shopping cart is a typical example. Spring Faces and Spring Web Flow (SWF) provide for these shortcomings and add a lot more. That SWF is really solving a real life problem, rather than providing yet another idiosyncratic way of creating a Java web application, is indirectly confirmed by the fact that other frameworks aiming at reasonable completeness also provide work-flow features (I am thinking of JBoss Seam), and also do it with JSF/Facelets.

  • JAX-WS is simply the most modern -- and widely supported -- way of exposing services for integration. See one of the later articles on how to best expose Spring beans as JAX-WS services when using a minimal Servlet container such as Tomcat.

  • Maven is almost a must given the above mix of products. I have seen projects that were using Ant or other build tools being compelled to almost re-invent Maven just to maintain a dependency list: the number of libraries coming from different projects needed to use what has been listed above is many dozens, and each library comes in many different versions, some of which are not compatible with the versions that are required by other libraries. Apart from this -- already compelling -- need to be able to maintain a list of all the dependencies and dependencies of dependencies, Maven provides for simpler build definitions if the project is using a standard layout (and nothing would stop it from using that when starting from scratch). On top of that Maven provides a cheap solution for maintaining a firm-wide repository of code artefacts where each artefact (.jar, .war, ...) could be stored with a version number (so that several versions could be maintained) and also associated with source code archives (so that a framework library could be used by developers with source code for browsing and de-bugging). This last is again something that at least one of the major companies that I had been working for in the past had created in-house just to solve internal software distribution problems; Maven's repositories provide minimal working solution out of the box.