-/**\r
-* Copyright (C) 2007 EDIT\r
-* European Distributed Institute of Taxonomy\r
-* http://www.e-taxonomy.eu\r
-*\r
-* The contents of this file are subject to the Mozilla Public License Version 1.1\r
-* See LICENSE.TXT at the top of this package for the full license terms.\r
-*/\r
-\r
-package eu.etaxonomy.cdm.persistence.dao.hibernate.common;\r
-\r
-import java.lang.reflect.Field;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-import java.util.UUID;\r
-\r
-import org.apache.log4j.Logger;\r
-import org.apache.lucene.search.Sort;\r
-import org.apache.lucene.search.SortField;\r
-import org.hibernate.Criteria;\r
-import org.hibernate.FlushMode;\r
-import org.hibernate.HibernateException;\r
-import org.hibernate.LockOptions;\r
-import org.hibernate.NonUniqueObjectException;\r
-import org.hibernate.Query;\r
-import org.hibernate.Session;\r
-import org.hibernate.criterion.Criterion;\r
-import org.hibernate.criterion.DetachedCriteria;\r
-import org.hibernate.criterion.Example;\r
-import org.hibernate.criterion.Example.PropertySelector;\r
-import org.hibernate.criterion.Order;\r
-import org.hibernate.criterion.ProjectionList;\r
-import org.hibernate.criterion.Projections;\r
-import org.hibernate.criterion.Restrictions;\r
-import org.hibernate.envers.query.AuditQuery;\r
-import org.hibernate.metadata.ClassMetadata;\r
-import org.hibernate.search.FullTextQuery;\r
-import org.hibernate.type.Type;\r
-import org.joda.time.DateTime;\r
-import org.springframework.beans.factory.annotation.Autowired;\r
-import org.springframework.dao.DataAccessException;\r
-import org.springframework.dao.InvalidDataAccessApiUsageException;\r
-import org.springframework.security.core.Authentication;\r
-import org.springframework.security.core.context.SecurityContextHolder;\r
-import org.springframework.stereotype.Repository;\r
-import org.springframework.util.ReflectionUtils;\r
-\r
-import eu.etaxonomy.cdm.model.common.CdmBase;\r
-import eu.etaxonomy.cdm.model.common.User;\r
-import eu.etaxonomy.cdm.model.common.VersionableEntity;\r
-import eu.etaxonomy.cdm.persistence.dao.common.ICdmEntityDao;\r
-import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;\r
-import eu.etaxonomy.cdm.persistence.dto.MergeResult;\r
-import eu.etaxonomy.cdm.persistence.hibernate.PostMergeEntityListener;\r
-import eu.etaxonomy.cdm.persistence.hibernate.replace.ReferringObjectMetadata;\r
-import eu.etaxonomy.cdm.persistence.hibernate.replace.ReferringObjectMetadataFactory;\r
-import eu.etaxonomy.cdm.persistence.query.Grouping;\r
-import eu.etaxonomy.cdm.persistence.query.MatchMode;\r
-import eu.etaxonomy.cdm.persistence.query.OrderHint;\r
-\r
-\r
-/**\r
- * @author a.mueller\r
- * FIXME CdmEntityDaoBase is abstract, can it be annotated with @Repository?\r
- */\r
-@Repository\r
-public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implements ICdmEntityDao<T> {\r
- private static final Logger logger = Logger.getLogger(CdmEntityDaoBase.class);\r
-\r
- protected int flushAfterNo = 1000; //large numbers may cause synchronisation errors when commiting the session !!\r
-\r
- protected Class<T> type;\r
-\r
-// protected Version version = Configuration.luceneVersion;\r
-\r
- @Autowired\r
-// @Qualifier("defaultBeanInitializer")\r
- protected IBeanInitializer defaultBeanInitializer;\r
-\r
- public void setDefaultBeanInitializer(IBeanInitializer defaultBeanInitializer) {\r
- this.defaultBeanInitializer = defaultBeanInitializer;\r
- }\r
-\r
- @Autowired\r
- private ReferringObjectMetadataFactory referringObjectMetadataFactory;\r
-\r
-\r
- public CdmEntityDaoBase(Class<T> type){\r
- this.type = type;\r
- logger.debug("Creating DAO of type [" + type.getSimpleName() + "]");\r
- }\r
-\r
- @Override\r
- public void lock(T t, LockOptions lockOptions) {\r
- getSession().buildLockRequest(lockOptions).lock(t);\r
- }\r
-\r
- @Override\r
- public void refresh(T t, LockOptions lockOptions, List<String> propertyPaths) {\r
- getSession().refresh(t, lockOptions);\r
- defaultBeanInitializer.initialize(t, propertyPaths);\r
- }\r
-\r
- //TODO this method should be moved to a concrete class (not typed)\r
- public UUID saveCdmObj(CdmBase cdmObj) throws DataAccessException {\r
- getSession().saveOrUpdate(cdmObj);\r
- return cdmObj.getUuid();\r
- }\r
-\r
- //TODO: Replace saveCdmObj() by saveCdmObject_\r
- private UUID saveCdmObject_(T cdmObj){\r
- getSession().saveOrUpdate(cdmObj);\r
- return cdmObj.getUuid();\r
- }\r
-\r
- //TODO: Use everywhere CdmEntityDaoBase.saveAll() instead of ServiceBase.saveCdmObjectAll()?\r
- //TODO: why does this use saveCdmObject_ which actually savesOrUpdateds data ?\r
- @Override\r
- public Map<UUID, T> saveAll(Collection<T> cdmObjCollection){\r
- int types = cdmObjCollection.getClass().getTypeParameters().length;\r
- if (types > 0){\r
- if (logger.isDebugEnabled()){logger.debug("ClassType: + " + cdmObjCollection.getClass().getTypeParameters()[0]);}\r
- }\r
-\r
- Map<UUID, T> resultMap = new HashMap<UUID, T>();\r
- Iterator<T> iterator = cdmObjCollection.iterator();\r
- int i = 0;\r
- while(iterator.hasNext()){\r
- if ( ( (i % 2000) == 0) && (i > 0) ){logger.debug("Saved " + i + " objects" );}\r
- T cdmObj = iterator.next();\r
- UUID uuid = saveCdmObject_(cdmObj);\r
- if (logger.isDebugEnabled()){logger.debug("Save cdmObj: " + (cdmObj == null? null: cdmObj.toString()));}\r
- resultMap.put(uuid, cdmObj);\r
- i++;\r
- if ( (i % flushAfterNo) == 0){\r
- try{\r
- if (logger.isDebugEnabled()){logger.debug("flush");}\r
- flush();\r
- }catch(Exception e){\r
- logger.error("An exception occurred when trying to flush data");\r
- e.printStackTrace();\r
- throw new RuntimeException(e);\r
- }\r
- }\r
- }\r
-\r
- if ( logger.isInfoEnabled() ){logger.info("Saved " + i + " objects" );}\r
- return resultMap;\r
- }\r
-\r
- private UUID saveOrUpdateCdmObject(T cdmObj){\r
- getSession().saveOrUpdate(cdmObj);\r
- return cdmObj.getUuid();\r
- }\r
-\r
- @Override\r
- public Map<UUID, T> saveOrUpdateAll(Collection<T> cdmObjCollection){\r
- int types = cdmObjCollection.getClass().getTypeParameters().length;\r
- if (types > 0){\r
- if (logger.isDebugEnabled()){logger.debug("ClassType: + " + cdmObjCollection.getClass().getTypeParameters()[0]);}\r
- }\r
-\r
- Map<UUID, T> resultMap = new HashMap<UUID, T>();\r
- Iterator<T> iterator = cdmObjCollection.iterator();\r
- int i = 0;\r
- while(iterator.hasNext()){\r
- if ( ( (i % 2000) == 0) && (i > 0) ){logger.debug("Saved " + i + " objects" );}\r
- T cdmObj = iterator.next();\r
- UUID uuid = saveOrUpdateCdmObject(cdmObj);\r
- if (logger.isDebugEnabled()){logger.debug("Save cdmObj: " + (cdmObj == null? null: cdmObj.toString()));}\r
- resultMap.put(uuid, cdmObj);\r
- i++;\r
- if ( (i % flushAfterNo) == 0){\r
- try{\r
- if (logger.isDebugEnabled()){logger.debug("flush");}\r
- flush();\r
- }catch(Exception e){\r
- logger.error("An exception occurred when trying to flush data");\r
- e.printStackTrace();\r
- throw new RuntimeException(e);\r
- }\r
- }\r
- }\r
-\r
- if ( logger.isInfoEnabled() ){logger.info("Saved " + i + " objects" );}\r
- return resultMap;\r
- }\r
-\r
-\r
-\r
-\r
- @Override\r
- public T replace(T x, T y) {\r
- if(x.equals(y)) {\r
- return y;\r
- }\r
-\r
- Class<?> commonClass = x.getClass();\r
- if(y != null) {\r
- while(!commonClass.isAssignableFrom(y.getClass())) {\r
- if(commonClass.equals(type)) {\r
- throw new RuntimeException();\r
- }\r
- commonClass = commonClass.getSuperclass();\r
- }\r
- }\r
-\r
- getSession().merge(x);\r
-\r
- Set<ReferringObjectMetadata> referringObjectMetas = referringObjectMetadataFactory.get(x.getClass());\r
-\r
- for(ReferringObjectMetadata referringObjectMetadata : referringObjectMetas) {\r
-\r
- List<CdmBase> referringObjects = referringObjectMetadata.getReferringObjects(x, getSession());\r
-\r
- for(CdmBase referringObject : referringObjects) {\r
- try {\r
- referringObjectMetadata.replace(referringObject,x,y);\r
- getSession().update(referringObject);\r
-\r
- } catch (IllegalArgumentException e) {\r
- throw new RuntimeException(e.getMessage(),e);\r
- } catch (IllegalAccessException e) {\r
- throw new RuntimeException(e.getMessage(),e);\r
- }\r
- }\r
- }\r
- return y;\r
- }\r
-\r
- @Override\r
- public Session getSession() throws DataAccessException {\r
- return super.getSession();\r
- }\r
-\r
- @Override\r
- public void clear() throws DataAccessException {\r
- Session session = getSession();\r
- session.clear();\r
- if (logger.isDebugEnabled()){logger.debug("dao clear end");}\r
- }\r
-\r
- @Override\r
- public MergeResult<T> merge(T transientObject, boolean returnTransientEntity) throws DataAccessException {\r
- Session session = getSession();\r
- PostMergeEntityListener.addSession(session);\r
- MergeResult<T> result = null;\r
- try {\r
- @SuppressWarnings("unchecked")\r
- T persistentObject = (T)session.merge(transientObject);\r
- if (logger.isDebugEnabled()){logger.debug("dao merge end");}\r
-\r
- if(returnTransientEntity) {\r
- if(transientObject != null && persistentObject != null) {\r
- transientObject.setId(persistentObject.getId());\r
- }\r
- result = new MergeResult(transientObject, PostMergeEntityListener.getNewEntities(session));\r
- } else {\r
- result = new MergeResult(persistentObject, null);\r
- }\r
- return result;\r
- } finally {\r
- PostMergeEntityListener.removeSession(session);\r
- }\r
- }\r
-\r
- @Override\r
- public T merge(T transientObject) throws DataAccessException {\r
- Session session = getSession();\r
- @SuppressWarnings("unchecked")\r
- T persistentObject = (T)session.merge(transientObject);\r
- if (logger.isDebugEnabled()){logger.debug("dao merge end");}\r
- return persistentObject;\r
- }\r
-\r
- @Override\r
- public UUID saveOrUpdate(T transientObject) throws DataAccessException {\r
- if (transientObject == null){\r
- logger.warn("Object to save should not be null. NOP");\r
- return null;\r
- }\r
- try {\r
- if (logger.isDebugEnabled()){logger.debug("dao saveOrUpdate start...");}\r
- if (logger.isDebugEnabled()){logger.debug("transientObject(" + transientObject.getClass().getSimpleName() + ") ID:" + transientObject.getId() + ", UUID: " + transientObject.getUuid()) ;}\r
- Session session = getSession();\r
- if(transientObject.getId() != 0 && VersionableEntity.class.isAssignableFrom(transientObject.getClass())) {\r
- VersionableEntity versionableEntity = (VersionableEntity)transientObject;\r
- versionableEntity.setUpdated(new DateTime());\r
- Authentication authentication = SecurityContextHolder.getContext().getAuthentication();\r
- if(authentication != null && authentication.getPrincipal() != null && authentication.getPrincipal() instanceof User) {\r
- User user = (User)authentication.getPrincipal();\r
- versionableEntity.setUpdatedBy(user);\r
- }\r
- }\r
- session.saveOrUpdate(transientObject);\r
- if (logger.isDebugEnabled()){logger.debug("dao saveOrUpdate end");}\r
- return transientObject.getUuid();\r
- } catch (NonUniqueObjectException e) {\r
- logger.error("Error in CdmEntityDaoBase.saveOrUpdate(obj)");\r
- logger.error(e.getIdentifier());\r
- logger.error(e.getEntityName());\r
- logger.error(e.getMessage());\r
-\r
- e.printStackTrace();\r
- throw e;\r
- } catch (HibernateException e) {\r
-\r
- e.printStackTrace();\r
- throw e;\r
- }\r
- }\r
-\r
- @Override\r
- public T save(T newInstance) throws DataAccessException {\r
- if (newInstance == null){\r
- logger.warn("Object to save should not be null. NOP");\r
- return null;\r
- }\r
- getSession().save(newInstance);\r
- return newInstance;\r
- }\r
-\r
- @Override\r
- public UUID update(T transientObject) throws DataAccessException {\r
- if (transientObject == null){\r
- logger.warn("Object to update should not be null. NOP");\r
- return null;\r
- }\r
- getSession().update(transientObject);\r
- return transientObject.getUuid();\r
- }\r
-\r
- @Override\r
- public UUID refresh(T persistentObject) throws DataAccessException {\r
- getSession().refresh(persistentObject);\r
- return persistentObject.getUuid();\r
- }\r
-\r
- @Override\r
- public UUID delete(T persistentObject) throws DataAccessException {\r
- if (persistentObject == null){\r
- logger.warn(type.getName() + " was 'null'");\r
- return null;\r
- }\r
-\r
- // Merge the object in if it is detached\r
- //\r
- // I think this is preferable to catching lazy initialization errors\r
- // as that solution only swallows and hides the exception, but doesn't\r
- // actually solve it.\r
- persistentObject = (T) getSession().merge(persistentObject);\r
- getSession().delete(persistentObject);\r
- return persistentObject.getUuid();\r
- }\r
-\r
- @Override\r
- public T findById(int id) throws DataAccessException {\r
- return getSession().get(type, id);\r
- }\r
-\r
-\r
- @Override\r
- public T findByUuid(UUID uuid) throws DataAccessException{\r
- Session session = getSession();\r
- Criteria crit = session.createCriteria(type);\r
- crit.add(Restrictions.eq("uuid", uuid));\r
- crit.addOrder(Order.desc("created"));\r
- @SuppressWarnings("unchecked")\r
- List<T> results = crit.list();\r
- Set<T> resultSet = new HashSet<>();\r
- resultSet.addAll(results);\r
- if (resultSet.isEmpty()){\r
- return null;\r
- }else{\r
- if(resultSet.size() > 1){\r
- logger.error("findByUuid() delivers more than one result for UUID: " + uuid);\r
- }\r
- return results.get(0);\r
- }\r
- }\r
-\r
- @Override\r
- public T findByUuidWithoutFlush(UUID uuid) throws DataAccessException{\r
- Session session = getSession();\r
- FlushMode currentFlushMode = session.getFlushMode();\r
- try {\r
- // set flush mode to manual so that the session does not flush\r
- // when before performing the query\r
- session.setFlushMode(FlushMode.MANUAL);\r
- Criteria crit = session.createCriteria(type);\r
- crit.add(Restrictions.eq("uuid", uuid));\r
- crit.addOrder(Order.desc("created"));\r
- @SuppressWarnings("unchecked")\r
- List<T> results = crit.list();\r
- if (results.isEmpty()){\r
- return null;\r
- }else{\r
- if(results.size() > 1){\r
- logger.error("findByUuid() delivers more than one result for UUID: " + uuid);\r
- }\r
- return results.get(0);\r
- }\r
- } finally {\r
- // set back the session flush mode\r
- if(currentFlushMode != null) {\r
- session.setFlushMode(currentFlushMode);\r
- }\r
- }\r
- }\r
-\r
- @Override\r
- public List<T> loadList(Collection<Integer> ids, List<String> propertyPaths) throws DataAccessException {\r
-\r
- if (ids.isEmpty()) {\r
- return new ArrayList<T>(0);\r
- }\r
-\r
- Criteria criteria = prepareList(ids, null, null, null, "id");\r
-\r
- if (logger.isDebugEnabled()){logger.debug(criteria.toString());}\r
-\r
- @SuppressWarnings("unchecked")\r
- List<T> result = criteria.list();\r
- defaultBeanInitializer.initializeAll(result, propertyPaths);\r
- return result;\r
- }\r
-\r
-\r
- @Override\r
- public List<T> list(Collection<UUID> uuids, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws DataAccessException {\r
-\r
- if (uuids == null || uuids.isEmpty()){\r
- return new ArrayList<>();\r
- }\r
-\r
- Criteria criteria = prepareList(uuids, pageSize, pageNumber, orderHints, "uuid");\r
-\r
- @SuppressWarnings("unchecked")\r
- List<T> result = criteria.list();\r
- defaultBeanInitializer.initializeAll(result, propertyPaths);\r
- return result;\r
- }\r
-\r
- /**\r
- * @param uuids\r
- * @param pageSize\r
- * @param pageNumber\r
- * @param orderHints\r
- * @param propertyName\r
- * @return\r
- */\r
- private Criteria prepareList(Collection<?> uuids, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, String propertyName) {\r
- Criteria criteria = getSession().createCriteria(type);\r
- criteria.add(Restrictions.in(propertyName, uuids));\r
-\r
- if(pageSize != null) {\r
- criteria.setMaxResults(pageSize);\r
- if(pageNumber != null) {\r
- criteria.setFirstResult(pageNumber * pageSize);\r
- } else {\r
- criteria.setFirstResult(0);\r
- }\r
- }\r
-\r
- if(orderHints == null) {\r
- orderHints = OrderHint.defaultOrderHintsFor(type);\r
- }\r
- addOrder(criteria, orderHints);\r
- return criteria;\r
- }\r
-\r
-\r
- protected List<T> findByParam(Class<? extends T> clazz, String param, String queryString, MatchMode matchmode, List<Criterion> criterion, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
- Criteria criteria = null;\r
-\r
- if(clazz == null) {\r
- criteria = getSession().createCriteria(type);\r
- } else {\r
- criteria = getSession().createCriteria(clazz);\r
- }\r
-\r
- if (queryString != null) {\r
- if(matchmode == null) {\r
- criteria.add(Restrictions.ilike(param, queryString));\r
- } else if(matchmode == MatchMode.BEGINNING) {\r
- criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.START));\r
- } else if(matchmode == MatchMode.END) {\r
- criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.END));\r
- } else if(matchmode == MatchMode.EXACT) {\r
- criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.EXACT));\r
- } else {\r
- criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.ANYWHERE));\r
- }\r
- }\r
-\r
- addCriteria(criteria, criterion);\r
-\r
- if(pageSize != null) {\r
- criteria.setMaxResults(pageSize);\r
- if(pageNumber != null) {\r
- criteria.setFirstResult(pageNumber * pageSize);\r
- } else {\r
- criteria.setFirstResult(0);\r
- }\r
- }\r
-\r
- addOrder(criteria, orderHints);\r
-\r
- @SuppressWarnings("unchecked")\r
- List<T> result = criteria.list();\r
- defaultBeanInitializer.initializeAll(result, propertyPaths);\r
- return result;\r
- }\r
-\r
- @Override\r
- public T load(UUID uuid) {\r
- T bean = findByUuid(uuid);\r
- if(bean == null){\r
- return null;\r
- }\r
- defaultBeanInitializer.load(bean);\r
-\r
- return bean;\r
- }\r
-\r
- @Override\r
- public T load(int id, List<String> propertyPaths){\r
- T bean = findById(id);\r
- if(bean == null){\r
- return bean;\r
- }\r
- defaultBeanInitializer.initialize(bean, propertyPaths);\r
-\r
- return bean;\r
- }\r
-\r
- @Override\r
- public T load(UUID uuid, List<String> propertyPaths){\r
- T bean = findByUuid(uuid);\r
- if(bean == null){\r
- return bean;\r
- }\r
- defaultBeanInitializer.initialize(bean, propertyPaths);\r
-\r
- return bean;\r
- }\r
-\r
- @Override\r
- public Boolean exists(UUID uuid) {\r
- if (findByUuid(uuid)==null){\r
- return false;\r
- }\r
- return true;\r
- }\r
-\r
- @Override\r
- public int count() {\r
- return count(type);\r
- }\r
-\r
- @Override\r
- public int count(Class<? extends T> clazz) {\r
- Session session = getSession();\r
- Criteria criteria = null;\r
- if(clazz == null) {\r
- criteria = session.createCriteria(type);\r
- } else {\r
- criteria = session.createCriteria(clazz);\r
- }\r
- criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));\r
-\r
- //since hibernate 4 (or so) uniqueResult returns Long, not Integer, therefore needs\r
- //to be casted. Think about returning long rather then int!\r
- return ((Number) criteria.uniqueResult()).intValue();\r
- }\r
-\r
- @Override\r
- public List<T> list(Integer limit, Integer start) {\r
- return list(limit, start, null);\r
- }\r
-\r
- @Override\r
- public List<Object[]> group(Class<? extends T> clazz,Integer limit, Integer start, List<Grouping> groups, List<String> propertyPaths) {\r
-\r
- Criteria criteria = null;\r
- if(clazz == null){\r
- criteria = getSession().createCriteria(type);\r
- } else {\r
- criteria = getSession().createCriteria(clazz);\r
- }\r
-\r
- addGroups(criteria,groups);\r
-\r
- if(limit != null) {\r
- criteria.setFirstResult(start);\r
- criteria.setMaxResults(limit);\r
- }\r
-\r
- @SuppressWarnings("unchecked")\r
- List<Object[]> result = criteria.list();\r
-\r
- if(propertyPaths != null && !propertyPaths.isEmpty()) {\r
- for(Object[] objects : result) {\r
- defaultBeanInitializer.initialize(objects[0], propertyPaths);\r
- }\r
- }\r
-\r
- return result;\r
- }\r
-\r
- protected void countGroups(DetachedCriteria criteria,List<Grouping> groups) {\r
- if(groups != null){\r
-\r
-\r
- Map<String,String> aliases = new HashMap<String,String>();\r
-\r
- for(Grouping grouping : groups) {\r
- if(grouping.getAssociatedObj() != null) {\r
- String alias = null;\r
- if((alias = aliases.get(grouping.getAssociatedObj())) == null) {\r
- alias = grouping.getAssociatedObjectAlias();\r
- aliases.put(grouping.getAssociatedObj(), alias);\r
- criteria.createAlias(grouping.getAssociatedObj(),alias);\r
- }\r
- }\r
- }\r
-\r
- ProjectionList projectionList = Projections.projectionList();\r
-\r
- for(Grouping grouping : groups) {\r
- grouping.addProjection(projectionList);\r
- }\r
- criteria.setProjection(projectionList);\r
- }\r
- }\r
-\r
- protected void addGroups(Criteria criteria,List<Grouping> groups) {\r
- if(groups != null){\r
-\r
-\r
- Map<String,String> aliases = new HashMap<String,String>();\r
-\r
- for(Grouping grouping : groups) {\r
- if(grouping.getAssociatedObj() != null) {\r
- String alias = null;\r
- if((alias = aliases.get(grouping.getAssociatedObj())) == null) {\r
- alias = grouping.getAssociatedObjectAlias();\r
- aliases.put(grouping.getAssociatedObj(), alias);\r
- criteria.createAlias(grouping.getAssociatedObj(),alias);\r
- }\r
- }\r
- }\r
-\r
- ProjectionList projectionList = Projections.projectionList();\r
-\r
- for(Grouping grouping : groups) {\r
- grouping.addProjection(projectionList);\r
- }\r
- criteria.setProjection(projectionList);\r
-\r
- for(Grouping grouping : groups) {\r
- grouping.addOrder(criteria);\r
-\r
- }\r
- }\r
- }\r
-\r
- protected void addCriteria(Criteria criteria, List<Criterion> criterion) {\r
- if(criterion != null) {\r
- for(Criterion c : criterion) {\r
- criteria.add(c);\r
- }\r
- }\r
-\r
- }\r
-\r
- protected void addOrder(FullTextQuery fullTextQuery, List<OrderHint> orderHints) {\r
- //FIXME preliminary hardcoded type:\r
- SortField.Type type = SortField.Type.STRING;\r
-\r
- if(orderHints != null && !orderHints.isEmpty()) {\r
- org.apache.lucene.search.Sort sort = new Sort();\r
- SortField[] sortFields = new SortField[orderHints.size()];\r
- for(int i = 0; i < orderHints.size(); i++) {\r
- OrderHint orderHint = orderHints.get(i);\r
- switch(orderHint.getSortOrder()) {\r
- case ASCENDING:\r
- sortFields[i] = new SortField(orderHint.getPropertyName(), type, true);\r
- break;\r
- case DESCENDING:\r
- default:\r
- sortFields[i] = new SortField(orderHint.getPropertyName(), type, false);\r
-\r
- }\r
- }\r
- sort.setSort(sortFields);\r
- fullTextQuery.setSort(sort);\r
-\r
- }\r
- }\r
-\r
- @Override\r
- public List<T> list(Integer limit, Integer start, List<OrderHint> orderHints) {\r
- return list(limit,start,orderHints,null);\r
- }\r
-\r
- @Override\r
- public List<T> list(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {\r
- Criteria criteria = getSession().createCriteria(type);\r
- if(limit != null) {\r
- criteria.setFirstResult(start);\r
- criteria.setMaxResults(limit);\r
- }\r
-\r
- addOrder(criteria,orderHints);\r
- @SuppressWarnings("unchecked")\r
- List<T> results = criteria.list();\r
-\r
- defaultBeanInitializer.initializeAll(results, propertyPaths);\r
- return results;\r
- }\r
-\r
- @Override\r
- public <S extends T> List<S> list(Class<S> clazz, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {\r
- Criteria criteria = null;\r
- if(clazz == null) {\r
- criteria = getSession().createCriteria(type);\r
- } else {\r
- criteria = getSession().createCriteria(clazz);\r
- }\r
-\r
- if(limit != null) {\r
- if(start != null) {\r
- criteria.setFirstResult(start);\r
- } else {\r
- criteria.setFirstResult(0);\r
- }\r
- criteria.setMaxResults(limit);\r
- }\r
-\r
- addOrder(criteria,orderHints);\r
-\r
- @SuppressWarnings("unchecked")\r
- List<S> results = criteria.list();\r
-\r
- defaultBeanInitializer.initializeAll(results, propertyPaths);\r
- return results;\r
- }\r
-\r
-\r
- public <S extends T> List<S> list(Class<S> type, Integer limit, Integer start, List<OrderHint> orderHints) {\r
- return list(type,limit,start,orderHints,null);\r
- }\r
-\r
- @Override\r
- public <S extends T> List<S> list(Class<S> type, Integer limit, Integer start) {\r
- return list(type,limit,start,null,null);\r
- }\r
-\r
- @Override\r
- public List<T> rows(String tableName, int limit, int start) {\r
- Query query = getSession().createQuery("from " + tableName + " order by uuid");\r
- query.setFirstResult(start);\r
- query.setMaxResults(limit);\r
- @SuppressWarnings("unchecked")\r
- List<T> result = query.list();\r
- return result;\r
- }\r
-\r
- @Override\r
- public Class<T> getType() {\r
- return type;\r
- }\r
-\r
- protected void setPagingParameter(Query query, Integer pageSize, Integer pageNumber){\r
- if(pageSize != null) {\r
- query.setMaxResults(pageSize);\r
- if(pageNumber != null) {\r
- query.setFirstResult(pageNumber * pageSize);\r
- } else {\r
- query.setFirstResult(0);\r
- }\r
- }\r
- }\r
-\r
- protected void setPagingParameter(AuditQuery query, Integer pageSize, Integer pageNumber){\r
- if(pageSize != null) {\r
- query.setMaxResults(pageSize);\r
- if(pageNumber != null) {\r
- query.setFirstResult(pageNumber * pageSize);\r
- } else {\r
- query.setFirstResult(0);\r
- }\r
- }\r
- }\r
-\r
- @Override\r
- public int count(T example, Set<String> includeProperties) {\r
- Criteria criteria = getSession().createCriteria(example.getClass());\r
- addExample(criteria,example,includeProperties);\r
-\r
- criteria.setProjection(Projections.rowCount());\r
- return ((Number)criteria.uniqueResult()).intValue();\r
- }\r
-\r
- protected void addExample(Criteria criteria, T example, Set<String> includeProperties) {\r
- if(includeProperties != null && !includeProperties.isEmpty()) {\r
- criteria.add(Example.create(example).setPropertySelector(new PropertySelectorImpl(includeProperties)));\r
- ClassMetadata classMetadata = getSession().getSessionFactory().getClassMetadata(example.getClass());\r
- for(String property : includeProperties) {\r
- Type type = classMetadata.getPropertyType(property);\r
- if(type.isEntityType()) {\r
- try {\r
- Field field = ReflectionUtils.findField(example.getClass(), property);\r
- field.setAccessible(true);\r
- Object value = field.get(example);\r
- if(value != null) {\r
- criteria.add(Restrictions.eq(property,value));\r
- } else {\r
- criteria.add(Restrictions.isNull(property));\r
- }\r
- } catch (SecurityException se) {\r
- throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property, se);\r
- } catch (HibernateException he) {\r
- throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property, he);\r
- } catch (IllegalArgumentException iae) {\r
- throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property, iae);\r
- } catch (IllegalAccessException ie) {\r
- throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property, ie);\r
- }\r
-\r
- }\r
- }\r
- } else {\r
- criteria.add(Example.create(example));\r
- }\r
- }\r
-\r
-\r
- protected long countByParam(Class<? extends T> clazz, String param, String queryString, MatchMode matchmode, List<Criterion> criterion) {\r
- Criteria criteria = null;\r
-\r
- if(clazz == null) {\r
- criteria = getSession().createCriteria(type);\r
- } else {\r
- criteria = getSession().createCriteria(clazz);\r
- }\r
-\r
- if (queryString != null) {\r
- if(matchmode == null) {\r
- criteria.add(Restrictions.ilike(param, queryString));\r
- } else if(matchmode == MatchMode.BEGINNING) {\r
- criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.START));\r
- } else if(matchmode == MatchMode.END) {\r
- criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.END));\r
- } else if(matchmode == MatchMode.EXACT) {\r
- criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.EXACT));\r
- } else {\r
- criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.ANYWHERE));\r
- }\r
- }\r
-\r
- addCriteria(criteria, criterion);\r
-\r
- criteria.setProjection(Projections.rowCount());\r
-\r
- return ((Number)criteria.uniqueResult()).longValue();\r
- }\r
-\r
-\r
- @Override\r
- public List<T> list(T example, Set<String> includeProperties, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {\r
- Criteria criteria = getSession().createCriteria(example.getClass());\r
- addExample(criteria,example,includeProperties);\r
-\r
- if(limit != null) {\r
- if(start != null) {\r
- criteria.setFirstResult(start);\r
- } else {\r
- criteria.setFirstResult(0);\r
- }\r
- criteria.setMaxResults(limit);\r
- }\r
-\r
- addOrder(criteria,orderHints);\r
-\r
- @SuppressWarnings("unchecked")\r
- List<T> results = criteria.list();\r
- defaultBeanInitializer.initializeAll(results, propertyPaths);\r
- return results;\r
- }\r
-\r
- private class PropertySelectorImpl implements PropertySelector {\r
-\r
- private final Set<String> includeProperties;\r
- /**\r
- *\r
- */\r
- private static final long serialVersionUID = -3175311800911570546L;\r
-\r
- public PropertySelectorImpl(Set<String> includeProperties) {\r
- this.includeProperties = includeProperties;\r
- }\r
-\r
- @Override\r
- public boolean include(Object propertyValue, String propertyName, Type type) {\r
- if(includeProperties.contains(propertyName)) {\r
- return true;\r
- } else {\r
- return false;\r
- }\r
- }\r
-\r
- }\r
-}\r
-\r
+/**
+* Copyright (C) 2007 EDIT
+* European Distributed Institute of Taxonomy
+* http://www.e-taxonomy.eu
+*
+* The contents of this file are subject to the Mozilla Public License Version 1.1
+* See LICENSE.TXT at the top of this package for the full license terms.
+*/
+
+package eu.etaxonomy.cdm.persistence.dao.hibernate.common;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.hibernate.Criteria;
+import org.hibernate.FlushMode;
+import org.hibernate.HibernateException;
+import org.hibernate.LockOptions;
+import org.hibernate.NonUniqueObjectException;
+import org.hibernate.Session;
+import org.hibernate.criterion.Criterion;
+import org.hibernate.criterion.DetachedCriteria;
+import org.hibernate.criterion.Disjunction;
+import org.hibernate.criterion.Example;
+import org.hibernate.criterion.Example.PropertySelector;
+import org.hibernate.criterion.LogicalExpression;
+import org.hibernate.criterion.Order;
+import org.hibernate.criterion.ProjectionList;
+import org.hibernate.criterion.Projections;
+import org.hibernate.criterion.Restrictions;
+import org.hibernate.criterion.Subqueries;
+import org.hibernate.envers.AuditReader;
+import org.hibernate.envers.AuditReaderFactory;
+import org.hibernate.envers.query.AuditQuery;
+import org.hibernate.metadata.ClassMetadata;
+import org.hibernate.sql.JoinType;
+import org.hibernate.type.Type;
+import org.joda.time.DateTime;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
+import org.springframework.dao.InvalidDataAccessApiUsageException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.util.ReflectionUtils;
+
+import eu.etaxonomy.cdm.model.common.CdmBase;
+import eu.etaxonomy.cdm.model.common.IPublishable;
+import eu.etaxonomy.cdm.model.common.VersionableEntity;
+import eu.etaxonomy.cdm.model.permission.User;
+import eu.etaxonomy.cdm.model.view.AuditEvent;
+import eu.etaxonomy.cdm.persistence.dao.common.ICdmEntityDao;
+import eu.etaxonomy.cdm.persistence.dao.common.Restriction;
+import eu.etaxonomy.cdm.persistence.dao.common.Restriction.Operator;
+import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
+import eu.etaxonomy.cdm.persistence.dto.MergeResult;
+import eu.etaxonomy.cdm.persistence.hibernate.PostMergeEntityListener;
+import eu.etaxonomy.cdm.persistence.hibernate.replace.ReferringObjectMetadata;
+import eu.etaxonomy.cdm.persistence.hibernate.replace.ReferringObjectMetadataFactory;
+import eu.etaxonomy.cdm.persistence.query.Grouping;
+import eu.etaxonomy.cdm.persistence.query.MatchMode;
+import eu.etaxonomy.cdm.persistence.query.OrderHint;
+
+/**
+ * Hibernate implementation for {@link ICdmEntityDao}.
+ */
+public abstract class CdmEntityDaoBase<T extends CdmBase>
+ extends DaoBase
+ implements ICdmEntityDao<T> {
+
+ private static final Logger logger = LogManager.getLogger(CdmEntityDaoBase.class);
+
+ protected int flushAfterNo = 1000; // large numbers may cause synchronisation errors
+ // when commiting the session
+ protected Class<T> type;
+
+ @Autowired
+ // @Qualifier("defaultBeanInitializer")
+ protected IBeanInitializer defaultBeanInitializer;
+
+ public void setDefaultBeanInitializer(IBeanInitializer defaultBeanInitializer) {
+ this.defaultBeanInitializer = defaultBeanInitializer;
+ }
+
+ @Autowired
+ private ReferringObjectMetadataFactory referringObjectMetadataFactory;
+
+ protected static final EnumSet<Operator> LEFTOUTER_OPS = EnumSet.of(Operator.AND_NOT, Operator.OR, Operator.OR_NOT);
+
+ public CdmEntityDaoBase(Class<T> type) {
+ this.type = type;
+ assert type != null;
+ logger.debug("Creating DAO of type [" + type.getSimpleName() + "]");
+ }
+
+ @Override
+ public void lock(T t, LockOptions lockOptions) {
+ getSession().buildLockRequest(lockOptions).lock(t);
+ }
+
+ @Override
+ public void refresh(T t, LockOptions lockOptions, List<String> propertyPaths) {
+ getSession().refresh(t, lockOptions);
+ defaultBeanInitializer.initialize(t, propertyPaths);
+ }
+
+ // TODO this method should be moved to a concrete class (not typed)
+ public UUID saveCdmObj(CdmBase cdmObj) throws DataAccessException {
+ getSession().saveOrUpdate(cdmObj);
+ return cdmObj.getUuid();
+ }
+
+ // TODO: Replace saveCdmObj() by saveCdmObject_
+ private UUID saveCdmObject_(T cdmObj) {
+ getSession().saveOrUpdate(cdmObj);
+ return cdmObj.getUuid();
+ }
+
+ // TODO: Use everywhere CdmEntityDaoBase.saveAll() instead of
+ // ServiceBase.saveCdmObjectAll()?
+ // TODO: why does this use saveCdmObject_ which actually savesOrUpdateds
+ // data ?
+ @Override
+ public Map<UUID, T> saveAll(Collection<? extends T> cdmObjCollection) {
+ int types = cdmObjCollection.getClass().getTypeParameters().length;
+ if (types > 0) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("ClassType: + " + cdmObjCollection.getClass().getTypeParameters()[0]);
+ }
+ }
+
+ Map<UUID, T> resultMap = new HashMap<>();
+ Iterator<? extends T> iterator = cdmObjCollection.iterator();
+ int i = 0;
+ while (iterator.hasNext()) {
+ if (((i % 2000) == 0) && (i > 0)) {
+ logger.debug("Saved " + i + " objects");
+ }
+ T cdmObj = iterator.next();
+ UUID uuid = saveCdmObject_(cdmObj);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Save cdmObj: " + (cdmObj == null ? null : cdmObj.toString()));
+ }
+ resultMap.put(uuid, cdmObj);
+ i++;
+ if ((i % flushAfterNo) == 0) {
+ try {
+ if (logger.isDebugEnabled()) {
+ logger.debug("flush");
+ }
+ flush();
+ } catch (Exception e) {
+ logger.error("An exception occurred when trying to flush data");
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ if (logger.isInfoEnabled()) {
+ logger.info("Saved " + i + " objects");
+ }
+ return resultMap;
+ }
+
+ private UUID saveOrUpdateCdmObject(T cdmObj) {
+ getSession().saveOrUpdate(cdmObj);
+ return cdmObj.getUuid();
+ }
+
+ @Override
+ public Map<UUID, T> saveOrUpdateAll(Collection<T> cdmObjCollection) {
+ int types = cdmObjCollection.getClass().getTypeParameters().length;
+ if (types > 0) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("ClassType: + " + cdmObjCollection.getClass().getTypeParameters()[0]);
+ }
+ }
+
+ Map<UUID, T> resultMap = new HashMap<>();
+ Iterator<T> iterator = cdmObjCollection.iterator();
+ int i = 0;
+ while (iterator.hasNext()) {
+ if (((i % 2000) == 0) && (i > 0)) {
+ logger.debug("Saved " + i + " objects");
+ }
+ T cdmObj = iterator.next();
+ UUID uuid = saveOrUpdateCdmObject(cdmObj);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Save cdmObj: " + (cdmObj == null ? null : cdmObj.toString()));
+ }
+ resultMap.put(uuid, cdmObj);
+ i++;
+ if ((i % flushAfterNo) == 0) {
+ try {
+ if (logger.isDebugEnabled()) {
+ logger.debug("flush");
+ }
+ flush();
+ } catch (Exception e) {
+ logger.error("An exception occurred when trying to flush data");
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ if (logger.isInfoEnabled()) {
+ logger.info("Saved " + i + " objects");
+ }
+ return resultMap;
+ }
+
+ @Override
+ public T replace(T x, T y) {
+ if (x.equals(y)) {
+ return y;
+ }
+
+ Class<?> commonClass = x.getClass();
+ if (y != null) {
+ while (!commonClass.isAssignableFrom(y.getClass())) {
+ if (commonClass.equals(type)) {
+ throw new RuntimeException();
+ }
+ commonClass = commonClass.getSuperclass();
+ }
+ }
+
+ getSession().merge(x);
+
+ Set<ReferringObjectMetadata> referringObjectMetas = referringObjectMetadataFactory.get(x.getClass());
+
+ for (ReferringObjectMetadata referringObjectMetadata : referringObjectMetas) {
+
+ List<CdmBase> referringObjects = referringObjectMetadata.getReferringObjects(x, getSession());
+
+ for (CdmBase referringObject : referringObjects) {
+ try {
+ referringObjectMetadata.replace(referringObject, x, y);
+ getSession().update(referringObject);
+
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+ }
+ return y;
+ }
+
+ @Override
+ public Session getSession() throws DataAccessException {
+ return super.getSession();
+ }
+
+ @Override
+ public void clear() throws DataAccessException {
+ Session session = getSession();
+ session.clear();
+ if (logger.isDebugEnabled()) {
+ logger.debug("dao clear end");
+ }
+ }
+
+ @Override
+ public MergeResult<T> merge(T transientObject, boolean returnTransientEntity) throws DataAccessException {
+ Session session = getSession();
+ PostMergeEntityListener.addSession(session);
+ MergeResult<T> result = null;
+ try {
+ @SuppressWarnings("unchecked")
+ T persistentObject = (T) session.merge(transientObject);
+ if (logger.isDebugEnabled()) {
+ logger.debug("dao merge end");
+ }
+
+ if (returnTransientEntity) {
+ if (transientObject != null && persistentObject != null) {
+ transientObject.setId(persistentObject.getId());
+ }
+ result = new MergeResult(transientObject, PostMergeEntityListener.getNewEntities(session));
+ } else {
+ result = new MergeResult(persistentObject, null);
+ }
+ return result;
+ } finally {
+ PostMergeEntityListener.removeSession(session);
+ }
+ }
+
+ @Override
+ public T merge(T transientObject) throws DataAccessException {
+ Session session = getSession();
+ @SuppressWarnings("unchecked")
+ T persistentObject = (T) session.merge(transientObject);
+ if (logger.isDebugEnabled()) {
+ logger.debug("dao merge end");
+ }
+ return persistentObject;
+ }
+
+ @Override
+ public UUID saveOrUpdate(T transientObject) throws DataAccessException {
+ if (transientObject == null) {
+ logger.warn("Object to save should not be null. NOP");
+ return null;
+ }
+ try {
+ if (logger.isDebugEnabled()) {
+ logger.debug("dao saveOrUpdate start...");
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("transientObject(" + transientObject.getClass().getSimpleName() + ") ID:"
+ + transientObject.getId() + ", UUID: " + transientObject.getUuid());
+ }
+ Session session = getSession();
+ if (transientObject.getId() != 0 && VersionableEntity.class.isAssignableFrom(transientObject.getClass())) {
+ VersionableEntity versionableEntity = (VersionableEntity) transientObject;
+ versionableEntity.setUpdated(new DateTime());
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ if (authentication != null && authentication.getPrincipal() != null
+ && authentication.getPrincipal() instanceof User) {
+ User user = (User) authentication.getPrincipal();
+ versionableEntity.setUpdatedBy(user);
+ }
+ }
+ session.saveOrUpdate(transientObject);
+ if (logger.isDebugEnabled()) {
+ logger.debug("dao saveOrUpdate end");
+ }
+ return transientObject.getUuid();
+ } catch (NonUniqueObjectException e) {
+ logger.error("Error in CdmEntityDaoBase.saveOrUpdate(obj). ID=" + e.getIdentifier() + ". Class="
+ + e.getEntityName());
+ logger.error(e.getMessage());
+
+ e.printStackTrace();
+ throw e;
+ } catch (HibernateException e) {
+
+ e.printStackTrace();
+ throw e;
+ }
+ }
+
+ @Override
+ public <S extends T> S save(S newInstance) throws DataAccessException {
+ if (newInstance == null) {
+ logger.warn("Object to save should not be null. NOP");
+ return null;
+ }
+ getSession().save(newInstance);
+ return newInstance;
+ }
+
+ @Override
+ public UUID update(T transientObject) throws DataAccessException {
+ if (transientObject == null) {
+ logger.warn("Object to update should not be null. NOP");
+ return null;
+ }
+ getSession().update(transientObject);
+ return transientObject.getUuid();
+ }
+
+ @Override
+ public UUID refresh(T persistentObject) throws DataAccessException {
+ getSession().refresh(persistentObject);
+ return persistentObject.getUuid();
+ }
+
+ @Override
+ public UUID delete(T persistentObject) throws DataAccessException {
+ if (persistentObject == null) {
+ logger.warn(type.getName() + " was 'null'");
+ return null;
+ }
+
+ // Ben Clark:
+ // Merge the object in if it is detached
+ //
+ // I think this is preferable to catching lazy initialization errors
+ // as that solution only swallows and hides the exception, but doesn't
+ // actually solve it.
+ persistentObject = (T) getSession().merge(persistentObject);
+ getSession().delete(persistentObject);
+ return persistentObject.getUuid();
+ }
+
+ @Override
+ public T findById(int id) throws DataAccessException {
+ return getSession().get(type, id);
+ }
+
+ @Override
+ public T findByUuid(UUID uuid) throws DataAccessException {
+ return this.findByUuid(uuid, INCLUDE_UNPUBLISHED);
+ }
+
+ protected T findByUuid(UUID uuid, boolean includeUnpublished) throws DataAccessException {
+ Session session = getSession();
+ Criteria crit = session.createCriteria(type);
+ crit.add(Restrictions.eq("uuid", uuid));
+ crit.addOrder(Order.desc("created"));
+ if (IPublishable.class.isAssignableFrom(type) && !includeUnpublished) {
+ crit.add(Restrictions.eq("publish", Boolean.TRUE));
+ }
+
+ @SuppressWarnings("unchecked")
+ List<T> results = crit.list();
+ Set<T> resultSet = new HashSet<>();
+ resultSet.addAll(results);
+ if (resultSet.isEmpty()) {
+ return null;
+ } else {
+ if (resultSet.size() > 1) {
+ logger.error("findByUuid() delivers more than one result for UUID: " + uuid);
+ }
+ return results.get(0);
+ }
+ }
+
+ @Override
+ public T findByUuidWithoutFlush(UUID uuid) throws DataAccessException {
+ Session session = getSession();
+ FlushMode currentFlushMode = session.getHibernateFlushMode();
+ try {
+ // set flush mode to manual so that the session does not flush
+ // when before performing the query
+ session.setHibernateFlushMode(FlushMode.MANUAL);
+ Criteria crit = session.createCriteria(type);
+ crit.add(Restrictions.eq("uuid", uuid));
+ crit.addOrder(Order.desc("created"));
+ @SuppressWarnings("unchecked")
+ List<T> results = crit.list();
+ if (results.isEmpty()) {
+ return null;
+ } else {
+ if (results.size() > 1) {
+ logger.error("findByUuid() delivers more than one result for UUID: " + uuid);
+ }
+ return results.get(0);
+ }
+ } finally {
+ // set back the session flush mode
+ if (currentFlushMode != null) {
+ session.setHibernateFlushMode(currentFlushMode);
+ }
+ }
+ }
+
+ @Override
+ public List<T> loadList(Collection<Integer> ids, List<OrderHint> orderHints, List<String> propertyPaths) throws DataAccessException {
+
+ if (ids.isEmpty()) {
+ return new ArrayList<>(0);
+ }
+
+ Criteria criteria = prepareList(null, ids, null, null, orderHints, "id");
+
+ if (logger.isDebugEnabled()) {
+ logger.debug(criteria.toString());
+ }
+
+ @SuppressWarnings("unchecked")
+ List<T> result = criteria.list();
+ defaultBeanInitializer.initializeAll(result, propertyPaths);
+ return result;
+ }
+
+ @Override
+ public List<T> list(Collection<UUID> uuids, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
+ List<String> propertyPaths) throws DataAccessException {
+
+ if (uuids == null || uuids.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ Criteria criteria = prepareList(null, uuids, pageSize, pageNumber, orderHints, "uuid");
+ @SuppressWarnings("unchecked")
+ List<T> result = criteria.list();
+ defaultBeanInitializer.initializeAll(result, propertyPaths);
+ return result;
+ }
+
+ @Override
+ public <S extends T> List<S> list(Class<S> clazz, Collection<UUID> uuids, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
+ List<String> propertyPaths) throws DataAccessException {
+
+ if (uuids == null || uuids.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ Criteria criteria = prepareList(clazz, uuids, pageSize, pageNumber, orderHints, "uuid");
+ @SuppressWarnings("unchecked")
+ List<S> result = criteria.list();
+ defaultBeanInitializer.initializeAll(result, propertyPaths);
+ return result;
+ }
+
+ @Override
+ public <S extends T> List<S> list(Class<S> type, List<Restriction<?>> restrictions, Integer limit, Integer start,
+ List<OrderHint> orderHints, List<String> propertyPaths) {
+
+ Criteria criteria = createCriteria(type, restrictions, false);
+
+ addLimitAndStart(criteria, limit, start);
+ addOrder(criteria, orderHints);
+
+ @SuppressWarnings("unchecked")
+ List<S> result = criteria.list();
+ defaultBeanInitializer.initializeAll(result, propertyPaths);
+ return result;
+ }
+
+ /**
+ * @param restrictions
+ * @param criteria
+ */
+ private void addRestrictions(List<Restriction<?>> restrictions, DetachedCriteria criteria) {
+
+ if(restrictions == null || restrictions.isEmpty()){
+ return ;
+ }
+
+ List<CriterionWithOperator> perProperty = new ArrayList<>(restrictions.size());
+ Map<String, String> aliases = new HashMap<>();
+
+
+
+ for(Restriction<?> restriction : restrictions){
+ Collection<? extends Object> values = restriction.getValues();
+ JoinType jointype = LEFTOUTER_OPS.contains(restriction.getOperator()) ? JoinType.LEFT_OUTER_JOIN : JoinType.INNER_JOIN;
+ if(values != null && !values.isEmpty()){
+ // ---
+ String propertyPath = restriction.getPropertyName();
+ String[] props = propertyPath.split("\\.");
+ String propertyName;
+ if(props.length == 1){
+ // direct property of the base type of the criteria
+ propertyName = propertyPath;
+ } else {
+ // create aliases if the propertyName is a dot separated property path
+ String aĺiasKey = jointype.name() + "_";
+ String aliasedProperty = null;
+ String alias = "";
+ for(int p = 0; p < props.length -1; p++){
+ aĺiasKey = aĺiasKey + (aĺiasKey.isEmpty() ? "" : ".") + props[p];
+ aliasedProperty = alias + (alias.isEmpty() ? "" : ".") + props[p];
+ if(!aliases.containsKey(aliasedProperty)){
+ alias = alias + (alias.isEmpty() ? "" : "_" ) + props[p];
+ aliases.put(aĺiasKey, alias);
+ criteria.createAlias(aliasedProperty, alias, jointype);
+ if(logger.isDebugEnabled()){
+ logger.debug("addRestrictions() alias created with aliasKey " + aĺiasKey + " => " + aliasedProperty + " as " + alias);
+ }
+ }
+ }
+ propertyName = alias + "." + props[props.length -1];
+ }
+ // ---
+ Criterion[] predicates = new Criterion[values.size()];
+ int i = 0;
+ for(Object v : values){
+ Criterion criterion = createRestriction(propertyName, v, restriction.getMatchMode());
+ if(restriction.isNot()){
+ if(props.length > 1){
+ criterion = Restrictions.or(Restrictions.not(criterion), Restrictions.isNull(propertyName));
+ } else {
+ criterion = Restrictions.not(criterion);
+ }
+ }
+ predicates[i++] = criterion;
+ if(logger.isDebugEnabled()){
+ logger.debug("addRestrictions() predicate with " + propertyName + " " + (restriction.getMatchMode() == null ? "=" : restriction.getMatchMode().name()) + " " + v.toString());
+ }
+ }
+ if(restriction.getOperator() == Operator.AND_NOT){
+ perProperty.add(new CriterionWithOperator(restriction.getOperator(), Restrictions.and(predicates)));
+ } else {
+ perProperty.add(new CriterionWithOperator(restriction.getOperator(), Restrictions.or(predicates)));
+ }
+ } // check has values
+ } // loop over restrictions
+
+ Restriction.Operator firstOperator = null;
+ if(!perProperty.isEmpty()){
+ LogicalExpression logicalExpression = null;
+ for(CriterionWithOperator cwo : perProperty){
+ if(logicalExpression == null){
+ firstOperator = cwo.operator;
+ logicalExpression = Restrictions.and(Restrictions.sqlRestriction("1=1"), cwo.criterion);
+ } else {
+ switch(cwo.operator){
+ case AND:
+ case AND_NOT:
+ logicalExpression = Restrictions.and(logicalExpression, cwo.criterion);
+ break;
+ case OR:
+ case OR_NOT:
+ logicalExpression = Restrictions.or(logicalExpression, cwo.criterion);
+ break;
+ default:
+ throw new RuntimeException("Unsupported Operator");
+ }
+ }
+
+ }
+
+
+ criteria.add(logicalExpression);
+// if(firstOperator == Operator.OR){
+// // OR
+// } else {
+// // AND
+// criteria.add(Restrictions.and(queryStringCriterion, logicalExpression));
+// }
+ }
+ if(logger.isDebugEnabled()){
+ logger.debug("addRestrictions() final criteria: " + criteria.toString());
+ }
+ }
+
+ /**
+ * @param propertyName
+ * @param value
+ * @param matchMode
+ * is only applied if the <code>value</code> is a
+ * <code>String</code> object
+ * @param criteria
+ * @return
+ */
+ private Criterion createRestriction(String propertyName, Object value, MatchMode matchMode) {
+
+ Criterion restriction;
+ if (value == null) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("createRestriction() " + propertyName + " is null ");
+ }
+ restriction = Restrictions.isNull(propertyName);
+ } else if (matchMode == null || !(value instanceof String)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("createRestriction() " + propertyName + " = " + value.toString());
+ }
+ restriction = Restrictions.eq(propertyName, value);
+ } else {
+ String queryString = (String) value;
+ if (logger.isDebugEnabled()) {
+ logger.debug("createRestriction() " + propertyName + " " + matchMode.getMatchOperator() + " "
+ + matchMode.queryStringFrom(queryString));
+ }
+ switch(matchMode){
+ case BEGINNING:
+ restriction = Restrictions.ilike(propertyName, queryString, org.hibernate.criterion.MatchMode.START);
+ break;
+ case END:
+ restriction = Restrictions.ilike(propertyName, queryString, org.hibernate.criterion.MatchMode.END);
+ break;
+ case LIKE:
+ restriction = Restrictions.ilike(propertyName, matchMode.queryStringFrom(queryString), org.hibernate.criterion.MatchMode.ANYWHERE);
+ break;
+ case EXACT:
+ restriction = Restrictions.ilike(propertyName, queryString, org.hibernate.criterion.MatchMode.EXACT);
+ break;
+ case ANYWHERE:
+ restriction = Restrictions.ilike(propertyName, queryString, org.hibernate.criterion.MatchMode.ANYWHERE);
+ break;
+ default:
+ throw new RuntimeException("Unknown MatchMode: " + matchMode.name());
+ }
+ }
+ return restriction;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public long count(Class<? extends T> type, List<Restriction<?>> restrictions) {
+
+ Criteria criteria = createCriteria(type, restrictions, false);
+
+ criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));
+
+ return (Long) criteria.uniqueResult();
+
+ }
+
+ /**
+ * @param uuids
+ * @param pageSize
+ * @param pageNumber
+ * @param orderHints
+ * @param propertyName
+ * @return
+ */
+ private Criteria prepareList(Class<? extends T> clazz, Collection<?> uuids, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
+ String propertyName) {
+ if (clazz == null){
+ clazz = type;
+ }
+ Criteria criteria = getSession().createCriteria(clazz);
+ criteria.add(Restrictions.in(propertyName, uuids));
+
+ if (pageSize != null) {
+ criteria.setMaxResults(pageSize);
+ if (pageNumber != null) {
+ criteria.setFirstResult(pageNumber * pageSize);
+ } else {
+ criteria.setFirstResult(0);
+ }
+ }
+
+ if (orderHints == null) {
+ orderHints = OrderHint.defaultOrderHintsFor(type);
+ }
+ addOrder(criteria, orderHints);
+ return criteria;
+ }
+
+ private Criteria criterionForType(Class<? extends T> clazz) {
+ return getSession().createCriteria(entityType(clazz));
+ }
+
+ protected Class<? extends T> entityType(Class<? extends T> clazz){
+ if (clazz != null) {
+ return clazz;
+ } else {
+ return type;
+ }
+ }
+
+ @Override
+ public T load(UUID uuid) {
+ T bean = findByUuid(uuid);
+ if (bean == null) {
+ return null;
+ }
+ defaultBeanInitializer.load(bean);
+
+ return bean;
+ }
+
+ @Override
+ public T load(int id, List<String> propertyPaths) {
+ T bean = findById(id);
+ if (bean == null) {
+ return bean;
+ }
+ defaultBeanInitializer.initialize(bean, propertyPaths);
+
+ return bean;
+ }
+
+ @Override
+ public T loadWithoutInitializing(int id){
+ return this.getSession().load(type, id);
+ }
+
+ @Override
+ public T load(UUID uuid, List<String> propertyPaths) {
+ return this.load(uuid, INCLUDE_UNPUBLISHED, propertyPaths);
+ }
+
+ protected T load(UUID uuid, boolean includeUnpublished, List<String> propertyPaths) {
+ T bean = findByUuid(uuid, includeUnpublished);
+ if (bean == null) {
+ return bean;
+ }
+ defaultBeanInitializer.initialize(bean, propertyPaths);
+
+ return bean;
+ }
+
+ @Override
+ public Boolean exists(UUID uuid) {
+ if (findByUuid(uuid) == null) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public long count() {
+ return count(type);
+ }
+
+ @Override
+ public long count(Class<? extends T> clazz) {
+ Session session = getSession();
+ Criteria criteria = null;
+ if (clazz == null) {
+ criteria = session.createCriteria(type);
+ } else {
+ criteria = session.createCriteria(clazz);
+ }
+ criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));
+
+ // since hibernate 4 (or so) uniqueResult returns Long, not Integer,
+ // therefore needs
+ // to be casted. Think about returning long rather then int!
+ return (long) criteria.uniqueResult();
+ }
+
+ @Override
+ public List<T> list(Integer limit, Integer start) {
+ return list(limit, start, null);
+ }
+
+ @Override
+ public List<Object[]> group(Class<? extends T> clazz, Integer limit, Integer start, List<Grouping> groups,
+ List<String> propertyPaths) {
+
+ Criteria criteria = null;
+ criteria = criterionForType(clazz);
+
+ addGroups(criteria, groups);
+
+ if (limit != null) {
+ criteria.setFirstResult(start);
+ criteria.setMaxResults(limit);
+ }
+
+ @SuppressWarnings("unchecked")
+ List<Object[]> result = criteria.list();
+
+ if (propertyPaths != null && !propertyPaths.isEmpty()) {
+ for (Object[] objects : result) {
+ defaultBeanInitializer.initialize(objects[0], propertyPaths);
+ }
+ }
+
+ return result;
+ }
+
+ protected void countGroups(DetachedCriteria criteria, List<Grouping> groups) {
+ if (groups != null) {
+
+ Map<String, String> aliases = new HashMap<String, String>();
+
+ for (Grouping grouping : groups) {
+ if (grouping.getAssociatedObj() != null) {
+ String alias = null;
+ if ((alias = aliases.get(grouping.getAssociatedObj())) == null) {
+ alias = grouping.getAssociatedObjectAlias();
+ aliases.put(grouping.getAssociatedObj(), alias);
+ criteria.createAlias(grouping.getAssociatedObj(), alias);
+ }
+ }
+ }
+
+ ProjectionList projectionList = Projections.projectionList();
+
+ for (Grouping grouping : groups) {
+ grouping.addProjection(projectionList);
+ }
+ criteria.setProjection(projectionList);
+ }
+ }
+
+ protected void addGroups(Criteria criteria, List<Grouping> groups) {
+ if (groups != null) {
+
+ Map<String, String> aliases = new HashMap<String, String>();
+
+ for (Grouping grouping : groups) {
+ if (grouping.getAssociatedObj() != null) {
+ String alias = null;
+ if ((alias = aliases.get(grouping.getAssociatedObj())) == null) {
+ alias = grouping.getAssociatedObjectAlias();
+ aliases.put(grouping.getAssociatedObj(), alias);
+ criteria.createAlias(grouping.getAssociatedObj(), alias);
+ }
+ }
+ }
+
+ ProjectionList projectionList = Projections.projectionList();
+
+ for (Grouping grouping : groups) {
+ grouping.addProjection(projectionList);
+ }
+ criteria.setProjection(projectionList);
+
+ for (Grouping grouping : groups) {
+ grouping.addOrder(criteria);
+
+ }
+ }
+ }
+
+ @Override
+ public List<T> list(Integer limit, Integer start, List<OrderHint> orderHints) {
+ return list(limit, start, orderHints, null);
+ }
+
+ @Override
+ public List<T> list(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
+ Criteria criteria = getSession().createCriteria(type);
+ if (limit != null) {
+ criteria.setFirstResult(start);
+ criteria.setMaxResults(limit);
+ }
+
+ addOrder(criteria, orderHints);
+ @SuppressWarnings("unchecked")
+ List<T> results = criteria.list();
+
+ defaultBeanInitializer.initializeAll(results, propertyPaths);
+ return results;
+ }
+
+ @Override
+ public <S extends T> List<S> list(Class<S> clazz, Integer limit, Integer start, List<OrderHint> orderHints,
+ List<String> propertyPaths) {
+ Criteria criteria = null;
+ if (clazz == null) {
+ criteria = getSession().createCriteria(type);
+ } else {
+ criteria = getSession().createCriteria(clazz);
+ }
+
+ addLimitAndStart(criteria, limit, start);
+
+ addOrder(criteria, orderHints);
+
+ @SuppressWarnings("unchecked")
+ List<S> results = criteria.list();
+
+ defaultBeanInitializer.initializeAll(results, propertyPaths);
+ return results;
+ }
+
+ public <S extends T> List<S> list(Class<S> type, Integer limit, Integer start, List<OrderHint> orderHints) {
+ return list(type, limit, start, orderHints, null);
+ }
+
+ @Override
+ public <S extends T> List<S> list(Class<S> type, Integer limit, Integer start) {
+ return list(type, limit, start, null, null);
+ }
+
+ @Override
+ public Class<T> getType() {
+ return type;
+ }
+
+ @Override
+ public long count(T example, Set<String> includeProperties) {
+ Criteria criteria = getSession().createCriteria(example.getClass());
+ addExample(criteria, example, includeProperties);
+
+ criteria.setProjection(Projections.rowCount());
+ return (Long) criteria.uniqueResult();
+ }
+
+ protected void addExample(Criteria criteria, T example, Set<String> includeProperties) {
+ if (includeProperties != null && !includeProperties.isEmpty()) {
+ criteria.add(Example.create(example).setPropertySelector(new PropertySelectorImpl(includeProperties)));
+ ClassMetadata classMetadata = getSession().getSessionFactory().getClassMetadata(example.getClass());
+ for (String property : includeProperties) {
+ Type type = classMetadata.getPropertyType(property);
+ if (type.isEntityType()) {
+ try {
+ Field field = ReflectionUtils.findField(example.getClass(), property);
+ field.setAccessible(true);
+ Object value = field.get(example);
+ if (value != null) {
+ criteria.add(Restrictions.eq(property, value));
+ } else {
+ criteria.add(Restrictions.isNull(property));
+ }
+ } catch (SecurityException se) {
+ throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property,
+ se);
+ } catch (HibernateException he) {
+ throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property,
+ he);
+ } catch (IllegalArgumentException iae) {
+ throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property,
+ iae);
+ } catch (IllegalAccessException ie) {
+ throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property,
+ ie);
+ }
+ }
+ }
+ } else {
+ criteria.add(Example.create(example));
+ }
+ }
+
+ /**
+ *
+ * NOTE: We can't reuse
+ * {@link #list(Class, String, Object, MatchMode, Integer, Integer, List, List)
+ * here due to different default behavior of the <code>matchmode</code>
+ * parameter.
+ *
+ * @param clazz
+ * @param param
+ * @param queryString
+ * @param matchmode
+ * @param criterion
+ * @param pageSize
+ * @param pageNumber
+ * @param orderHints
+ * @param propertyPaths
+ * @return
+ */
+ @Override
+ public <S extends T> List<S> findByParam(Class<S> clazz, String param, String queryString, MatchMode matchmode,
+ List<Criterion> criterion, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
+ List<String> propertyPaths) {
+ Set<String> stringSet = new HashSet<>();
+ stringSet.add(param);
+ return this.findByParam(clazz, stringSet, queryString, matchmode,
+ criterion, pageSize, pageNumber, orderHints,
+ propertyPaths);
+ }
+
+ @Override
+ public <S extends T> List<S> findByParam(Class<S> clazz, Set<String> params, String queryString, MatchMode matchmode,
+ List<Criterion> criterion, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
+ List<String> propertyPaths) {
+
+ Criteria criteria = criterionForType(clazz);
+
+ if (queryString != null) {
+ Set<Criterion> criterions = new HashSet<>();
+ for (String param: params){
+ Criterion crit;
+ if (matchmode == null) {
+ crit = Restrictions.ilike(param, queryString);
+ } else if (matchmode == MatchMode.BEGINNING) {
+ crit = Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.START);
+ } else if (matchmode == MatchMode.END) {
+ crit = Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.END);
+ } else if (matchmode == MatchMode.EXACT) {
+ crit = Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.EXACT);
+ } else {
+ crit = Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.ANYWHERE);
+ }
+ criterions.add(crit);
+ }
+ if (criterions.size()>1){
+ Iterator<Criterion> critIterator = criterions.iterator();
+ Disjunction disjunction = Restrictions.disjunction();
+ while (critIterator.hasNext()){
+ disjunction.add(critIterator.next());
+ }
+ criteria.add(disjunction);
+ }else{
+ if (!criterions.isEmpty()){
+ criteria.add(criterions.iterator().next());
+ }
+ }
+ }
+
+ addCriteria(criteria, criterion);
+
+ addPageSizeAndNumber(criteria, pageSize, pageNumber);
+ addOrder(criteria, orderHints);
+
+ @SuppressWarnings("unchecked")
+ List<S> result = criteria.list();
+ defaultBeanInitializer.initializeAll(result, propertyPaths);
+ return result;
+ }
+
+ @Override
+ public long countByParam(Class<? extends T> clazz, String param, String queryString, MatchMode matchmode,
+ List<Criterion> criterion) {
+
+ Criteria criteria = null;
+
+ criteria = criterionForType(clazz);
+
+ if (queryString != null) {
+ if (matchmode == null) {
+ criteria.add(Restrictions.ilike(param, queryString));
+ } else if (matchmode == MatchMode.BEGINNING) {
+ criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.START));
+ } else if (matchmode == MatchMode.END) {
+ criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.END));
+ } else if (matchmode == MatchMode.EXACT) {
+ criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.EXACT));
+ } else {
+ criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.ANYWHERE));
+ }
+ }
+
+ addCriteria(criteria, criterion);
+
+ criteria.setProjection(Projections.rowCount());
+
+ return (Long) criteria.uniqueResult();
+ }
+
+ /**
+ * Creates a criteria query for the CDM <code>type</code> either for counting or listing matching entities.
+ * <p>
+ * The set of matching entities can be restricted by passing a list of {@link Restriction} objects.
+ * Restrictions can logically combined:
+ <pre>
+ Arrays.asList(
+ new Restriction<String>("titleCache", MatchMode.ANYWHERE, "foo"),
+ new Restriction<String>("institute.name", Operator.OR, MatchMode.BEGINNING, "Bar")
+ );
+ </pre>
+ * The first Restriction in the example above by default has the <code>Operator.AND</code> which will be
+ * ignored since this is the first restriction. The <code>Operator</code> of further restrictions in the
+ * list are used to combine with the previous restriction.
+ *
+ * @param type
+ * @param restrictions
+ * @param doCount
+ * @return
+ */
+ protected Criteria createCriteria(Class<? extends T> type, List<Restriction<?>> restrictions, boolean doCount) {
+
+ DetachedCriteria idsOnlyCriteria = DetachedCriteria.forClass(entityType(type));
+ idsOnlyCriteria.setProjection(Projections.distinct(Projections.id()));
+
+ addRestrictions(restrictions, idsOnlyCriteria);
+
+ Criteria criteria = criterionForType(type);
+ criteria.add(Subqueries.propertyIn("id", idsOnlyCriteria));
+
+ if(doCount){
+ criteria.setProjection(Projections.rowCount());
+ } else {
+ idsOnlyCriteria.setProjection(Projections.distinct(Projections.property("id")));
+ }
+
+ return criteria;
+ }
+
+
+ @Override
+ public <S extends T> List<S> findByParamWithRestrictions(Class<S> clazz, String param, String queryString,
+ MatchMode matchmode, List<Restriction<?>> restrictions, Integer pageSize, Integer pageNumber,
+ List<OrderHint> orderHints, List<String> propertyPaths) {
+
+ List<Restriction<?>> allRestrictions = new ArrayList<>();
+ allRestrictions.add(new Restriction<>(param, matchmode, queryString));
+ if(restrictions != null){
+ allRestrictions.addAll(restrictions);
+ }
+ Criteria criteria = createCriteria(clazz, allRestrictions, false);
+
+ addPageSizeAndNumber(criteria, pageSize, pageNumber);
+
+ addOrder(criteria, orderHints);
+
+ @SuppressWarnings("unchecked")
+ List<S> result = criteria.list();
+ defaultBeanInitializer.initializeAll(result, propertyPaths);
+ return result;
+ }
+
+ @Override
+ public long countByParamWithRestrictions(Class<? extends T> clazz, String param, String queryString,
+ MatchMode matchmode, List<Restriction<?>> restrictions) {
+
+ List<Restriction<?>> allRestrictions = new ArrayList<>();
+ allRestrictions.add(new Restriction<>(param, matchmode, queryString));
+ if(restrictions != null){
+ allRestrictions.addAll(restrictions);
+ }
+ Criteria criteria = createCriteria(clazz, allRestrictions, true);
+
+ return (Long) criteria.uniqueResult();
+ }
+
+ @Override
+ public <S extends T> List<S> list(S example, Set<String> includeProperties, Integer limit, Integer start,
+ List<OrderHint> orderHints, List<String> propertyPaths) {
+ Criteria criteria = getSession().createCriteria(example.getClass());
+ addExample(criteria, example, includeProperties);
+
+ addLimitAndStart(criteria, limit, start);
+
+ addOrder(criteria, orderHints);
+
+ @SuppressWarnings("unchecked")
+ List<S> results = criteria.list();
+ defaultBeanInitializer.initializeAll(results, propertyPaths);
+ return results;
+ }
+
+ private class PropertySelectorImpl implements PropertySelector {
+
+ private final Set<String> includeProperties;
+ /**
+ *
+ */
+ private static final long serialVersionUID = -3175311800911570546L;
+
+ public PropertySelectorImpl(Set<String> includeProperties) {
+ this.includeProperties = includeProperties;
+ }
+
+ @Override
+ public boolean include(Object propertyValue, String propertyName, Type type) {
+ if (includeProperties.contains(propertyName)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ }
+
+ private class CriterionWithOperator {
+
+ Restriction.Operator operator;
+ Criterion criterion;
+
+
+ public CriterionWithOperator(Operator operator, Criterion criterion) {
+ super();
+ this.operator = operator;
+ this.criterion = criterion;
+ }
+
+
+ }
+
+ /**
+ * Returns a Criteria for the given {@link Class class} or, if
+ * <code>null</code>, for the base {@link Class class} of this DAO.
+ *
+ * @param clazz
+ * @return the Criteria
+ */
+ protected Criteria getCriteria(Class<? extends CdmBase> clazz) {
+ Criteria criteria = null;
+ if (clazz == null) {
+ criteria = getSession().createCriteria(type);
+ } else {
+ criteria = getSession().createCriteria(clazz);
+ }
+ return criteria;
+ }
+
+ /**
+ * @param clazz
+ * @param auditEvent
+ * @return
+ */
+ protected AuditQuery makeAuditQuery(Class<? extends CdmBase> clazz, AuditEvent auditEvent) {
+ AuditQuery query = null;
+
+ if (clazz == null) {
+ query = getAuditReader().createQuery().forEntitiesAtRevision(type, auditEvent.getRevisionNumber());
+ } else {
+ query = getAuditReader().createQuery().forEntitiesAtRevision(clazz, auditEvent.getRevisionNumber());
+ }
+ return query;
+ }
+
+ protected AuditReader getAuditReader() {
+ return AuditReaderFactory.get(getSession());
+ }
+}