/**
* Copyright 2011 Intuit Inc. All Rights Reserved
*/
package com.intuit.tank.dao;
/*
* #%L
* Data Access
* %%
* Copyright (C) 2011 - 2015 Intuit Inc.
* %%
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
* #L%
*/
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceException;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.LockOptions;
import org.hibernate.Session;
import org.hibernate.envers.AuditReader;
import org.hibernate.envers.AuditReaderFactory;
import com.intuit.tank.project.BaseEntity;
import com.intuit.tank.view.filter.ViewFilterType;
/**
* BaseDao
*
* @author dangleton
*
*/
public abstract class BaseDao<T_ENTITY extends BaseEntity> {
private static final Logger LOG = LogManager.getLogger(BaseDao.class);
private static final ThreadLocalEntityManagerProvider emProvider = new ThreadLocalEntityManagerProvider();
private Class<T_ENTITY> entityClass;
private boolean reloadEntities;
/**
* @param entityClass
*/
@SuppressWarnings("unchecked")
protected BaseDao() {
// entity ManagerFactory = Persistence.createEntityManagerFactory("wats");
@SuppressWarnings("rawtypes") Class getClass = (Class) getClass();
ParameterizedType genericSuperclass = (ParameterizedType) getClass.getGenericSuperclass();
this.entityClass = (Class<T_ENTITY>) genericSuperclass.getActualTypeArguments()[0];
}
/**
* @return the reloadEntities
*/
boolean isReloadEntities() {
return reloadEntities;
}
/**
* @param reloadEntities
* the reloadEntities to set
*/
void setReloadEntities(boolean reloadEntities) {
this.reloadEntities = reloadEntities;
}
/**
*
* @param id
* @return
*/
public int getHeadRevisionNumber(int id) {
int result = 0;
try {
begin();
AuditReader reader = AuditReaderFactory.get(getEntityManager());
List<Number> revisions = reader.getRevisions(entityClass, id);
if (!revisions.isEmpty()) {
result = revisions.get(revisions.size() - 1).intValue();
}
commit();
} catch (NoResultException e) {
rollback();
LOG.warn("No result for revision with id of " + id);
} finally {
cleanup();
}
return result;
}
/**
* gets the entity at the specified revision
*
* @param id
* the id of the entity to fetch
* @param revisionNumber
* the revision number
* @return the entity or null if no entity can be found
*/
@Nullable
public T_ENTITY findRevision(int id, int revisionNumber) {
T_ENTITY result = null;
try {
begin();
AuditReader reader = AuditReaderFactory.get(getEntityManager());
result = reader.find(entityClass, id, revisionNumber);
commit();
} catch (NoResultException e) {
rollback();
LOG.warn("No result for revision " + revisionNumber + " with id of " + id);
} finally {
cleanup();
}
return result;
}
/**
* Persist or update the entity. Persist if the primary key is null update otherwise.
*
* @param entity
* the entity to persist
* @return the persisted unattached entity.
* @throws HibernateException
* if there is an error in persistence
*/
@Nonnull
public T_ENTITY saveOrUpdate(@Nonnull T_ENTITY entity) throws HibernateException {
EntityManager em = getEntityManager();
try {
begin();
if (entity.getId() == 0) {
em.persist(entity);
} else {
entity = em.merge(entity);
}
commit();
} catch (ConstraintViolationException e) {
for (@SuppressWarnings("rawtypes") ConstraintViolation v : e.getConstraintViolations()) {
LOG.warn("ConstraintViolation for " + entityClass.getSimpleName() + " "
+ "[property: " + v.getPropertyPath().iterator().next().getName() + "]" + " "
+ "[message: " + v.getMessage() + "]" + " "
+ "[invalid value: " + v.getInvalidValue() + "]");
}
throw e;
} catch (PersistenceException e) { // wrapped in a Persistence Exception
if (e.getCause() instanceof ConstraintViolationException) {
ConstraintViolationException cve = (ConstraintViolationException) e.getCause();
for (@SuppressWarnings("rawtypes") ConstraintViolation v : cve.getConstraintViolations()) {
LOG.warn("ConstraintViolation for " + entityClass.getSimpleName() + " "
+ "[property: " + v.getPropertyPath().iterator().next().getName() + "]" + " "
+ "[message: " + v.getMessage() + "]" + " "
+ "[invalid value: " + v.getInvalidValue() + "]");
}
throw cve;
}
throw e;
} catch (Exception e) {
rollback();
e.printStackTrace();
throw new RuntimeException(e);
} finally {
cleanup();
}
return entity;
}
/**
*
* @param entities
*/
public List<T_ENTITY> persistCollection(Collection<T_ENTITY> entities) {
List<T_ENTITY> ret = new ArrayList<T_ENTITY>();
EntityManager em = getEntityManager();
try {
begin();
int count = 0;
for (T_ENTITY entity : entities) {
if (entity.getId() == 0) {
em.persist(entity);
} else {
entity = em.merge(entity);
}
ret.add(entity);
if (++count % 1000 == 0) {
em.flush();
em.clear();
}
}
commit();
} catch (ConstraintViolationException e) {
for (@SuppressWarnings("rawtypes") ConstraintViolation v : e.getConstraintViolations()) {
LOG.warn("ConstraintViolation for " + entityClass.getSimpleName() + " "
+ "[property: " + v.getPropertyPath().iterator().next().getName() + "]" + " "
+ "[message: " + v.getMessage() + "]" + " "
+ "[invalid value: " + v.getInvalidValue() + "]");
}
throw e;
} catch (PersistenceException e) { // wrapped in a Persistence Exception
if (e.getCause() instanceof ConstraintViolationException) {
ConstraintViolationException cve = (ConstraintViolationException) e.getCause();
for (@SuppressWarnings("rawtypes") ConstraintViolation v : cve.getConstraintViolations()) {
LOG.warn("ConstraintViolation for " + entityClass.getSimpleName() + " "
+ "[property: " + v.getPropertyPath().iterator().next().getName() + "]" + " "
+ "[message: " + v.getMessage() + "]" + " "
+ "[invalid value: " + v.getInvalidValue() + "]");
}
throw cve;
}
throw e;
} catch (Exception e) {
rollback();
LOG.error("Error storing object to persistent storage: " + e.toString(), e);
throw new RuntimeException(e);
} finally {
cleanup();
}
return ret;
}
/**
* Deletes the entity from the datastore and applies cascade rules.
*
* @param id
* the id of the entity to delete
* @throws HibernateException
* if there is an error in persistence
*/
public void delete(@Nonnull Integer id) throws HibernateException {
EntityManager em = getEntityManager();
try {
begin();
T_ENTITY entity = em.find(entityClass, id);
if (entity != null) {
LOG.debug("deleting entity " + entity.toString());
em.remove(entity);
}
commit();
} catch (Exception e) {
rollback();
e.printStackTrace();
throw new RuntimeException(e);
} finally {
cleanup();
}
}
/**
* Deletes the entity from the datastore and applies cascade rules.
*
* @param entity
* the entity to delete
* @throws HibernateException
* if there is an error in persistence
*/
public void delete(@Nonnull T_ENTITY entity) throws HibernateException {
delete(entity.getId());
}
/**
* Gets an entity by the id or null if no entity exists with the specified id.
*
* @param id
* the primary key
* @return the entity or null
*/
@Nullable
public T_ENTITY findById(@Nonnull Integer id) {
T_ENTITY result = null;
try {
begin();
result = getEntityManager().find(entityClass, id);
if (reloadEntities && result != null) {
getHibernateSession().refresh(result, LockOptions.READ);
}
commit();
} catch (Exception e) {
rollback();
e.printStackTrace();
throw new RuntimeException(e);
} finally {
cleanup();
}
return result;
}
/**
* Finds all where in IDs
*
* @param ids
* @return
*/
@Nonnull
public List<T_ENTITY> findForIds(@Nonnull List<Integer> ids) {
String prefix = "x";
NamedParameter parameter = new NamedParameter(BaseEntity.PROPERTY_ID, "id", ids);
StringBuilder sb = new StringBuilder();
sb.append(buildQlSelect(prefix)).append(startWhere()).append(buildWhereClause(Operation.IN, prefix, parameter));
return listWithJQL(sb.toString(), parameter);
}
/**
* Finds all Objects of type T_ENTITY
*
* @return the nonnull list of entities
* @throws HibernateException
* if there is an error in persistence
*/
@Nonnull
public List<T_ENTITY> findAll() throws HibernateException {
List<T_ENTITY> results = null;
EntityManager em = getEntityManager();
try {
begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<T_ENTITY> query = cb.createQuery(entityClass);
query.from(entityClass);
results = em.createQuery(query).getResultList();
commit();
} catch (Exception e) {
rollback();
e.printStackTrace();
throw new RuntimeException(e);
} finally {
cleanup();
}
return results;
}
/**
* Find all objects of type T_ENTITY that satisfy the ViewFilterType
*
* @param viewFilter
* @return the list of entities that satisfy the filter
*/
public List<T_ENTITY> findFiltered(ViewFilterType viewFilter) {
List<T_ENTITY> results = null;
EntityManager em = getEntityManager();
try {
begin();
if (!viewFilter.equals(ViewFilterType.ALL)) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<T_ENTITY> query = cb.createQuery(entityClass);
Root<T_ENTITY> root = query.from(entityClass);
query.select(root);
query.where(cb.greaterThan(root.<Date>get(BaseEntity.PROPERTY_CREATE), ViewFilterType.getViewFilterDate(viewFilter)));
query.orderBy(cb.desc(root.get(BaseEntity.PROPERTY_CREATE)));
results = em.createQuery(query).getResultList();
} else {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<T_ENTITY> query = cb.createQuery(entityClass);
Root<T_ENTITY> root = query.from(entityClass);
query.select(root);
query.orderBy(cb.desc(root.get(BaseEntity.PROPERTY_CREATE)));
results = em.createQuery(query).getResultList();
}
commit();
} catch (Exception e) {
rollback();
e.printStackTrace();
throw new RuntimeException(e);
} finally {
cleanup();
}
return results;
}
/**
*
* @param qlString
* @param params
* @return
*/
@Nullable
public T_ENTITY findOneWithJQL(String qlString, NamedParameter... params) {
T_ENTITY result = null;
TypedQuery<T_ENTITY> query = null;
EntityManager em = getEntityManager();
try {
begin();
query = em.createQuery(qlString, entityClass);
for (NamedParameter param : params) {
query.setParameter(param.getName(), param.getValue());
}
result = query.getSingleResult();
commit();
} catch (Exception e) {
rollback();
LOG.info("no entity matching query "+query.toString());
} finally {
cleanup();
}
return result;
}
/**
* returns list of entities meeting the specified criteria.
*
* @param criterion
* varargs criterion. (use something like Restrictions.eq(propertyName, value);) null criterion returns
* all.
* @param sortOrder
* the Order Object. Null value indicates no sort order.
* @return the non null list.
* @throws HibernateException
* if there is an error in persistence
*/
@Nonnull
public List<T_ENTITY> listWithJQL(String qlString, NamedParameter... params) {
List<T_ENTITY> result = null;
EntityManager em = getEntityManager();
try {
begin();
TypedQuery<T_ENTITY> query = em.createQuery(qlString, entityClass);
for (NamedParameter param : params) {
query.setParameter(param.getName(), param.getValue());
}
result = query.getResultList();
commit();
} catch (Exception e) {
rollback();
e.printStackTrace();
throw new RuntimeException(e);
} finally {
cleanup();
}
return result;
}
protected String buildQlSelect(String prefix) {
return new StringBuilder().append("SELECT ").append(prefix).append(" FROM ").append(entityClass.getName())
.append(" AS ").append(prefix).append(" ").toString();
}
protected String buildWhereClause(Operation op, String prefix, NamedParameter param) {
if (op == Operation.NULL || op == Operation.NOT_NULL) {
return buildFieldId(prefix, param.getField()) + " " + op.getRepresentation();
}
return buildFieldId(prefix, param.getField()) + buildParameterName(op, param.getName());
}
protected String buildFieldId(String prefix, String field) {
return " " + prefix + "." + field + " ";
}
protected String buildParameterName(Operation op, String name) {
return " " + op.getRepresentation() + " :" + name + op.getEnding() + " ";
}
protected String buildSortOrderClause(SortDirection direction, String prefix, String field) {
return " ORDER BY " + prefix + "." + field + " " + direction.name();
}
/**
* @return
*/
protected String startWhere() {
return " WHERE ";
}
/**
* @return
*/
protected String getAnd() {
return " AND ";
}
/**
* Gets an open hibernate session.
*
* @return the session
*/
protected Session getHibernateSession() {
Session s = (Session) getEntityManager().getDelegate();
return s;
}
/**
* Gets a JPA EntityManager
*
* @return the entityManager
*/
protected EntityManager getEntityManager() {
return emProvider.get().getEntityManager();
}
/**
* @return the emProvider
*/
protected ThreadLocalEntityManagerProvider getEmProvider() {
return emProvider;
}
protected void begin() {
emProvider.get().startTrasaction(this);
}
protected void commit() {
emProvider.get().commitTransaction(this);
}
protected void rollback() {
emProvider.get().rollbackTransaction(this);
}
protected void cleanup() {
emProvider.get().cleanup(this);
}
public static class ThreadLocalEntityManagerProvider extends ThreadLocal<TransactionContainer> {
public TransactionContainer initialValue() {
return new TransactionContainer();
}
}
}