/*
jBilling - The Enterprise Open Source Billing System
Copyright (C) 2003-2011 Enterprise jBilling Software Ltd. and Emiliano Conde
This file is part of jbilling.
jbilling is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
jbilling 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with jbilling. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sapienter.jbilling.server.util.db;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import com.sapienter.jbilling.common.SessionInternalError;
import com.sapienter.jbilling.server.util.Context;
import org.hibernate.LockMode;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public abstract class AbstractDAS<T> extends HibernateDaoSupport {
private static final Logger LOG = Logger.getLogger(AbstractDAS.class);
private Class<T> persistentClass;
// if querys will be run cached or not
private boolean queriesCached = false;
@SuppressWarnings("unchecked")
public AbstractDAS() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
setSessionFactory((SessionFactory) Context.getBean(Context.Name.HIBERNATE_SESSION));
}
/**
* Merges the entity, creating or updating as necessary
*
* @param newEntity entity to save/update
* @return saved entity
*/
@SuppressWarnings("unchecked")
public T save(T newEntity) {
T retValue = (T) getSession().merge(newEntity);
return retValue;
}
public void delete(T entity) {
//em.remove(entity);
getHibernateTemplate().delete(entity);
}
public void refresh(T entity) {
getHibernateTemplate().refresh(entity);
}
public Class<T> getPersistentClass() {
return persistentClass;
}
/**
* This will load a proxy. If the row does not exist, it still returns an
* object (not null) and it will NOT throw an
* exception (until the other fields are accessed).
* Use this by default, if the row is missing, it is an error.
* @param id
* @return
*/
@SuppressWarnings("unchecked")
public T find(Serializable id) {
if (id == null) return null;
return getHibernateTemplate().load(getPersistentClass(), id);
}
/**
* This will hit the DB. If the row does not exist, it will NOT throw an
* exception but it WILL return NULL
* @param id
* @return
*/
@SuppressWarnings("unchecked")
public T findNow(Serializable id) {
if (id == null) return null;
return getHibernateTemplate().get(getPersistentClass(), id);
}
/**
* This will lock the row for the duration of this transaction. Or wait until the row is
* unlocked if it is already locked. It genererates a select ... for update
* @param id
* @return
*/
@SuppressWarnings("unchecked")
public T findForUpdate(Serializable id) {
if (id == null) {
return null;
}
return getHibernateTemplate().get(getPersistentClass(), id, LockMode.UPGRADE);
}
@SuppressWarnings("unchecked")
public List<T> findAll() {
return findByCriteria();
}
@SuppressWarnings("unchecked")
public List<T> findByExample(T exampleInstance, String... excludeProperty) {
Criteria crit = getSession().createCriteria(getPersistentClass());
Example example = Example.create(exampleInstance);
for (String exclude : excludeProperty) {
example.excludeProperty(exclude);
}
crit.add(example);
crit.setCacheable(queriesCached);
return crit.list();
}
@SuppressWarnings("unchecked")
public T findByExampleSingle(T exampleInstance, String... excludeProperty) {
Criteria crit = getSession().createCriteria(getPersistentClass());
Example example = Example.create(exampleInstance);
for (String exclude : excludeProperty) {
example.excludeProperty(exclude);
}
crit.add(example);
crit.setCacheable(queriesCached);
return (T) crit.uniqueResult();
}
@SuppressWarnings("unchecked")
public T makePersistent(T entity) {
getHibernateTemplate().saveOrUpdate(entity);
return entity;
}
public void makeTransient(T entity) {
getHibernateTemplate().delete(entity);
}
public void flush() {
getHibernateTemplate().flush();
}
public void clear() {
getHibernateTemplate().clear();
}
/**
* Returns true if a persisted record exsits for the given id.
*
* @param id primary key of entity
* @return true if entity exists for id, false if entity does not exist
*/
public boolean isIdPersisted(Serializable id) {
Criteria criteria = getSession().createCriteria(getPersistentClass())
.add(Restrictions.idEq(id))
.setProjection(Projections.rowCount());
return (criteria.uniqueResult() != null && ((Integer) criteria.uniqueResult()) > 0);
}
/**
* Use this inside subclasses as a convenience method.
*/
@SuppressWarnings("unchecked")
protected List<T> findByCriteria(Criterion... criterion) {
Criteria crit = getSession().createCriteria(getPersistentClass());
for (Criterion c : criterion) {
crit.add(c);
}
crit.setCacheable(queriesCached);
return crit.list();
}
@SuppressWarnings("unchecked")
protected T findByCriteriaSingle(Criterion... criterion) {
Criteria crit = getSession().createCriteria(getPersistentClass());
for (Criterion c : criterion) {
crit.add(c);
}
crit.setCacheable(queriesCached);
return (T) crit.uniqueResult();
}
protected void useCache() {
queriesCached = true;
}
/**
* Makes this DTO now attached to the session and part of the persistent context.
* This WILL trigger an update, which is usually fine since the reason to reattach
* is to modify the object.
* @param dto
*/
public void reattach(T dto) {
getSession().update(dto);
}
/**
* Places the DTO in the session without updates or version checkes.
* You have to make sure that the DTO has not been modified to use this
* @param dto
*/
public void reattachUnmodified(T dto) {
getSession().lock(dto, LockMode.NONE);
}
/**
* Detaches the DTO from the session. Updates to the object will
* no longer make it to the database.
*/
public void detach(T dto) {
getSession().flush(); // without this, get ready for the evil 'nonthreadsafe access to session'
getSession().evict(dto);
}
protected void touch(List<T> list, String methodName) {
try {
Method toCall = persistentClass.getMethod(methodName);
for(int f=0; list.size() < f; f++) {
toCall.invoke(list.get(f));
}
} catch (Exception e) {
throw new SessionInternalError("Error invoking method when touching proxy object",
AbstractDAS.class, e);
}
}
}