/**
* @Date: Feb 26, 2010 1:29:59 PM
*/
package com.philip.journal.core.dao.spring.hibernate;
//import static com.philip.core.WarningType.UNCHECKED;
import static com.philip.journal.core.dao.DaoConstant.LIST;
import static com.philip.journal.core.dao.DaoConstant.UNIQUE;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.MappingException;
import org.hibernate.PropertyValueException;
import org.hibernate.QueryException;
import org.hibernate.Session;
import org.hibernate.TransientObjectException;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException;
import org.springframework.orm.hibernate3.HibernateSystemException;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import com.philip.core.WarningType;
import com.philip.journal.common.BeanUtils;
import com.philip.journal.core.Messages;
import com.philip.journal.core.Messages.Error;
import com.philip.journal.core.bean.AbstractBean;
import com.philip.journal.core.exception.JournalException;
/**
* @param <E> - AbstractBean to which this DAO subclass will act.
*
* @author cry30
*/
public abstract class BaseDAOSpringHibernate<E extends AbstractBean> extends HibernateDaoSupport { // NOPMD by r39 on 3/30/11 12:06 PM
/** This field is for subclass use. Will need to define class level logger to trace this class. */
private final Log logger = LogFactory.getLog(BaseDAOSpringHibernate.class); // NOPMD by r39 on 3/30/11 12:02 PM
/** Generic type to which this instance is based. */
private transient Class<E> type;
/**
* @param pType the type to set.
* @return this instance.
*/
protected BaseDAOSpringHibernate<E> setType(final Class<E> pType)
{
this.type = pType;
return this;
}
/**
* @param <T> The journal bean as subject of this DAO.
*
* Factory method for unit testing.
*
* @param targetClass Test target Entity class.
* @return BaseDAOSpringHibernate instance.
*/
public static <T extends AbstractBean> BaseDAOSpringHibernate<T> newInstance(final Class<T> targetClass)
{
return new BaseDAOSpringHibernate<T>() {}.setType(targetClass);
}
/** Currently supported sql criteria types. */
protected enum CriteriaType {
/** RTFC. */
Equal, NotEqual, Like, Ilike
};
/** Class logger. */
private static final Logger LOGGER = Logger.getLogger(BaseDAOSpringHibernate.class); // NOPMD by r39 on 3/30/11 12:06 PM
/**
* Deletes all specified object.
*
* @param listObj - the list of objects to delete.
*
* @exception IllegalArgumentException If any of the following is true:
* <ul>
* <li>when the object passed is null.
* <li>when one of the items in the listObj is null or is not a valid Branch.
* </ul>
*
* @exception JournalException when delete fails due to missing object for delete due to concurrency.
*/
protected void deleteAll0(final List<?> listObj)
{
if (listObj == null) {
throw JournalException.wrapperException(new IllegalArgumentException(Messages.Error.IAE_NULL));
}
for (final Object object : listObj) {
if (object == null) {
throw JournalException.wrapperException(new IllegalArgumentException(Messages.Error.IAE_NULL));
}
}
final HibernateTemplate hibernateTemplate = getHibernateTemplate();
try {
hibernateTemplate.deleteAll(listObj);
hibernateTemplate.flush();
} catch (final HibernateOptimisticLockingFailureException holfe) {
throw new JournalException(Messages.Error.JE_OBJ_MISS_ERR, holfe);
} catch (final DataIntegrityViolationException exception) {
throw new JournalException(exception.getMessage(), exception.getCause().getCause());//NOPMD Wrap internal exception with client exception.
}
}
/**
* Reads all objects.
*
* @return List of all objects.
*/
@SuppressWarnings(WarningType.UNCHECKED)
protected List<E> readAll0()
{
return getHibernateTemplate().<List<E>> execute(new HibernateCallback<List<E>>() {
@Override
public List<E> doInHibernate(final Session session) throws SQLException
{
return session.createCriteria(getTargetClass()).list();
}
});
}
/**
* Reads all records whose property matches the given value. String comparison is case sensitive.
*
* @param property property name.
* @param value property value.
* @return List of entities matching the given property name-value.
* @exception JournalException when the property passed is null;
*/
protected List<E> readAll0(final String property, final Object value)
{
if (property == null) {
throw JournalException.wrapperException(new IllegalArgumentException(Messages.Error.IAE_NULL));
}
final Map<String, Object> param = new HashMap<String, Object>();
param.put(property, value);
return this.<List<E>> readObject(param, LIST, CriteriaType.Equal);
}
/**
* returns List of objects with ordering.
*
* @param orderCriteria - String array of colon (:) separated values. (e.g. name:desc, startDate:asc)
* @return Sorted list of objects.
*/
@SuppressWarnings(WarningType.UNCHECKED)
protected List<E> readAll0(final String[] orderCriteria)
{
final Criteria criteria = getSession().createCriteria(getTargetClass());
String[] tokens;
final Class<String>[] strClsArr = new Class[] { String.class };
final Object[] objParam = new Object[1];
for (final String string : orderCriteria) {
tokens = string.split(":");
Order order;
try {
objParam[0] = tokens[0];
order = (Order) Order.class.getMethod(tokens[1], strClsArr).invoke(null, objParam);
} catch (final Exception e) {
throw new JournalException(e.getMessage(), e);
}
criteria.addOrder(order);
}
final List<E> retval = new ArrayList<E>();
@SuppressWarnings(WarningType.RAW)
final List list = criteria.list();
for (final Object object : list) {
retval.add((E) object);
}
return retval;
}
/**
* Deletes the given persistent object.
*
* @param object Entity instance to delete.
* @exception JournalException when the data to be delete is not found. This can happen due to concurrency or
* passing incorrect ID.
*/
protected void delete(final Object object)
{
if (object == null) {
throw JournalException.wrapperException(new IllegalArgumentException(
"Developer Error. Cannot delete null object"));
}
final HibernateTemplate hibernateTemplate = getHibernateTemplate();
long pkeyId;
final String fieldName = (String) BeanUtils.getProperty(object, "primaryKeyField");
pkeyId = (Long) BeanUtils.getProperty(object, fieldName);
if (hibernateTemplate.get(getTargetClass(), pkeyId) == null) {
throw new JournalException("Error deleting non-existent entity.");
}
hibernateTemplate.delete(object);
hibernateTemplate.flush();
}
/**
* This will provide this abstract class to use the correct data object. CCE try-catch is a workaround with spring
* to explicitly pass the type since it could not be determined at runtime.
*
* @return Target Entity class.
*/
@SuppressWarnings(WarningType.UNCHECKED)
protected Class<E> getTargetClass()
{
Class<E> retval = null;
if (this.type == null) {
retval = (Class<E>) ((java.lang.reflect.ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
} else {
retval = this.type;
}
return retval;
}
/**
* Reads all objects matching a list of criteria. Criteria list will need to match all specified criterion.
*
* @param param - Mapping of property to value for the object match.
* @return List of objects that matches all the property-value param.
*/
protected List<E> readAll(final Map<String, Object> param)
{
return this.<List<E>> readObject(param, LIST);
}
/**
* Reads all objects matching (using LIKE operator) a list of criteria. Criteria list will need to match all
* specified criterion.
*
* @param param - Mapping of property to value for the object match.
* @return List of objects that matches all the property-value param.
*/
protected List<E> readAllLike(final Map<String, String> param)
{
return this.<List<E>> readObject(convertStrStrMapToStrObj(param), LIST, CriteriaType.Like);
}
/**
* Helper method to convert Map<String, String> to Map<String, Object>.
*
* @param mapStrStr input.
* @return converted Map.
*/
private Map<String, Object> convertStrStrMapToStrObj(final Map<String, String> mapStrStr)
{
final Map<String, Object> newParam = new HashMap<String, Object>();
for (final String next : mapStrStr.keySet()) {
final Object val = mapStrStr.get(next);
newParam.put(next, val == null ? null : val.toString());
}
return newParam;
}
/**
* Reads all objects matching (using LIKE operator case insensitive) a list of criteria. Criteria list will need to
* match all specified criterion.
*
* @param param - Mapping of property to value for the object match.
* @return List of objects that matches all the property-value param.
*/
protected List<E> readAllIlike(final Map<String, String> param)
{
return this.<List<E>> readObject(convertStrStrMapToStrObj(param), LIST, CriteriaType.Ilike);
}
/**
* @param parentBeanProp - current property name that refers to the parent bean
* @param parentProp - property name of the parent bean to match
* @param parentPropValue - value to match in the parentProperty parameter
* @return List of all domain object matching the specified parameters.
* @exception IllegalArgumentException when the parentPropertyValue is relationship-mapped hibernate class;
*/
@SuppressWarnings(WarningType.UNCHECKED)
protected List<E> readAllByParent(final String parentBeanProp, final String parentProp, final Object parentPropValue)
{
return getHibernateTemplate().<List<E>> execute(new HibernateCallback<List<E>>() {
@Override
public List<E> doInHibernate(final Session session) throws SQLException
{
final Criteria crit = session.createCriteria(getTargetClass());
crit.createCriteria(parentBeanProp).add(Restrictions.eq(parentProp, parentPropValue));
try {
final List<?> ret = crit.list();
final List<E> castedList = new ArrayList<E>();
for (final Object nextObj : ret) {
castedList.add((E) nextObj);
}
return castedList;
} catch (final TransientObjectException toe) {
throw new IllegalArgumentException("Mapping backed property cannot be used.", toe);
}
}
});
}
/**
* Reads the first object matching a list of criteria. Criteria list will need exactly match all specified
* criterion.
*
* @param param - Mapping of property to value for the object match.
* @return The first object that matched all the property-value param. null if no match is found.
* @exception JournalException when the parameter passed is null or when the query results in more than one record.
*/
protected E readObject(final Map<String, Object> param)
{
if (param == null) {
throw JournalException.wrapperException(new IllegalArgumentException("Map parameter cannot be null."));
}
return this.<E> readObject(param, UNIQUE);
}
/**
* Reads the first object matching the named query. <br/>
*
* @param namedQuery - named query defined in one of the mapping configurations.
* @param map criteria map to match.
* @exception JournalException If any of the following is true:
* <ul>
* <li>when the namedQuery or map parameter passed is null.
* <li>when there is mismatch on the object type of a given property in the map.
* </ul>
*
* @return Entity object matching the named query and satisfying the criteria map.
*/
protected E readObject(final String namedQuery, final Map<String, Object> map)
{
if (namedQuery == null || map == null) {
throw JournalException.wrapperException(new IllegalArgumentException(Messages.Error.IAE_NULL));
}
try {
final HibernateCallback<E> callBack = new ReadObjectCallback0<E>(namedQuery, map);
return getHibernateTemplate().execute(callBack);
} catch (final HibernateSystemException hse) {
if (hse.getCause() instanceof MappingException) {
throw JournalException.wrapperException(new IllegalArgumentException("Invalid namedQuery: "
+ namedQuery, hse));
} else {
throw JournalException.wrapperException(hse);
}
}
}
/**
* Reusable method to find the first matching object given a property and the exact value to match.
*
* @param property - target bean property to match with.
* @param value - value to match.
* @return The first matching object. null if no match was found.
* @exception JournalException If any of the following is true:
* <ul>
* <li> <code>value</code> did not match the bean field type.</li>
* <li> <code>property</code> is null.</li>
* </ul>
*/
protected E readObject(final String property, final Object value)
{
if (property == null) {
throw JournalException.wrapperException(new IllegalArgumentException(Error.IAE_NULL));
}
final Map<String, Object> param = new HashMap<String, Object>();
param.put(property, value);
return this.<E> readObject(param, UNIQUE, CriteriaType.Equal);
}
/**
* Retrieves a bean matching a referenced master Object along with a matching property for the bean.
*
* @param parentBeanProp - bean property name of which the type is the master bean.
* @param parentProp - property name of the master bean.
* @param parentPropValue - property value of the master object.
* @param beanProperty - ordinary property name of the bean
* @param beanValue - value of the beanProp param.
*
* @exception NullPointerException If any of the following is true:
* <ul>
* <li><code>parentBeanProperty</code> is <code>null</code>.
* <li><code>parentProperty</code> is <code>null</code>.
* <li><code>beanProperty</code> is <code>null</code>.
* </ul>
* @exception JournalException If any of the following is true:
* <ul>
* <li><code>parentBeanProp</code>could not be resolved.
* <li><code>parentProp</code>could not be resolved.
* <li><code>beanProperty</code>could not be resolved.
* <li><code>parentPropertyValue</code> did not match the parent bean field type.
* <li><code>beanValue</code> did not match the bean field type.
*
* </ul>
* @return the matched bean. null if it cannot be found.
*/
@SuppressWarnings(com.philip.core.WarningType.UNCHECKED)
protected E readObjectByParent(final String parentBeanProp, final String parentProp, final Object parentPropValue,
final String beanProperty, final Object beanValue)
{
return getHibernateTemplate().execute(new HibernateCallback<E>() {
@Override
public E doInHibernate(final Session session) throws SQLException
{
final Criteria crit = session.createCriteria(getTargetClass());
crit.createCriteria(parentBeanProp).add(Restrictions.eq(parentProp, parentPropValue));
crit.add(Restrictions.eq(beanProperty, beanValue));
try {
return (E) crit.uniqueResult();
} catch (final QueryException qe) {
throw JournalException.wrapperException(new IllegalArgumentException(qe.getMessage(), qe));
} catch (final NullPointerException qe) { // NOPMD by r39 - Hibernate internal design throws the NullPointerException.
throw JournalException.wrapperException(new IllegalArgumentException(qe.getMessage(), qe));
} catch (final ClassCastException qe) {
throw JournalException.wrapperException(new IllegalArgumentException(qe.getMessage(), qe));
}
}
});
}
/**
* Insert or update the given object.
*
* @param object - hibernate mapped entity.
* @exception JournalException If any of the following is true:
* <ul>
* <li><code>object</code> passed has a non-existent primary key value.
* <li>null value on a not-null property constraint.
* </ul>
*
* @exception JournalException when the object passed is null.
*/
protected void save(final E object)
{
if (object == null) {
throw JournalException.wrapperException(new IllegalArgumentException(Messages.Error.IAE_NULL));
}
try {
final HibernateTemplate hibernateTemplate = getHibernateTemplate();
hibernateTemplate.saveOrUpdate(object);
hibernateTemplate.flush();
} catch (final HibernateSystemException hse) {
LOGGER.error(hse.getMessage());
} catch (final DataAccessException dae) {
final Throwable cause = dae.getCause();
if (cause.getClass().equals(org.hibernate.StaleStateException.class)) {
throw new JournalException(Messages.Error.IAE_GENERIC, Messages.Error.IAE_INV_UPDATE,
cause.getMessage(), dae);
} else if (cause.getClass().equals(PropertyValueException.class)) {
throw new JournalException(cause.getMessage(), cause); // NOPMD by r39
} else if (cause.getClass().equals(TransientObjectException.class)) {
throw new JournalException(Messages.Error.JE_UNSVD_ROBJ_ERR, cause); // NOPMD by
} else {
final String errorMsg = ((SQLException) dae.getCause().getCause()).getMessage();
throw new JournalException(errorMsg, dae);
}
} catch (final Exception e) {
throw new JournalException(e.getMessage(), e);
}
}
/**
* Helper method to read a List or a single object matching the name-value criteria.
*
* @param <T> Entity type or List of Entity type.
* @param param - name value pair to match for.
* @param methodName - specify the method to use for the result whether list or uniqueResult.
* @param criteriaType Criteria type based on enumerated supported criterias.
* @return List or a single object matching the name-value criteria depending on the specified methodName.
*/
private <T> T readObject(final Map<String, Object> param, final String methodName, final CriteriaType criteriaType)
{
return getHibernateTemplate().execute(
new ReadObjectCallback1<T>(param, methodName, criteriaType, getTargetClass()));
}
/**
* Helper method to read a List or a single object matching the name-value criteria. Defaults to use Equal operator.
*
* @param <T> Entity type or List of Entity type.
* @param param - name value pair to match for.
* @param methodName - specify the method to use for the result whether list or uniqueResult.
* @return List or a single object matching the name-value criteria depending on the specified methodName.
*/
private <T> T readObject(final Map<String, Object> param, final String methodName)
{
return this.<T> readObject(param, methodName, CriteriaType.Equal);
}
/**
* Accessor method.
*
* @return the logger
*/
public Log getLogger()
{
return logger;
}
}