I am developing a webapp using Spring MVC + Hibernate. I created a GenericDao class that implements basic data access methods, to be extended by all my concrete daos in the app. What I want advice or review about is the exception handling of the data access layer exceptions. Let me post a shortened version of my generic Dao
class:
public class GenericDaoHibernateImpl<E, PK extends Serializable> extends AbstractDaoHibernateImpl implements
GenericDao<E, PK> {
private Class<E> entityClass;
@SuppressWarnings("unchecked")
public GenericDaoHibernateImpl() {
this.entityClass = (Class<E>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
public Class<E> getEntityClass() {
return entityClass;
}
public void saveOrUpdate(E entity) throws GenericDataBaseException {
try {
getSession().saveOrUpdate(entity);
} catch (Throwable t) {
Collection<Object> args = new ArrayList<Object>();
args.add(entity);
throw exceptionHandler.handle(this, t, "saveOrUpdate", args);
}
}
public void delete(PK id) throws GenericDataBaseException, InstanceNotFoundException {
try {
getSession().delete(findById(id));
} catch (InstanceNotFoundException t) {
throw t;
} catch (Throwable t) {
Collection<Object> args = new ArrayList<Object>();
args.add(id);
throw exceptionHandler.handle(this, t, "delete", args);
}
}
@SuppressWarnings("unchecked")
public E findById(PK id) throws GenericDataBaseException, InstanceNotFoundException {
try {
E entity = (E) getSession().get(entityClass, id);
if (entity == null)
throw new InstanceNotFoundException(id, entityClass.getName());
return entity;
} catch (InstanceNotFoundException t) {
throw t;
} catch (Throwable t) {
Collection<Object> args = new ArrayList<Object>();
args.add(id);
throw exceptionHandler.handle(this, t, "findById", args);
}
}
@SuppressWarnings("unchecked")
public List<E> findByProperty(String propertyName, Object propertyValue, String orderBy, boolean isOrderAsc,
int firstResult, int maxResults) throws GenericDataBaseException, NoSearchResultException {
try {
Criteria criteria = getSession().createCriteria(getEntityClass());
criteria.add(Expression.eq(propertyName, propertyValue));
criteria.addOrder(getOrder(orderBy, isOrderAsc));
criteria.setFirstResult(firstResult);
criteria.setMaxResults(maxResults);
List<E> result = criteria.list();
if (result == null || result.size() == 0)
throw new NoSearchResultException("", getEntityClass());
return result;
} catch(NoSearchResultException t)
{
throw t;
}
catch (Throwable t) {
Collection<Object> args = new ArrayList<Object>();
args.add(propertyName);
args.add(propertyValue);
args.add(orderBy);
args.add(isOrderAsc);
args.add(firstResult);
args.add(maxResults);
throw exceptionHandler.handle(this, t, "findByProperty", args);
}
}
}
What I do is throw my own created exceptions (InstanceNotFound
, NoSearchResults
, GenericDatabaseException
) and propagate them to the controller, where I catch them using @ExceptionHandler annotation:
@Controller
public class MyControllerextends BaseController {
public String myMethod(Model model) throws NoSearchResultException {
(...)
}
@ExceptionHandler(InstanceNotFoundException.class)
public ModelAndView instanceNotFoundException(InstanceNotFoundException e) {
String message ="Error inesperado";
message = "Element with provided ID was not found";
return devuelvePaginaError(message);
}
@ExceptionHandler(NoSearchResultException.class)
public ModelAndView handleNoSearchResultException(NoSearchResultException e) {
String message ="Error inesperado";
message = "Search of " + e.getObjectClass().getSimpleName() + " with no results";
return devuelvePaginaError(message);
}
}
My question is, am I doing this correctly? Should I catch my exceptions earlier, in another layer? Should I even model events like "instance not found" or "No search results" as Exceptions?