RepositoryBaseImpl.java
package com.tradecloud.repository.base.impl;
import com.tradecloud.authentication.MultiTenantUtil;
import com.tradecloud.common.externalreference.ExternalReference;
import com.tradecloud.domain.common.IntegratedPersistenceBase;
import com.tradecloud.domain.common.IntegratedStaticDataEntityBase;
import com.tradecloud.domain.exception.BaseBusinessException;
import com.tradecloud.domain.exception.InvalidEntityException;
import com.tradecloud.domain.infrastructure.persistence.CriteriaBuilder;
import com.tradecloud.domain.model.organisationalunit.OrganisationalUnit;
import com.tradecloud.domain.search.SearchParams;
import com.tradecloud.repository.SearchMetaParams;
import com.tradecloud.repository.base.RepositoryBase;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.criterion.*;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory;
import org.hibernate.hql.spi.QueryTranslator;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.loader.criteria.CriteriaJoinWalker;
import org.hibernate.loader.criteria.CriteriaQueryTranslator;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public class RepositoryBaseImpl<T, S> extends RepositorySearchBaseImpl<T, S> implements RepositoryBase<T, S> {
private static final long serialVersionUID = 1L;
protected static final String LIKE = "%";
private Class<T> persistentClass;
private static Logger log = Logger.getLogger(RepositoryBaseImpl.class);
@SuppressWarnings("unchecked")
public RepositoryBaseImpl() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
public RepositoryBaseImpl(final Class<T> persistentClass) {
super();
this.persistentClass = persistentClass;
}
@Override
public long count(String tableName) {
return ((BigInteger) getCurrentSession().createSQLQuery("select count(*) from " + tableName)
.uniqueResult()).longValue();
}
@Override
public long count() {
return (Long) getCurrentSession()
.createQuery("select count(*) from " + getPersistentClass().getSimpleName()).uniqueResult();
}
@Override
public List<T> findAll() {
return (List<T>) loadAll(persistentClass);
}
@Override
public void saveOrUpdate(final T entity) {
getCurrentSession().saveOrUpdate(entity);
}
@Override
public void save(final T entity) {
getCurrentSession().persist(entity);
}
@Override
public void persist(T entity) {
getCurrentSession().persist(entity);
}
@Override
public Serializable save2(T entity) {
return getCurrentSession().save(entity);
}
@Override
public T merge(final T entity) {
return (T) getCurrentSession().merge(entity);
}
@Override
public void delete(T entity) {
try {
getCurrentSession().delete(entity);
getCurrentSession().flush();
} catch (Exception e) {
if (entity != null) {
handleDeleteConstraint(entity.getClass().getSimpleName(), e);
}
throw e;
}
}
@Override
public void update(T entity) {
getCurrentSession().update(entity);
}
@Override
public T retrieve(Long id) {
//log.debug("Finding entity. Class '" + persistentClass + "'. Id '" + id + "'");
return (T) getCurrentSession().get(persistentClass, id);
}
@Override
public T retrieve(String code) {
// log.info("In find by code. Searching for " +
// persistentClass.getName() + " with code:" + code);
List<T> results = (List<T>) findByNamedParam("from " + persistentClass.getName() + " x where x.code = :code", "code", code);
if (results.size() > 1) {
log.warn("Found " + results.size() + " " + persistentClass.getName() + "s with code '" + code + "'. Returning first one");
}
if (!results.isEmpty()) {
return results.iterator().next();
}
return null;
}
@Override
public T retrieve(ExternalReference externalReference) {
String simpleName = persistentClass.getSimpleName();
return getT(externalReference, simpleName);
}
protected T getT(ExternalReference externalReference, String simpleName) {
StringBuilder sb = new StringBuilder("select t1 FROM " + simpleName + " t1")
.append(" join t1.externalReferences as er where ")
.append(" er.integratedSystem=:integratedSystem")
.append(" and er.referenceValue=:referenceValue");
Query query = getSessionCustom().createQuery(sb.toString());
query.setParameter("integratedSystem", externalReference.getIntegratedSystem());
query.setParameter("referenceValue", externalReference.getReferenceValue());
List<T> results = query.list();
return filterResult(externalReference, results);
}
protected T filterResult(ExternalReference externalReference, List<T> results) {
if (results.size() > 1) {
return failDueToDuplicateResults(externalReference, results);
} else if (results.size() == 1) {
return results.iterator().next();
} else {
return null;
}
}
protected T failDueToDuplicateResults(ExternalReference externalReference, List<T> results) {
throw new BaseBusinessException("External reference not unique. Count=" + results.size() + ". Type=" + persistentClass.getSimpleName() +
". System=" + externalReference.getIntegratedSystem().getName() + ". Reference=" + externalReference.getReferenceValue() + ".");
}
@Override
@Transactional(readOnly = true)
public List<T> findAll(SearchParams searchParams) {
DetachedCriteria criteria = DetachedCriteria.forClass(persistentClass);
CriteriaBuilder.applySearchParams(criteria, searchParams);
@SuppressWarnings("unchecked")
List<T> results = criteria.getExecutableCriteria(getSessionCustom()).list();
return results;
}
@Override
public long count(S search) {
DetachedCriteria criteria = DetachedCriteria.forClass(persistentClass);
// just do a "select count(*)" rather than a full table retrieve!
criteria.setProjection(Projections.rowCount());
List<Long> results = criteria.getExecutableCriteria(getSessionCustom()).list();
// will always have a result of the num of rows
if (results.isEmpty()) {
return 0;
} else {
return results.get(0);
}
}
@Override
public void flush() {
getCurrentSession().flush();
}
protected Class<T> getPersistentClass() {
return this.persistentClass;
}
protected void setPersistentClass(Class<T> persistentClass) {
this.persistentClass = persistentClass;
}
@Override
public void evict(final T entity) {
getCurrentSession().evict(entity);
}
/**
* Validates that the supplied object is not null. If it is, we throw an exception and specify the name of the object.
*
* @param object the object to check for null
* @param name the name of the field represented by the object, this will be used in the exception for information
* @throws IllegalArgumentException if object is null
*/
protected void validateNotNull(Object object, String name) {
if (object == null) {
throw new IllegalArgumentException("Must supply " + name);
}
}
protected List<T> getExecutableCriteriaList(DetachedCriteria detachedCriteria, SearchMetaParams searchMetaParams) {
StopWatch sw = new StopWatch();
sw.start();
Criteria criteria = applyMetaDataCriteria(detachedCriteria, searchMetaParams);
@SuppressWarnings("unchecked")
List<T> results = criteria.list();
sw.stop();
log.debug("Found " + results.size() + " results for list. Class=" + persistentClass
+ ". Meta params=" + searchMetaParams + " Time=" + sw.getTime());
return results;
}
protected Criteria applyMetaDataCriteria(DetachedCriteria detachedCriteria, SearchMetaParams searchMetaParams) {
if (searchMetaParams != null && searchMetaParams.getOrderBy() != null) {
detachedCriteria.addOrder(CriteriaBuilder.createOrdering(searchMetaParams.getOrderBy(), searchMetaParams.isAsc()));
}
Criteria criteria = detachedCriteria.getExecutableCriteria(getSessionCustom());
if (searchMetaParams != null && searchMetaParams.isPaged()) {
criteria.setFirstResult(searchMetaParams.getRowIndex());
criteria.setMaxResults(searchMetaParams.getRowCount());
}
return criteria;
}
/**
* Method guarantees result ordering. Method not placed in RepositoryBase interface.
*
* @param detachedCriteria
* @param searchMetaParams
* @param orderByField
* @param asc
* @return
*/
public List<T> getExecutableCriteriaList(DetachedCriteria detachedCriteria, SearchMetaParams searchMetaParams,
String orderByField, boolean asc) {
detachedCriteria.addOrder(CriteriaBuilder.createOrdering(orderByField, asc));
Criteria criteria = detachedCriteria.getExecutableCriteria(getSessionCustom());
if (searchMetaParams != null) {
criteria.setFirstResult(searchMetaParams.getRowIndex());
criteria.setMaxResults(searchMetaParams.getRowCount());
}
@SuppressWarnings("unchecked")
List<T> results = criteria.list();
log.debug("Found " + results.size() + " results for list. Class=" + persistentClass + ". Meta params=" + searchMetaParams);
return results;
}
protected long getExecutableCriteriaCount(DetachedCriteria detachedCriteria) {
Projection projection = Projections.rowCount();
return getExecutableCriteriaCount(detachedCriteria, projection);
}
protected long getExecutableCriteriaCount(DetachedCriteria detachedCriteria, Projection projection) {
StopWatch sw = new StopWatch();
sw.start();
// Got duplicate search results unless this was added. Think it is related to org units
// detachedCriteria.setResultTransformer(CriteriaOperation.DISTINCT_ROOT_ENTITY);
detachedCriteria.setProjection(projection);
long count = (Long) detachedCriteria.getExecutableCriteria(getSessionCustom()).list().get(0);
sw.stop();
log.debug("Found " + count + " results for count. Class=" + persistentClass + ". Time=" + sw.getTime());
return count;
}
protected long getExecutableCriteriaCount(Criteria criteria) {
// Got duplicate search results unless this was added. Think it is related to org units
// detachedCriteria.setResultTransformer(CriteriaOperation.DISTINCT_ROOT_ENTITY);
criteria.setProjection(Projections.rowCount());
long count = (Long) criteria.list().get(0);
log.debug("Found " + count + " results for count. Class=" + persistentClass);
return count;
}
@Override
public T getById(Long id) {
return (T) getCurrentSession().get(persistentClass, id);
}
@Override
public List<T> search(S search) {
throw new NotImplementedException(persistentClass.getSimpleName() + " search");
}
/**
* Default method that prevents use in the case this is not used correctly. Designed to be overridden by sub-classes that use the
* {@link #search(Object, SearchMetaParams)} method to specify the specific search criteria
**/
@Override
protected Collection<CriteriaValue> mapFieldsToValues(S search) {
throw new UnsupportedOperationException("Classes that do not override the mapFieldsToValues(SearchBase search) method are not allowed to"
+ " execute the createQuery(SearchBase search, String selectPrefix, String tableName) method");
}
/**
* Utility method. Also centralise the slightly bad cast.
*/
protected List<?> findByNamedQueryAndNamedParam(String query, String paramName, Object value) {
return (List<?>) getNamedQueryAndNamedParam(query, paramName, value);
}
protected List<?> findByNamedQueryAndNamedParam(String query, String paramName, Object... value) {
return (List<?>) getNamedQueryAndNamedParam(query, paramName, StringUtils.collectionToCommaDelimitedString(Arrays.asList(value)));
}
protected List<?> findByNamedQueryAndNamedParam(String query, String paramName, List<?> value) {
return (List<?>) getNamedQueryAndNamedParamList(query, paramName, value);
}
/**
* Utility method. Also centralise the slightly bad cast.
*/
protected List<?> findByNamedQueryAndNamedParam(String query, String paramName[], Object value[]) {
return (List<?>) getNamedQueryAndNamedParam(query, paramName, value);
}
protected static void initQueryParams(Query query, SearchMetaParams searchMetaParams) {
if (searchMetaParams != null) {
query.setFirstResult(searchMetaParams.getRowIndex());
query.setMaxResults(searchMetaParams.getRowCount());
}
}
private void validateExternalReference(List<Serializable> list, Serializable id, String externalReferenceValue) {
if (id == null) {
if (!list.isEmpty()) {
throw new InvalidEntityException("Duplicate external reference " + externalReferenceValue);
}
} else {
Set clean = new HashSet<>(list);
if (clean.size() > 1) {
throw new InvalidEntityException("Duplicate external reference " + externalReferenceValue);
} else if (clean.size() == 1) {
if (!clean.iterator().next().equals(id)) {
throw new InvalidEntityException("Duplicate external reference " + externalReferenceValue);
}
}
}
}
/**
* Putting this here because it is common to a few places.
*
* @param <X>
* @param c
* @param externalReferences
* @param id
*/
protected <X extends IntegratedPersistenceBase> void validatePBExternalReferences(Class<X> c, Collection<ExternalReference> externalReferences,
Serializable id) {
for (ExternalReference externalReference : externalReferences) {
DetachedCriteria criteria = DetachedCriteria.forClass(c);
criteria.setProjection(Projections.property("id"));
criteria.add(Restrictions.eq("active", true));
criteria.createCriteria("externalReferences")
.add(Restrictions.eq("integratedSystem", externalReference.getIntegratedSystem()))
.add(Restrictions.eq("referenceValue", externalReference.getReferenceValue()));
List<Serializable> list = criteria.getExecutableCriteria(getSessionCustom()).list();
validateExternalReference(list, id, externalReference.getReferenceValue());
}
}
protected <X extends IntegratedStaticDataEntityBase> void validateISDEBExternalReferenceList(Class<X> c,
Collection<ExternalReference> externalReferences,
Serializable id) {
for (ExternalReference externalReference : externalReferences) {
DetachedCriteria criteria = DetachedCriteria.forClass(c);
criteria.setProjection(Projections.property("code"));
criteria.add(Restrictions.eq("active", true));
DetachedCriteria extRefCrit = criteria.createCriteria("externalReferenceWrapper");
extRefCrit.createCriteria("externalReferences")
.add(Restrictions.eq("integratedSystem", externalReference.getIntegratedSystem()))
.add(Restrictions.eq("referenceValue", externalReference.getReferenceValue()));
List<Serializable> list = criteria.getExecutableCriteria(getSessionCustom()).list();
validateExternalReference(list, id, externalReference.getReferenceValue());
}
}
protected Set<OrganisationalUnit> getUserOrganisationalUnits() {
Set<OrganisationalUnit> organisationalUnits = MultiTenantUtil.getActiveUser().getOrganisationalUnits();
if (CollectionUtils.isEmpty(organisationalUnits) && CollectionUtils.isEmpty(MultiTenantUtil.getActiveUser().getOrganisationalUnit())) {
throw new IllegalStateException("filter by org is true, but organisational unit is not set");
}
if (CollectionUtils.isNotEmpty(organisationalUnits))
return organisationalUnits;
if (CollectionUtils.isNotEmpty(MultiTenantUtil.getActiveUser().getOrganisationalUnit()))
return MultiTenantUtil.getActiveUser().getOrganisationalUnit();
throw new IllegalStateException("filter by org is true, but organisational unit is not set");
}
protected String getOrgIdsAsString(Collection<OrganisationalUnit> userOrganisationalUnits) {
List<String> orgIdString = userOrganisationalUnits.stream()
.map(organisationalUnit -> organisationalUnit.getId().toString()).collect(Collectors.toList());
return org.apache.commons.lang3.StringUtils.join(orgIdString, ",");
}
protected String getOrgIdsAsCode(Collection<OrganisationalUnit> userOrganisationalUnits) {
List<String> orgIdString = userOrganisationalUnits.stream()
.map(organisationalUnit -> "'" + organisationalUnit.getCode() + "'").collect(Collectors.toList());
return org.apache.commons.lang3.StringUtils.join(orgIdString, ",");
}
protected <T> List<T> findAll(SearchParams searchParams, DetachedCriteria detachedCriteria) {
Order resultOrdering = searchParams.isDescending() == true ? Order.desc(searchParams.getOrderBy()) : Order.asc(searchParams.getOrderBy());
DetachedCriteria criteria = detachedCriteria.addOrder(resultOrdering);
// Check if we are only searching for active entities
if (searchParams.isActiveOnly()) {
criteria.add(Restrictions.eq("active", true));
}
@SuppressWarnings("unchecked")
List<T> results = criteria.getExecutableCriteria(getSessionCustom()).list();
return results;
}
@Override
public String criteriaToSQLQuery(DetachedCriteria criteria) {
CriteriaImpl criteriaImpl = (CriteriaImpl) criteria.getExecutableCriteria(getCurrentSession());
return criteriaToSQLQuery(criteriaImpl);
}
@Override
public String criteriaToSQLQuery(org.hibernate.query.Query query) {
String hqlQueryString = query.unwrap(Query.class).getQueryString();
//#hql to sql
//String hqlQueryString = sb.toString();
ASTQueryTranslatorFactory queryTranslatorFactory = new ASTQueryTranslatorFactory();
SessionImplementor hibernateSession = getCurrentSession().getSession().unwrap(SessionImplementor.class);
QueryTranslator queryTranslator = queryTranslatorFactory.createQueryTranslator("", hqlQueryString, java.util.Collections.EMPTY_MAP, hibernateSession.getFactory(), null);
queryTranslator.compile(java.util.Collections.EMPTY_MAP, false);
return queryTranslator.getSQLString();
}
public String criteriaToSQLQuery(Criteria criteria) {
CriteriaImpl criteriaImpl = (CriteriaImpl) criteria;
SessionImplementor session = (SessionImplementor) criteriaImpl.getSession();
SessionFactoryImplementor factory = session.getFactory();
CriteriaQueryTranslator translator = new CriteriaQueryTranslator(factory, criteriaImpl, criteriaImpl.getEntityOrClassName(),
CriteriaQueryTranslator.ROOT_SQL_ALIAS);
String[] implementors = factory.getImplementors(criteriaImpl.getEntityOrClassName());
CriteriaJoinWalker walker = new CriteriaJoinWalker((OuterJoinLoadable) factory.getEntityPersister(implementors[0]),
translator,
factory,
criteriaImpl,
criteriaImpl.getEntityOrClassName(),
session.getLoadQueryInfluencers());
String sql = walker.getSQLString();
return sql;
}
public static String toLIKE(String s) {
return LIKE + s + LIKE;
}
}