improve generics for collection save in service and dao layer
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / persistence / dao / hibernate / common / CdmEntityDaoBase.java
index 5c796fede45dc5b5b40fd4df554f82268b527509..19cef6d825d06f65bea9f15a2b3fcb58ed80793c 100644 (file)
@@ -12,7 +12,9 @@ package eu.etaxonomy.cdm.persistence.dao.hibernate.common;
 import java.lang.reflect.Field;\r
 import java.util.ArrayList;\r
 import java.util.Collection;\r
+import java.util.EnumSet;\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
@@ -20,8 +22,6 @@ import java.util.Set;
 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
@@ -31,15 +31,20 @@ import org.hibernate.Query;
 import org.hibernate.Session;\r
 import org.hibernate.criterion.Criterion;\r
 import org.hibernate.criterion.DetachedCriteria;\r
+import org.hibernate.criterion.Disjunction;\r
 import org.hibernate.criterion.Example;\r
 import org.hibernate.criterion.Example.PropertySelector;\r
+import org.hibernate.criterion.LogicalExpression;\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.criterion.Subqueries;\r
+import org.hibernate.envers.AuditReader;\r
+import org.hibernate.envers.AuditReaderFactory;\r
 import org.hibernate.envers.query.AuditQuery;\r
 import org.hibernate.metadata.ClassMetadata;\r
-import org.hibernate.search.FullTextQuery;\r
+import org.hibernate.sql.JoinType;\r
 import org.hibernate.type.Type;\r
 import org.joda.time.DateTime;\r
 import org.springframework.beans.factory.annotation.Autowired;\r
@@ -51,9 +56,13 @@ import org.springframework.stereotype.Repository;
 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.IPublishable;\r
 import eu.etaxonomy.cdm.model.common.VersionableEntity;\r
+import eu.etaxonomy.cdm.model.permission.User;\r
+import eu.etaxonomy.cdm.model.view.AuditEvent;\r
 import eu.etaxonomy.cdm.persistence.dao.common.ICdmEntityDao;\r
+import eu.etaxonomy.cdm.persistence.dao.common.Restriction;\r
+import eu.etaxonomy.cdm.persistence.dao.common.Restriction.Operator;\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
@@ -63,23 +72,25 @@ import eu.etaxonomy.cdm.persistence.query.Grouping;
 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
+ * @author a.mueller FIXME CdmEntityDaoBase is abstract, can it be annotated\r
+ *         with @Repository?\r
  */\r
 @Repository\r
-public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implements ICdmEntityDao<T> {\r
+public abstract class CdmEntityDaoBase<T extends CdmBase>\r
+        extends DaoBase\r
+        implements ICdmEntityDao<T> {\r
+\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
+    protected int flushAfterNo = 1000; // large numbers may cause\r
+                                       // synchronisation errors when commiting\r
+                                       // the session !!\r
 \r
     protected Class<T> type;\r
 \r
-//    protected Version version = Configuration.luceneVersion;\r
-\r
     @Autowired\r
-//     @Qualifier("defaultBeanInitializer")\r
+    // @Qualifier("defaultBeanInitializer")\r
     protected IBeanInitializer defaultBeanInitializer;\r
 \r
     public void setDefaultBeanInitializer(IBeanInitializer defaultBeanInitializer) {\r
@@ -89,9 +100,11 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
     @Autowired\r
     private ReferringObjectMetadataFactory referringObjectMetadataFactory;\r
 \r
+    protected static final EnumSet<Operator> LEFTOUTER_OPS = EnumSet.of(Operator.AND_NOT, Operator.OR, Operator.OR_NOT);\r
 \r
-    public CdmEntityDaoBase(Class<T> type){\r
+    public CdmEntityDaoBase(Class<T> type) {\r
         this.type = type;\r
+        assert type != null;\r
         logger.debug("Creating DAO of type [" + type.getSimpleName() + "]");\r
     }\r
 \r
@@ -106,42 +119,52 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
         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
+    // 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
+    // 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
+    // TODO: Use everywhere CdmEntityDaoBase.saveAll() instead of\r
+    // ServiceBase.saveCdmObjectAll()?\r
+    // TODO: why does this use saveCdmObject_ which actually savesOrUpdateds\r
+    // data ?\r
     @Override\r
-    public Map<UUID, T> saveAll(Collection<T> cdmObjCollection){\r
+    public Map<UUID, T> saveAll(Collection<? extends 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
+        if (types > 0) {\r
+            if (logger.isDebugEnabled()) {\r
+                logger.debug("ClassType: + " + cdmObjCollection.getClass().getTypeParameters()[0]);\r
+            }\r
         }\r
 \r
-        Map<UUID, T> resultMap = new HashMap<UUID, T>();\r
-        Iterator<T> iterator = cdmObjCollection.iterator();\r
+        Map<UUID, T> resultMap = new HashMap<>();\r
+        Iterator<? extends 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
+        while (iterator.hasNext()) {\r
+            if (((i % 2000) == 0) && (i > 0)) {\r
+                logger.debug("Saved " + i + " objects");\r
+            }\r
             T cdmObj = iterator.next();\r
             UUID uuid = saveCdmObject_(cdmObj);\r
-            if (logger.isDebugEnabled()){logger.debug("Save cdmObj: " + (cdmObj == null? null: cdmObj.toString()));}\r
+            if (logger.isDebugEnabled()) {\r
+                logger.debug("Save cdmObj: " + (cdmObj == null ? null : cdmObj.toString()));\r
+            }\r
             resultMap.put(uuid, cdmObj);\r
             i++;\r
-            if ( (i % flushAfterNo) == 0){\r
-                try{\r
-                    if (logger.isDebugEnabled()){logger.debug("flush");}\r
+            if ((i % flushAfterNo) == 0) {\r
+                try {\r
+                    if (logger.isDebugEnabled()) {\r
+                        logger.debug("flush");\r
+                    }\r
                     flush();\r
-                }catch(Exception e){\r
+                } catch (Exception e) {\r
                     logger.error("An exception occurred when trying to flush data");\r
                     e.printStackTrace();\r
                     throw new RuntimeException(e);\r
@@ -149,37 +172,47 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
             }\r
         }\r
 \r
-        if ( logger.isInfoEnabled() ){logger.info("Saved " + i + " objects" );}\r
+        if (logger.isInfoEnabled()) {\r
+            logger.info("Saved " + i + " objects");\r
+        }\r
         return resultMap;\r
     }\r
 \r
-    private UUID saveOrUpdateCdmObject(T cdmObj){\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
+    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
+        if (types > 0) {\r
+            if (logger.isDebugEnabled()) {\r
+                logger.debug("ClassType: + " + cdmObjCollection.getClass().getTypeParameters()[0]);\r
+            }\r
         }\r
 \r
-        Map<UUID, T> resultMap = new HashMap<UUID, T>();\r
+        Map<UUID, T> resultMap = new HashMap<>();\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
+        while (iterator.hasNext()) {\r
+            if (((i % 2000) == 0) && (i > 0)) {\r
+                logger.debug("Saved " + i + " objects");\r
+            }\r
             T cdmObj = iterator.next();\r
             UUID uuid = saveOrUpdateCdmObject(cdmObj);\r
-            if (logger.isDebugEnabled()){logger.debug("Save cdmObj: " + (cdmObj == null? null: cdmObj.toString()));}\r
+            if (logger.isDebugEnabled()) {\r
+                logger.debug("Save cdmObj: " + (cdmObj == null ? null : cdmObj.toString()));\r
+            }\r
             resultMap.put(uuid, cdmObj);\r
             i++;\r
-            if ( (i % flushAfterNo) == 0){\r
-                try{\r
-                    if (logger.isDebugEnabled()){logger.debug("flush");}\r
+            if ((i % flushAfterNo) == 0) {\r
+                try {\r
+                    if (logger.isDebugEnabled()) {\r
+                        logger.debug("flush");\r
+                    }\r
                     flush();\r
-                }catch(Exception e){\r
+                } catch (Exception e) {\r
                     logger.error("An exception occurred when trying to flush data");\r
                     e.printStackTrace();\r
                     throw new RuntimeException(e);\r
@@ -187,23 +220,22 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
             }\r
         }\r
 \r
-        if ( logger.isInfoEnabled() ){logger.info("Saved " + i + " objects" );}\r
+        if (logger.isInfoEnabled()) {\r
+            logger.info("Saved " + i + " objects");\r
+        }\r
         return resultMap;\r
     }\r
 \r
-\r
-\r
-\r
     @Override\r
     public T replace(T x, T y) {\r
-        if(x.equals(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
+        if (y != null) {\r
+            while (!commonClass.isAssignableFrom(y.getClass())) {\r
+                if (commonClass.equals(type)) {\r
                     throw new RuntimeException();\r
                 }\r
                 commonClass = commonClass.getSuperclass();\r
@@ -214,21 +246,21 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
 \r
         Set<ReferringObjectMetadata> referringObjectMetas = referringObjectMetadataFactory.get(x.getClass());\r
 \r
-        for(ReferringObjectMetadata referringObjectMetadata : referringObjectMetas) {\r
+        for (ReferringObjectMetadata referringObjectMetadata : referringObjectMetas) {\r
 \r
-          List<CdmBase> referringObjects = referringObjectMetadata.getReferringObjects(x, getSession());\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
+            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
+                } 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
         }\r
         return y;\r
     }\r
@@ -242,7 +274,9 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
     public void clear() throws DataAccessException {\r
         Session session = getSession();\r
         session.clear();\r
-        if (logger.isDebugEnabled()){logger.debug("dao clear end");}\r
+        if (logger.isDebugEnabled()) {\r
+            logger.debug("dao clear end");\r
+        }\r
     }\r
 \r
     @Override\r
@@ -252,11 +286,13 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
         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
+            T persistentObject = (T) session.merge(transientObject);\r
+            if (logger.isDebugEnabled()) {\r
+                logger.debug("dao merge end");\r
+            }\r
 \r
-            if(returnTransientEntity) {\r
-                if(transientObject != null && persistentObject != null) {\r
+            if (returnTransientEntity) {\r
+                if (transientObject != null && persistentObject != null) {\r
                     transientObject.setId(persistentObject.getId());\r
                 }\r
                 result = new MergeResult(transientObject, PostMergeEntityListener.getNewEntities(session));\r
@@ -273,37 +309,46 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
     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
+        T persistentObject = (T) session.merge(transientObject);\r
+        if (logger.isDebugEnabled()) {\r
+            logger.debug("dao merge end");\r
+        }\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
+    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
+        try {\r
+            if (logger.isDebugEnabled()) {\r
+                logger.debug("dao saveOrUpdate start...");\r
+            }\r
+            if (logger.isDebugEnabled()) {\r
+                logger.debug("transientObject(" + transientObject.getClass().getSimpleName() + ") ID:"\r
+                        + transientObject.getId() + ", UUID: " + transientObject.getUuid());\r
+            }\r
             Session session = getSession();\r
-            if(transientObject.getId() != 0 && VersionableEntity.class.isAssignableFrom(transientObject.getClass())) {\r
-                VersionableEntity versionableEntity = (VersionableEntity)transientObject;\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
+                if (authentication != null && authentication.getPrincipal() != null\r
+                        && 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
+            if (logger.isDebugEnabled()) {\r
+                logger.debug("dao saveOrUpdate end");\r
+            }\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("Error in CdmEntityDaoBase.saveOrUpdate(obj). ID=" + e.getIdentifier() + ". Class="\r
+                    + e.getEntityName());\r
             logger.error(e.getMessage());\r
 \r
             e.printStackTrace();\r
@@ -316,22 +361,22 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
     }\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
+    public <S extends T> S save(S 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
+        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
+        if (transientObject == null) {\r
+            logger.warn("Object to update should not be null. NOP");\r
+            return null;\r
         }\r
-       getSession().update(transientObject);\r
+        getSession().update(transientObject);\r
         return transientObject.getUuid();\r
     }\r
 \r
@@ -343,7 +388,7 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
 \r
     @Override\r
     public UUID delete(T persistentObject) throws DataAccessException {\r
-        if (persistentObject == null){\r
+        if (persistentObject == null) {\r
             logger.warn(type.getName() + " was 'null'");\r
             return null;\r
         }\r
@@ -353,29 +398,38 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
         // 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
+        persistentObject = (T) getSession().merge(persistentObject);\r
         getSession().delete(persistentObject);\r
         return persistentObject.getUuid();\r
     }\r
 \r
-       @Override\r
+    @Override\r
     public T findById(int id) throws DataAccessException {\r
-        return (T) getSession().get(type, id);\r
+        return getSession().get(type, id);\r
     }\r
 \r
-\r
     @Override\r
-    public T findByUuid(UUID uuid) throws DataAccessException{\r
+    public T findByUuid(UUID uuid) throws DataAccessException {\r
+        return this.findByUuid(uuid, INCLUDE_UNPUBLISHED);\r
+    }\r
+\r
+    protected T findByUuid(UUID uuid, boolean includeUnpublished) 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
+        if (IPublishable.class.isAssignableFrom(type) && !includeUnpublished) {\r
+            crit.add(Restrictions.eq("publish", Boolean.TRUE));\r
+        }\r
+\r
         @SuppressWarnings("unchecked")\r
-               List<T> results = crit.list();\r
-        if (results.isEmpty()){\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(results.size() > 1){\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
@@ -383,63 +437,271 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
     }\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
+    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> listByIds(Collection<Integer> ids,  Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws DataAccessException {\r
+    public List<T> loadList(Collection<Integer> ids, List<OrderHint> orderHints, List<String> propertyPaths) throws DataAccessException {\r
 \r
         if (ids.isEmpty()) {\r
-            return new ArrayList<T>(0);\r
+            return new ArrayList<>(0);\r
         }\r
 \r
-        Criteria criteria = prepareList(ids, pageSize, pageNumber, orderHints, "id");\r
+        Criteria criteria = prepareList(null, ids, null, null, orderHints, "id");\r
+\r
+        if (logger.isDebugEnabled()) {\r
+            logger.debug(criteria.toString());\r
+        }\r
+\r
+        @SuppressWarnings("unchecked")\r
+        List<T> result = criteria.list();\r
+        defaultBeanInitializer.initializeAll(result, propertyPaths);\r
+        return result;\r
+    }\r
+\r
+    @Override\r
+    public List<T> list(Collection<UUID> uuids, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,\r
+            List<String> propertyPaths) throws DataAccessException {\r
+\r
+        if (uuids == null || uuids.isEmpty()) {\r
+            return new ArrayList<>();\r
+        }\r
+\r
+        Criteria criteria = prepareList(null, uuids, pageSize, pageNumber, orderHints, "uuid");\r
+        @SuppressWarnings("unchecked")\r
+        List<T> result = criteria.list();\r
+        defaultBeanInitializer.initializeAll(result, propertyPaths);\r
+        return result;\r
+    }\r
 \r
-        if (logger.isDebugEnabled()){logger.debug(criteria.toString());};\r
+    @Override\r
+    public <S extends T> List<S> list(Class<S> clazz, Collection<UUID> uuids, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,\r
+            List<String> propertyPaths) throws DataAccessException {\r
 \r
-         @SuppressWarnings("unchecked")\r
-               List<T> result = criteria.list();\r
-         defaultBeanInitializer.initializeAll(result, propertyPaths);\r
-         return result;\r
-     }\r
+        if (uuids == null || uuids.isEmpty()) {\r
+            return new ArrayList<>();\r
+        }\r
 \r
+        Criteria criteria = prepareList(clazz, uuids, pageSize, pageNumber, orderHints, "uuid");\r
+        @SuppressWarnings("unchecked")\r
+        List<S> result = criteria.list();\r
+        defaultBeanInitializer.initializeAll(result, propertyPaths);\r
+        return result;\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
+    public <S extends T> List<S> list(Class<S> type, List<Restriction<?>> restrictions, Integer limit, Integer start,\r
+            List<OrderHint> orderHints, List<String> propertyPaths) {\r
 \r
-        Criteria criteria = prepareList(uuids, pageSize, pageNumber, orderHints, "uuid");\r
+        Criteria criteria = createCriteria(type, restrictions, false);\r
+\r
+        addLimitAndStart(criteria, limit, start);\r
+        addOrder(criteria, orderHints);\r
 \r
         @SuppressWarnings("unchecked")\r
-               List<T> result = criteria.list();\r
+        List<S> result = criteria.list();\r
         defaultBeanInitializer.initializeAll(result, propertyPaths);\r
         return result;\r
     }\r
 \r
+    /**\r
+     * @param restrictions\r
+     * @param criteria\r
+     */\r
+    private void addRestrictions(List<Restriction<?>> restrictions, DetachedCriteria criteria) {\r
+\r
+        if(restrictions == null || restrictions.isEmpty()){\r
+            return ;\r
+        }\r
+\r
+        List<CriterionWithOperator> perProperty = new ArrayList<>(restrictions.size());\r
+        Map<String, String> aliases = new HashMap<>();\r
+\r
+\r
+\r
+        for(Restriction<?> restriction : restrictions){\r
+            Collection<? extends Object> values = restriction.getValues();\r
+            JoinType jointype = LEFTOUTER_OPS.contains(restriction.getOperator()) ? JoinType.LEFT_OUTER_JOIN : JoinType.INNER_JOIN;\r
+            if(values != null && !values.isEmpty()){\r
+                // ---\r
+                String propertyPath = restriction.getPropertyName();\r
+                String[] props =  propertyPath.split("\\.");\r
+                String propertyName;\r
+                if(props.length == 1){\r
+                    // direct property of the base type of the criteria\r
+                    propertyName = propertyPath;\r
+                } else {\r
+                    // create aliases if the propertyName is a dot separated property path\r
+                    String aĺiasKey = jointype.name() + "_";\r
+                    String aliasedProperty = null;\r
+                    String alias = "";\r
+                    for(int p = 0; p < props.length -1; p++){\r
+                        aĺiasKey = aĺiasKey + (aĺiasKey.isEmpty() ? "" : ".") + props[p];\r
+                        aliasedProperty = alias + (alias.isEmpty() ? "" : ".") + props[p];\r
+                        if(!aliases.containsKey(aliasedProperty)){\r
+                            alias = alias + (alias.isEmpty() ? "" : "_" ) + props[p];\r
+                            aliases.put(aĺiasKey, alias);\r
+                            criteria.createAlias(aliasedProperty, alias, jointype);\r
+                            if(logger.isDebugEnabled()){\r
+                                logger.debug("addRestrictions() alias created with aliasKey " + aĺiasKey + " => " + aliasedProperty + " as " + alias);\r
+                            }\r
+                        }\r
+                    }\r
+                    propertyName = alias + "." + props[props.length -1];\r
+                }\r
+                // ---\r
+                Criterion[] predicates = new Criterion[values.size()];\r
+                int i = 0;\r
+                for(Object v : values){\r
+                    Criterion criterion = createRestriction(propertyName, v, restriction.getMatchMode());\r
+                    if(restriction.isNot()){\r
+                        if(props.length > 1){\r
+                            criterion = Restrictions.or(Restrictions.not(criterion), Restrictions.isNull(propertyName));\r
+                        } else {\r
+                            criterion = Restrictions.not(criterion);\r
+                        }\r
+                    }\r
+                    predicates[i++] = criterion;\r
+                    if(logger.isDebugEnabled()){\r
+                        logger.debug("addRestrictions() predicate with " + propertyName + " " + (restriction.getMatchMode() == null ? "=" : restriction.getMatchMode().name()) + " " + v.toString());\r
+                    }\r
+                }\r
+                if(restriction.getOperator() == Operator.AND_NOT){\r
+                    perProperty.add(new CriterionWithOperator(restriction.getOperator(), Restrictions.and(predicates)));\r
+                } else {\r
+                    perProperty.add(new CriterionWithOperator(restriction.getOperator(), Restrictions.or(predicates)));\r
+                }\r
+            } // check has values\r
+        } // loop over restrictions\r
+\r
+        Restriction.Operator firstOperator = null;\r
+        if(!perProperty.isEmpty()){\r
+            LogicalExpression logicalExpression = null;\r
+            for(CriterionWithOperator cwo : perProperty){\r
+                if(logicalExpression == null){\r
+                    firstOperator = cwo.operator;\r
+                    logicalExpression = Restrictions.and(Restrictions.sqlRestriction("1=1"), cwo.criterion);\r
+                } else {\r
+                    switch(cwo.operator){\r
+                        case AND:\r
+                        case AND_NOT:\r
+                            logicalExpression = Restrictions.and(logicalExpression, cwo.criterion);\r
+                            break;\r
+                        case OR:\r
+                        case OR_NOT:\r
+                            logicalExpression = Restrictions.or(logicalExpression, cwo.criterion);\r
+                            break;\r
+                        default:\r
+                            throw new RuntimeException("Unsupported Operator");\r
+                    }\r
+                }\r
+\r
+            }\r
+\r
+\r
+            criteria.add(logicalExpression);\r
+//            if(firstOperator == Operator.OR){\r
+//                // OR\r
+//            } else {\r
+//                // AND\r
+//                criteria.add(Restrictions.and(queryStringCriterion, logicalExpression));\r
+//            }\r
+        }\r
+        if(logger.isDebugEnabled()){\r
+            logger.debug("addRestrictions() final criteria: " + criteria.toString());\r
+        }\r
+    }\r
+\r
+    /**\r
+     * @param propertyName\r
+     * @param value\r
+     * @param matchMode\r
+     *            is only applied if the <code>value</code> is a\r
+     *            <code>String</code> object\r
+     * @param criteria\r
+     * @return\r
+     */\r
+    private Criterion createRestriction(String propertyName, Object value, MatchMode matchMode) {\r
+\r
+        Criterion restriction;\r
+        if (value == null) {\r
+            if (logger.isDebugEnabled()) {\r
+                logger.debug("createRestriction() " + propertyName + " is null ");\r
+            }\r
+            restriction = Restrictions.isNull(propertyName);\r
+        } else if (matchMode == null || !(value instanceof String)) {\r
+            if (logger.isDebugEnabled()) {\r
+                logger.debug("createRestriction() " + propertyName + " = " + value.toString());\r
+            }\r
+            restriction = Restrictions.eq(propertyName, value);\r
+        } else {\r
+            String queryString = (String) value;\r
+            if (logger.isDebugEnabled()) {\r
+                logger.debug("createRestriction() " + propertyName + " " + matchMode.getMatchOperator() + " "\r
+                        + matchMode.queryStringFrom(queryString));\r
+            }\r
+            switch(matchMode){\r
+            case BEGINNING:\r
+                restriction = Restrictions.ilike(propertyName, queryString, org.hibernate.criterion.MatchMode.START);\r
+                break;\r
+            case END:\r
+                restriction = Restrictions.ilike(propertyName, queryString, org.hibernate.criterion.MatchMode.END);\r
+                break;\r
+            case LIKE:\r
+                restriction = Restrictions.ilike(propertyName, matchMode.queryStringFrom(queryString), org.hibernate.criterion.MatchMode.ANYWHERE);\r
+                break;\r
+            case EXACT:\r
+                restriction = Restrictions.ilike(propertyName, queryString, org.hibernate.criterion.MatchMode.EXACT);\r
+                break;\r
+            case ANYWHERE:\r
+                restriction = Restrictions.ilike(propertyName, queryString, org.hibernate.criterion.MatchMode.ANYWHERE);\r
+                break;\r
+            default:\r
+                throw new RuntimeException("Unknown MatchMode: " + matchMode.name());\r
+            }\r
+        }\r
+        return restriction;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     */\r
+    @Override\r
+    public long count(Class<? extends T> type, List<Restriction<?>> restrictions) {\r
+\r
+        Criteria criteria = createCriteria(type, restrictions, false);\r
+\r
+        criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));\r
+\r
+        return (Long) criteria.uniqueResult();\r
+\r
+    }\r
+\r
     /**\r
      * @param uuids\r
      * @param pageSize\r
@@ -448,73 +710,50 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
      * @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
+    private Criteria prepareList(Class<? extends T> clazz, Collection<?> uuids, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,\r
+            String propertyName) {\r
+        if (clazz == null){\r
+            clazz = type;\r
+        }\r
+        Criteria criteria = getSession().createCriteria(clazz);\r
         criteria.add(Restrictions.in(propertyName, uuids));\r
 \r
-        if(pageSize != null) {\r
+        if (pageSize != null) {\r
             criteria.setMaxResults(pageSize);\r
-            if(pageNumber != null) {\r
+            if (pageNumber != null) {\r
                 criteria.setFirstResult(pageNumber * pageSize);\r
             } else {\r
                 criteria.setFirstResult(0);\r
             }\r
         }\r
 \r
-        if(orderHints == null) {\r
+        if (orderHints == null) {\r
             orderHints = OrderHint.defaultOrderHintsFor(type);\r
         }\r
         addOrder(criteria, orderHints);\r
         return criteria;\r
     }\r
 \r
+    /**\r
+     * @param clazz\r
+     * @return\r
+     */\r
+    private Criteria criterionForType(Class<? extends T> clazz) {\r
+        return  getSession().createCriteria(entityType(clazz));\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
+    protected Class<? extends T> entityType(Class<? extends T> clazz){\r
+        if (clazz != null) {\r
+            return clazz;\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
+            return type;\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
+        if (bean == null) {\r
             return null;\r
         }\r
         defaultBeanInitializer.load(bean);\r
@@ -523,9 +762,9 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
     }\r
 \r
     @Override\r
-    public T load(int id, List<String> propertyPaths){\r
+    public T load(int id, List<String> propertyPaths) {\r
         T bean = findById(id);\r
-        if(bean == null){\r
+        if (bean == null) {\r
             return bean;\r
         }\r
         defaultBeanInitializer.initialize(bean, propertyPaths);\r
@@ -534,9 +773,18 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
     }\r
 \r
     @Override\r
-    public T load(UUID uuid, List<String> propertyPaths){\r
-        T bean = findByUuid(uuid);\r
-        if(bean == null){\r
+    public T loadWithoutInitializing(int id){\r
+        return this.getSession().load(type, id);\r
+    }\r
+\r
+    @Override\r
+    public T load(UUID uuid, List<String> propertyPaths) {\r
+        return this.load(uuid, INCLUDE_UNPUBLISHED, propertyPaths);\r
+    }\r
+\r
+    protected T load(UUID uuid, boolean includeUnpublished, List<String> propertyPaths) {\r
+        T bean = findByUuid(uuid, includeUnpublished);\r
+        if (bean == null) {\r
             return bean;\r
         }\r
         defaultBeanInitializer.initialize(bean, propertyPaths);\r
@@ -546,31 +794,32 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
 \r
     @Override\r
     public Boolean exists(UUID uuid) {\r
-        if (findByUuid(uuid)==null){\r
+        if (findByUuid(uuid) == null) {\r
             return false;\r
         }\r
         return true;\r
     }\r
 \r
     @Override\r
-    public int count() {\r
+    public long count() {\r
         return count(type);\r
     }\r
 \r
     @Override\r
-    public int count(Class<? extends T> clazz) {\r
+    public long count(Class<? extends T> clazz) {\r
         Session session = getSession();\r
         Criteria criteria = null;\r
-        if(clazz == 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
+        // since hibernate 4 (or so) uniqueResult returns Long, not Integer,\r
+        // therefore needs\r
+        // to be casted. Think about returning long rather then int!\r
+        return (long) criteria.uniqueResult();\r
     }\r
 \r
     @Override\r
@@ -579,189 +828,135 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
     }\r
 \r
     @Override\r
-    public List<Object[]> group(Class<? extends T> clazz,Integer limit, Integer start, List<Grouping> groups, List<String> propertyPaths) {\r
+    public List<Object[]> group(Class<? extends T> clazz, Integer limit, Integer start, List<Grouping> groups,\r
+            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
+        criteria = criterionForType(clazz);\r
 \r
-        addGroups(criteria,groups);\r
+        addGroups(criteria, groups);\r
 \r
-        if(limit != null) {\r
+        if (limit != null) {\r
             criteria.setFirstResult(start);\r
             criteria.setMaxResults(limit);\r
         }\r
 \r
         @SuppressWarnings("unchecked")\r
-               List<Object[]> result = criteria.list();\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
+        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
+    protected void countGroups(DetachedCriteria criteria, List<Grouping> groups) {\r
+        if (groups != null) {\r
 \r
-            Map<String,String> aliases = new HashMap<String,String>();\r
+            Map<String, String> aliases = new HashMap<String, String>();\r
 \r
-            for(Grouping grouping : groups) {\r
-                if(grouping.getAssociatedObj() != null) {\r
+            for (Grouping grouping : groups) {\r
+                if (grouping.getAssociatedObj() != null) {\r
                     String alias = null;\r
-                    if((alias = aliases.get(grouping.getAssociatedObj())) == 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
+                        criteria.createAlias(grouping.getAssociatedObj(), alias);\r
                     }\r
                 }\r
             }\r
 \r
             ProjectionList projectionList = Projections.projectionList();\r
 \r
-            for(Grouping grouping : groups) {\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
+    protected void addGroups(Criteria criteria, List<Grouping> groups) {\r
+        if (groups != null) {\r
 \r
+            Map<String, String> aliases = new HashMap<String, String>();\r
 \r
-            Map<String,String> aliases = new HashMap<String,String>();\r
-\r
-            for(Grouping grouping : groups) {\r
-                if(grouping.getAssociatedObj() != null) {\r
+            for (Grouping grouping : groups) {\r
+                if (grouping.getAssociatedObj() != null) {\r
                     String alias = null;\r
-                    if((alias = aliases.get(grouping.getAssociatedObj())) == 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
+                        criteria.createAlias(grouping.getAssociatedObj(), alias);\r
                     }\r
                 }\r
             }\r
 \r
             ProjectionList projectionList = Projections.projectionList();\r
 \r
-            for(Grouping grouping : groups) {\r
+            for (Grouping grouping : groups) {\r
                 grouping.addProjection(projectionList);\r
             }\r
             criteria.setProjection(projectionList);\r
 \r
-            for(Grouping grouping : groups) {\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
+        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
+        if (limit != null) {\r
             criteria.setFirstResult(start);\r
             criteria.setMaxResults(limit);\r
         }\r
 \r
-        addOrder(criteria,orderHints);\r
+        addOrder(criteria, orderHints);\r
         @SuppressWarnings("unchecked")\r
-               List<T> results = criteria.list();\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
+    public <S extends T> List<S> list(Class<S> clazz, Integer limit, Integer start, List<OrderHint> orderHints,\r
+            List<String> propertyPaths) {\r
         Criteria criteria = null;\r
-        if(clazz == 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
+        addLimitAndStart(criteria, limit, start);\r
 \r
-        addOrder(criteria,orderHints);\r
+        addOrder(criteria, orderHints);\r
 \r
         @SuppressWarnings("unchecked")\r
-               List<S> results = criteria.list();\r
+        List<S> results = criteria.list();\r
+\r
         defaultBeanInitializer.initializeAll(results, propertyPaths);\r
         return results;\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
+        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
+        return list(type, limit, start, null, null);\r
     }\r
 \r
     @Override\r
@@ -769,22 +964,22 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
         return type;\r
     }\r
 \r
-    protected void setPagingParameter(Query query, Integer pageSize, Integer pageNumber){\r
-        if(pageSize != null) {\r
+    protected void setPagingParameter(Query query, Integer pageSize, Integer pageIndex) {\r
+        if (pageSize != null) {\r
             query.setMaxResults(pageSize);\r
-            if(pageNumber != null) {\r
-                query.setFirstResult(pageNumber * pageSize);\r
+            if (pageIndex != null) {\r
+                query.setFirstResult(pageIndex * 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
+    protected void setPagingParameter(AuditQuery query, Integer pageSize, Integer pageIndex) {\r
+        if (pageSize != null) {\r
             query.setMaxResults(pageSize);\r
-            if(pageNumber != null) {\r
-                query.setFirstResult(pageNumber * pageSize);\r
+            if (pageIndex != null) {\r
+                query.setFirstResult(pageIndex * pageSize);\r
             } else {\r
                 query.setFirstResult(0);\r
             }\r
@@ -792,40 +987,43 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
     }\r
 \r
     @Override\r
-    public int count(T example, Set<String> includeProperties) {\r
+    public long count(T example, Set<String> includeProperties) {\r
         Criteria criteria = getSession().createCriteria(example.getClass());\r
-        addExample(criteria,example,includeProperties);\r
+        addExample(criteria, example, includeProperties);\r
 \r
         criteria.setProjection(Projections.rowCount());\r
-        return ((Number)criteria.uniqueResult()).intValue();\r
+        return (Long) criteria.uniqueResult();\r
     }\r
 \r
     protected void addExample(Criteria criteria, T example, Set<String> includeProperties) {\r
-        if(includeProperties != null && !includeProperties.isEmpty()) {\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
+            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
+                        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
+                        throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property,\r
+                                se);\r
                     } catch (HibernateException he) {\r
-                        throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property, he);\r
+                        throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property,\r
+                                he);\r
                     } catch (IllegalArgumentException iae) {\r
-                        throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property, iae);\r
+                        throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property,\r
+                                iae);\r
                     } catch (IllegalAccessException ie) {\r
-                        throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property, ie);\r
+                        throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property,\r
+                                ie);\r
                     }\r
-\r
                 }\r
             }\r
         } else {\r
@@ -833,24 +1031,120 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
         }\r
     }\r
 \r
+    /**\r
+     *\r
+     * NOTE: We can't reuse\r
+     * {@link #list(Class, String, Object, MatchMode, Integer, Integer, List, List)\r
+     * here due to different default behavior of the <code>matchmode</code>\r
+     * parameter.\r
+     *\r
+     * @param clazz\r
+     * @param param\r
+     * @param queryString\r
+     * @param matchmode\r
+     * @param criterion\r
+     * @param pageSize\r
+     * @param pageNumber\r
+     * @param orderHints\r
+     * @param propertyPaths\r
+     * @return\r
+     */\r
+    @Override\r
+    public <S extends T> List<S> findByParam(Class<S> clazz, String param, String queryString, MatchMode matchmode,\r
+            List<Criterion> criterion, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,\r
+            List<String> propertyPaths) {\r
+        Set<String> stringSet = new HashSet<>();\r
+        stringSet.add(param);\r
+        return this.findByParam(clazz, stringSet, queryString, matchmode,\r
+                criterion, pageSize, pageNumber, orderHints,\r
+                propertyPaths);\r
+    }\r
 \r
-    protected int countByParam(Class<? extends T> clazz, String param, String queryString, MatchMode matchmode, List<Criterion> criterion) {\r
-        Criteria criteria = null;\r
+    @Override\r
+    public <S extends T> List<S> findByParam(Class<S> clazz, Set<String> params, String queryString, MatchMode matchmode,\r
+            List<Criterion> criterion, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,\r
+            List<String> propertyPaths) {\r
+\r
+        Criteria criteria = criterionForType(clazz);\r
+\r
+        if (queryString != null) {\r
+            Set<Criterion> criterions = new HashSet<>();\r
+            for (String param: params){\r
+                Criterion crit;\r
+                if (matchmode == null) {\r
+                     crit = Restrictions.ilike(param, queryString);\r
+                } else if (matchmode == MatchMode.BEGINNING) {\r
+                     crit = Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.START);\r
+                } else if (matchmode == MatchMode.END) {\r
+                    crit = Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.END);\r
+                } else if (matchmode == MatchMode.EXACT) {\r
+                    crit = Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.EXACT);\r
+                } else {\r
+                    crit = Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.ANYWHERE);\r
+                }\r
+                criterions.add(crit);\r
+            }\r
+            if (criterions.size()>1){\r
+                Iterator<Criterion> critIterator = criterions.iterator();\r
+                Disjunction disjunction = Restrictions.disjunction();\r
+                while (critIterator.hasNext()){\r
+                    disjunction.add(critIterator.next());\r
+                }\r
+\r
+                criteria.add(disjunction);\r
+\r
+            }else{\r
+                if (!criterions.isEmpty()){\r
+                    criteria.add(criterions.iterator().next());\r
+                }\r
+            }\r
 \r
-        if(clazz == null) {\r
-            criteria = getSession().createCriteria(type);\r
-        } else {\r
-            criteria = getSession().createCriteria(clazz);\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<S> result = criteria.list();\r
+        defaultBeanInitializer.initializeAll(result, propertyPaths);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     *\r
+     * @param clazz\r
+     * @param param\r
+     * @param queryString\r
+     * @param matchmode\r
+     * @param criterion\r
+     * @return\r
+     */\r
+    @Override\r
+    public long countByParam(Class<? extends T> clazz, String param, String queryString, MatchMode matchmode,\r
+            List<Criterion> criterion) {\r
+\r
+        Criteria criteria = null;\r
+\r
+        criteria = criterionForType(clazz);\r
+\r
         if (queryString != null) {\r
-            if(matchmode == null) {\r
+            if (matchmode == null) {\r
                 criteria.add(Restrictions.ilike(param, queryString));\r
-            } else if(matchmode == MatchMode.BEGINNING) {\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
+            } else if (matchmode == MatchMode.END) {\r
                 criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.END));\r
-            } else if(matchmode == MatchMode.EXACT) {\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
@@ -861,29 +1155,105 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
 \r
         criteria.setProjection(Projections.rowCount());\r
 \r
-//        List<T> result = criteria.list();\r
-        return ((Number)criteria.uniqueResult()).intValue();\r
+        return (Long) criteria.uniqueResult();\r
+    }\r
+\r
+    /**\r
+     * Creates a criteria query for the CDM <code>type</code> either for counting or listing matching entities.\r
+     * <p>\r
+     * The set of matching entities can be restricted by passing a list  of {@link Restriction} objects.\r
+     * Restrictions can logically combined:\r
+     <pre>\r
+       Arrays.asList(\r
+           new Restriction<String>("titleCache", MatchMode.ANYWHERE, "foo"),\r
+           new Restriction<String>("institute.name", Operator.OR, MatchMode.BEGINNING, "Bar")\r
+       );\r
+     </pre>\r
+     * The first Restriction in the example above by default has the <code>Operator.AND</code> which will be\r
+     * ignored since this is the first restriction. The <code>Operator</code> of further restrictions in the\r
+     * list are used to combine with the previous restriction.\r
+     *\r
+     * @param type\r
+     * @param restrictions\r
+     * @param doCount\r
+     * @return\r
+     */\r
+    protected Criteria createCriteria(Class<? extends T> type, List<Restriction<?>> restrictions, boolean doCount) {\r
+\r
+        DetachedCriteria idsOnlyCriteria = DetachedCriteria.forClass(entityType(type));\r
+        idsOnlyCriteria.setProjection(Projections.distinct(Projections.id()));\r
+\r
+        addRestrictions(restrictions, idsOnlyCriteria);\r
+\r
+        Criteria criteria = criterionForType(type);\r
+        criteria.add(Subqueries.propertyIn("id", idsOnlyCriteria));\r
+\r
+        if(doCount){\r
+            criteria.setProjection(Projections.rowCount());\r
+        } else {\r
+            idsOnlyCriteria.setProjection(Projections.distinct(Projections.property("id")));\r
+        }\r
+\r
+        return criteria;\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
+    public <S extends T> List<S> findByParamWithRestrictions(Class<S> clazz, String param, String queryString,\r
+            MatchMode matchmode, List<Restriction<?>> restrictions, Integer pageSize, Integer pageNumber,\r
+            List<OrderHint> orderHints, List<String> propertyPaths) {\r
+\r
+        List<Restriction<?>> allRestrictions = new ArrayList<>();\r
+        allRestrictions.add(new Restriction<String>(param, matchmode, queryString));\r
+        if(restrictions != null){\r
+            allRestrictions.addAll(restrictions);\r
+        }\r
+        Criteria criteria = createCriteria(clazz, allRestrictions, false);\r
 \r
-        if(limit != null) {\r
-            if(start != null) {\r
-                criteria.setFirstResult(start);\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
-            criteria.setMaxResults(limit);\r
         }\r
 \r
-        addOrder(criteria,orderHints);\r
+        addOrder(criteria, orderHints);\r
 \r
         @SuppressWarnings("unchecked")\r
-               List<T> results = criteria.list();\r
+        List<S> result = criteria.list();\r
+        defaultBeanInitializer.initializeAll(result, propertyPaths);\r
+        return result;\r
+\r
+    }\r
+\r
+    @Override\r
+    public long countByParamWithRestrictions(Class<? extends T> clazz, String param, String queryString,\r
+            MatchMode matchmode, List<Restriction<?>> restrictions) {\r
+\r
+        List<Restriction<?>> allRestrictions = new ArrayList<>();\r
+        allRestrictions.add(new Restriction<String>(param, matchmode, queryString));\r
+        if(restrictions != null){\r
+            allRestrictions.addAll(restrictions);\r
+        }\r
+        Criteria criteria = createCriteria(clazz, allRestrictions, true);\r
+\r
+        return (Long) criteria.uniqueResult();\r
+    }\r
+\r
+    @Override\r
+    public <S extends T> List<S> list(S example, Set<String> includeProperties, Integer limit, Integer start,\r
+            List<OrderHint> orderHints, List<String> propertyPaths) {\r
+        Criteria criteria = getSession().createCriteria(example.getClass());\r
+        addExample(criteria, example, includeProperties);\r
+\r
+        addLimitAndStart(criteria, limit, start);\r
+\r
+        addOrder(criteria, orderHints);\r
+\r
+        @SuppressWarnings("unchecked")\r
+        List<S> results = criteria.list();\r
         defaultBeanInitializer.initializeAll(results, propertyPaths);\r
         return results;\r
     }\r
@@ -901,8 +1271,8 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
         }\r
 \r
         @Override\r
-        public boolean include(Object propertyValue, String propertyName,      Type type) {\r
-            if(includeProperties.contains(propertyName)) {\r
+        public boolean include(Object propertyValue, String propertyName, Type type) {\r
+            if (includeProperties.contains(propertyName)) {\r
                 return true;\r
             } else {\r
                 return false;\r
@@ -910,5 +1280,56 @@ public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implem
         }\r
 \r
     }\r
-}\r
 \r
+    private class CriterionWithOperator {\r
+\r
+        Restriction.Operator operator;\r
+        Criterion criterion;\r
+\r
+\r
+        public CriterionWithOperator(Operator operator, Criterion criterion) {\r
+            super();\r
+            this.operator = operator;\r
+            this.criterion = criterion;\r
+        }\r
+\r
+\r
+    }\r
+\r
+    /**\r
+     * Returns a Criteria for the given {@link Class class} or, if\r
+     * <code>null</code>, for the base {@link Class class} of this DAO.\r
+     *\r
+     * @param clazz\r
+     * @return the Criteria\r
+     */\r
+    protected Criteria getCriteria(Class<? extends CdmBase> clazz) {\r
+        Criteria criteria = null;\r
+        if (clazz == null) {\r
+            criteria = getSession().createCriteria(type);\r
+        } else {\r
+            criteria = getSession().createCriteria(clazz);\r
+        }\r
+        return criteria;\r
+    }\r
+\r
+    /**\r
+     * @param clazz\r
+     * @param auditEvent\r
+     * @return\r
+     */\r
+    protected AuditQuery makeAuditQuery(Class<? extends CdmBase> clazz, AuditEvent auditEvent) {\r
+        AuditQuery query = null;\r
+\r
+        if (clazz == null) {\r
+            query = getAuditReader().createQuery().forEntitiesAtRevision(type, auditEvent.getRevisionNumber());\r
+        } else {\r
+            query = getAuditReader().createQuery().forEntitiesAtRevision(clazz, auditEvent.getRevisionNumber());\r
+        }\r
+        return query;\r
+    }\r
+\r
+    protected AuditReader getAuditReader() {\r
+        return AuditReaderFactory.get(getSession());\r
+    }\r
+}\r