/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2006-2011 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <license@opennms.org>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/
package org.opennms.netmgt.dao.hibernate;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import javax.persistence.Table;
import org.hibernate.Criteria;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Projections;
import org.hibernate.metadata.ClassMetadata;
import org.opennms.core.utils.LogUtils;
import org.opennms.netmgt.dao.OnmsDao;
import org.opennms.netmgt.model.OnmsCriteria;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
/**
* <p>Abstract AbstractDaoHibernate class.</p>
*
* @author ranger
* @version $Id: $
*/
public abstract class AbstractDaoHibernate<T, K extends Serializable> extends HibernateDaoSupport implements OnmsDao<T, K> {
Class<T> m_entityClass;
private String m_lockName;
private final HibernateCriteriaConverter m_criteriaConverter = new HibernateCriteriaConverter();
/**
* <p>Constructor for AbstractDaoHibernate.</p>
*
* @param entityClass a {@link java.lang.Class} object.
* @param <T> a T object.
* @param <K> a K object.
*/
public AbstractDaoHibernate(final Class<T> entityClass) {
super();
m_entityClass = entityClass;
Table table = m_entityClass.getAnnotation(Table.class);
m_lockName = (table == null || "".equals(table.name()) ? m_entityClass.getSimpleName() : table.name()).toUpperCase() + "_ACCESS";
}
@Override
protected void initDao() throws Exception {
getHibernateTemplate().saveOrUpdate(new AccessLock(m_lockName));
}
/**
* This is used to lock the table in order to implement upsert type operations
*/
public void lock() {
getHibernateTemplate().get(AccessLock.class, m_lockName, LockMode.UPGRADE);
}
/** {@inheritDoc} */
public void initialize(final Object obj) {
getHibernateTemplate().initialize(obj);
}
/**
* <p>flush</p>
*/
public void flush() {
getHibernateTemplate().flush();
}
/**
* <p>clear</p>
*/
public void clear() {
getHibernateTemplate().clear();
}
/**
* <p>evict</p>
*
* @param entity a T object.
*/
public void evict(final T entity) {
getHibernateTemplate().evict(entity);
}
/**
* <p>merge</p>
*
* @param entity a T object.
*/
public void merge(final T entity) {
getHibernateTemplate().merge(entity);
}
/**
* <p>find</p>
*
* @param query a {@link java.lang.String} object.
* @return a {@link java.util.List} object.
*/
@SuppressWarnings("unchecked")
public List<T> find(final String query) {
return getHibernateTemplate().find(query);
}
/**
* <p>find</p>
*
* @param query a {@link java.lang.String} object.
* @param values a {@link java.lang.Object} object.
* @return a {@link java.util.List} object.
*/
@SuppressWarnings("unchecked")
public List<T> find(final String query, final Object... values) {
return getHibernateTemplate().find(query, values);
}
/**
* <p>findObjects</p>
*
* @param clazz a {@link java.lang.Class} object.
* @param query a {@link java.lang.String} object.
* @param values a {@link java.lang.Object} object.
* @param <S> a S object.
* @return a {@link java.util.List} object.
*/
@SuppressWarnings("unchecked")
public <S> List<S> findObjects(final Class<S> clazz, final String query, final Object... values) {
final List<S> notifs = getHibernateTemplate().find(query, values);
return notifs;
}
/**
* <p>queryInt</p>
*
* @param query a {@link java.lang.String} object.
* @return a int.
*/
protected int queryInt(final String query) {
final HibernateCallback<Number> callback = new HibernateCallback<Number>() {
public Number doInHibernate(final Session session) throws HibernateException {
return (Number)session.createQuery(query).uniqueResult();
}
};
return getHibernateTemplate().execute(callback).intValue();
}
/**
* <p>queryInt</p>
*
* @param queryString a {@link java.lang.String} object.
* @param args a {@link java.lang.Object} object.
* @return a int.
*/
protected int queryInt(final String queryString, final Object... args) {
final HibernateCallback<Number> callback = new HibernateCallback<Number>() {
public Number doInHibernate(final Session session) throws HibernateException, SQLException {
final Query query = session.createQuery(queryString);
for (int i = 0; i < args.length; i++) {
query.setParameter(i, args[i]);
}
return (Number)query.uniqueResult();
}
};
return getHibernateTemplate().execute(callback).intValue();
}
//TODO: This method duplicates below impl, delete this
/**
* <p>findUnique</p>
*
* @param query a {@link java.lang.String} object.
* @return a T object.
*/
protected T findUnique(final String query) {
return findUnique(m_entityClass, query);
}
/**
* <p>findUnique</p>
*
* @param queryString a {@link java.lang.String} object.
* @param args a {@link java.lang.Object} object.
* @return a T object.
*/
protected T findUnique(final String queryString, final Object... args) {
return findUnique(m_entityClass, queryString, args);
}
/**
* <p>findUnique</p>
*
* @param type a {@link java.lang.Class} object.
* @param queryString a {@link java.lang.String} object.
* @param args a {@link java.lang.Object} object.
* @param <S> a S object.
* @return a S object.
*/
protected <S> S findUnique(final Class <? extends S> type, final String queryString, final Object... args) {
final HibernateCallback<S> callback = new HibernateCallback<S>() {
public S doInHibernate(final Session session) throws HibernateException, SQLException {
final Query query = session.createQuery(queryString);
for (int i = 0; i < args.length; i++) {
query.setParameter(i, args[i]);
}
final Object result = query.uniqueResult();
return result == null ? null : type.cast(result);
}
};
// logger.debug(String.format("findUnique(%s, %s, %s) = %s", type, queryString, Arrays.toString(args), result));
// Assert.isTrue(result == null || type.isInstance(result), "Expected "+result+" to an instance of "+type+" but is "+(result == null ? null : result.getClass()));
return getHibernateTemplate().execute(callback);
}
/**
* <p>countAll</p>
*
* @return a int.
*/
public int countAll() {
return queryInt("select count(*) from " + m_entityClass.getName());
}
/**
* <p>delete</p>
*
* @param entity a T object.
* @throws org.springframework.dao.DataAccessException if any.
*/
public void delete(final T entity) throws DataAccessException {
getHibernateTemplate().delete(entity);
}
/**
* <p>deleteAll</p>
*
* @param entities a {@link java.util.Collection} object.
* @throws org.springframework.dao.DataAccessException if any.
*/
public void deleteAll(final Collection<T> entities) throws DataAccessException {
getHibernateTemplate().deleteAll(entities);
}
/**
* <p>findAll</p>
*
* @return a {@link java.util.List} object.
* @throws org.springframework.dao.DataAccessException if any.
*/
public List<T> findAll() throws DataAccessException {
return getHibernateTemplate().loadAll(m_entityClass);
}
/**
* <p>findMatchingObjects</p>
*
* @param type a {@link java.lang.Class} object.
* @param onmsCrit a {@link org.opennms.netmgt.model.OnmsCriteria} object.
* @param <S> a S object.
* @return a {@link java.util.List} object.
*/
@SuppressWarnings("unchecked")
public <S> List<S> findMatchingObjects(final Class<S> type, final OnmsCriteria onmsCrit ) {
onmsCrit.resultsOfType(type);
final HibernateCallback<S> callback = new HibernateCallback<S>() {
public S doInHibernate(final Session session) throws HibernateException, SQLException {
final Criteria attachedCrit = onmsCrit.getDetachedCriteria().getExecutableCriteria(session);
if (onmsCrit.getFirstResult() != null) attachedCrit.setFirstResult(onmsCrit.getFirstResult());
if (onmsCrit.getMaxResults() != null) attachedCrit.setMaxResults(onmsCrit.getMaxResults());
return (S)attachedCrit.list();
}
};
return getHibernateTemplate().executeFind(callback);
}
@SuppressWarnings("unchecked")
public List<T> findMatching(final org.opennms.core.criteria.Criteria criteria) {
final HibernateCallback<List<T>> callback = new HibernateCallback<List<T>>() {
public List<T> doInHibernate(final Session session) throws HibernateException, SQLException {
LogUtils.debugf(this, "criteria = %s", criteria);
final Criteria hibernateCriteria = m_criteriaConverter.convert(criteria, session);
return (List<T>)(hibernateCriteria.list());
}
};
return getHibernateTemplate().executeFind(callback);
}
/** {@inheritDoc} */
public int countMatching(final org.opennms.core.criteria.Criteria criteria) throws DataAccessException {
final HibernateCallback<Integer> callback = new HibernateCallback<Integer>() {
public Integer doInHibernate(final Session session) throws HibernateException, SQLException {
final Criteria hibernateCriteria = m_criteriaConverter.convert(criteria, session);
hibernateCriteria.setProjection(Projections.rowCount());
return (Integer)hibernateCriteria.uniqueResult();
}
};
return getHibernateTemplate().execute(callback).intValue();
}
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
public List<T> findMatching(final OnmsCriteria onmsCrit) throws DataAccessException {
onmsCrit.resultsOfType(m_entityClass); //FIXME: why is this here?
final HibernateCallback<List<T>> callback = new HibernateCallback<List<T>>() {
public List<T> doInHibernate(final Session session) throws HibernateException, SQLException {
final Criteria attachedCrit = onmsCrit.getDetachedCriteria().getExecutableCriteria(session);
if (onmsCrit.getFirstResult() != null) attachedCrit.setFirstResult(onmsCrit.getFirstResult());
if (onmsCrit.getMaxResults() != null) attachedCrit.setMaxResults(onmsCrit.getMaxResults());
return (List<T>)attachedCrit.list();
}
};
return getHibernateTemplate().executeFind(callback);
}
/** {@inheritDoc} */
public int countMatching(final OnmsCriteria onmsCrit) throws DataAccessException {
final HibernateCallback<Integer> callback = new HibernateCallback<Integer>() {
public Integer doInHibernate(final Session session) throws HibernateException, SQLException {
final Criteria attachedCrit = onmsCrit.getDetachedCriteria().getExecutableCriteria(session).setProjection(Projections.rowCount());
return (Integer)attachedCrit.uniqueResult();
}
};
return getHibernateTemplate().execute(callback).intValue();
}
/**
* <p>bulkDelete</p>
*
* @param hql a {@link java.lang.String} object.
* @param values an array of {@link java.lang.Object} objects.
* @return a int.
* @throws org.springframework.dao.DataAccessException if any.
*/
public int bulkDelete(final String hql, final Object[] values ) throws DataAccessException {
return getHibernateTemplate().bulkUpdate(hql, values);
}
/**
* <p>get</p>
*
* @param id a K object.
* @return a T object.
* @throws org.springframework.dao.DataAccessException if any.
*/
public T get(final K id) throws DataAccessException {
return m_entityClass.cast(getHibernateTemplate().get(m_entityClass, id));
}
/**
* <p>load</p>
*
* @param id a K object.
* @return a T object.
* @throws org.springframework.dao.DataAccessException if any.
*/
public T load(final K id) throws DataAccessException {
return m_entityClass.cast(getHibernateTemplate().load(m_entityClass, id));
}
/**
* <p>save</p>
*
* @param entity a T object.
* @throws org.springframework.dao.DataAccessException if any.
*/
public void save(final T entity) throws DataAccessException {
getHibernateTemplate().save(entity);
}
/**
* <p>saveOrUpdate</p>
*
* @param entity a T object.
* @throws org.springframework.dao.DataAccessException if any.
*/
public void saveOrUpdate(final T entity) throws DataAccessException {
try {
getHibernateTemplate().saveOrUpdate(entity);
} catch (final DataAccessException e) {
logExtraSaveOrUpdateExceptionInformation(entity, e);
// Rethrow the exception
throw e;
}
}
/**
* <p>update</p>
*
* @param entity a T object.
* @throws org.springframework.dao.DataAccessException if any.
*/
public void update(final T entity) throws DataAccessException {
try {
getHibernateTemplate().update(entity);
} catch (final DataAccessException e) {
logExtraSaveOrUpdateExceptionInformation(entity, e);
// Rethrow the exception
throw e;
}
}
/**
* <p>Parse the {@link DataAccessException} to see if special problems were
* encountered while performing the query. See issue NMS-5029 for examples of
* stack traces that can be thrown from these calls.</p>
* {@see http://issues.opennms.org/browse/NMS-5029}
*/
private void logExtraSaveOrUpdateExceptionInformation(final T entity, final DataAccessException e) {
Throwable cause = e;
while (cause.getCause() != null) {
//if (cause.getCause().getClass().getName().equals(PSQLException.class.getName())) {
if (cause.getMessage().contains("duplicate key value violates unique constraint")) {
final ClassMetadata meta = getSessionFactory().getClassMetadata(m_entityClass);
LogUtils.warnf(this, "Duplicate key constraint violation, class: %s, key value: %s", m_entityClass.getName(), meta.getPropertyValue(entity, meta.getIdentifierPropertyName(), EntityMode.POJO));
break;
} else if (cause.getMessage().contains("given object has a null identifier")) {
LogUtils.warnf(this, "Null identifier on object, class: %s: %s", m_entityClass.getName(), entity.toString());
break;
}
//}
cause = cause.getCause();
}
}
}