This is an automated email from the ASF dual-hosted git repository.

solomax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openmeetings.git


The following commit(s) were added to refs/heads/master by this push:
     new 8f499d303 [OPENMEETINGS-2733] query construction is improved
8f499d303 is described below

commit 8f499d303fc95ae8eb9374832be9103d3c9719c4
Author: Maxim Solodovnik <[email protected]>
AuthorDate: Tue May 17 00:57:13 2022 +0700

    [OPENMEETINGS-2733] query construction is improved
---
 .../openmeetings/db/dao/IDataProviderDao.java      |   3 +-
 .../db/dao/IGroupAdminDataProviderDao.java         |   5 +-
 .../db/dao/basic/ConfigurationDao.java             |  14 +-
 .../openmeetings/db/dao/basic/MailMessageDao.java  |  29 +--
 .../db/dao/calendar/AppointmentDao.java            |   3 +-
 .../db/dao/calendar/OmCalendarDao.java             |   3 +-
 .../openmeetings/db/dao/file/BaseFileItemDao.java  |   3 +-
 .../apache/openmeetings/db/dao/label/LabelDao.java |   2 +-
 .../openmeetings/db/dao/room/ExtraMenuDao.java     |  51 ++----
 .../openmeetings/db/dao/room/InvitationDao.java    |  76 ++++----
 .../apache/openmeetings/db/dao/room/RoomDao.java   |  44 +++--
 .../openmeetings/db/dao/server/LdapConfigDao.java  |  11 +-
 .../openmeetings/db/dao/server/OAuth2Dao.java      |  12 +-
 .../apache/openmeetings/db/dao/user/GroupDao.java  |  43 +++--
 .../openmeetings/db/dao/user/GroupUserDao.java     |  27 ++-
 .../db/dao/user/PrivateMessageDao.java             |   3 +-
 .../db/dao/user/PrivateMessageFolderDao.java       |   3 +-
 .../apache/openmeetings/db/dao/user/UserDao.java   | 195 ++++++++++----------
 .../openmeetings/db/entity/room/Invitation.java    |   8 -
 .../org/apache/openmeetings/db/util/DaoHelper.java | 204 ++++++++++++++-------
 .../web/admin/groups/GroupUsersPanel.java          |   2 +-
 .../web/data/SearchableDataProvider.java           |  10 +-
 .../web/data/SearchableGroupAdminDataProvider.java |   4 +-
 .../web/user/profile/InvitationsPanel.java         |   6 +-
 .../web/user/profile/UserSearchPanel.java          |  10 +-
 .../openmeetings/webservice/GroupWebService.java   |   3 +-
 26 files changed, 419 insertions(+), 355 deletions(-)

diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/IDataProviderDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/IDataProviderDao.java
index 228c4b369..a18d987a4 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/IDataProviderDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/IDataProviderDao.java
@@ -21,6 +21,7 @@ package org.apache.openmeetings.db.dao;
 import java.util.List;
 
 import org.apache.openmeetings.db.entity.IDataProviderEntity;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.springframework.transaction.annotation.Transactional;
 
 /**
@@ -62,7 +63,7 @@ public interface IDataProviderDao<T extends 
IDataProviderEntity> {
         * @param order - column and sort order
         * @return list of instances in the range specified
         */
-       List<T> get(String search, long start, long count, String order);
+       List<T> get(String search, long start, long count, SortParam<String> 
order);
 
        /**
         * Count the number of instances of {@link T}
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/IGroupAdminDataProviderDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/IGroupAdminDataProviderDao.java
index 2eed8571f..2bc825754 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/IGroupAdminDataProviderDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/IGroupAdminDataProviderDao.java
@@ -21,6 +21,7 @@ package org.apache.openmeetings.db.dao;
 import java.util.List;
 
 import org.apache.openmeetings.db.entity.IDataProviderEntity;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 
 public interface IGroupAdminDataProviderDao<T extends IDataProviderEntity> 
extends IDataProviderDao<T> {
        /**
@@ -33,7 +34,7 @@ public interface IGroupAdminDataProviderDao<T extends 
IDataProviderEntity> exten
         * @param order - column and sort order
         * @return list of instances in the range specified
         */
-       List<T> adminGet(String search, Long adminId, long start, long count, 
String order);
+       List<T> adminGet(String search, Long adminId, long start, long count, 
SortParam<String> order);
 
        /**
         * Get a list of instances of {@link T}
@@ -44,7 +45,7 @@ public interface IGroupAdminDataProviderDao<T extends 
IDataProviderEntity> exten
         * @param order - column and sort order
         * @return list of instances in the range specified
         */
-       default List<T> adminGet(String search, long start, long count, String 
order) {
+       default List<T> adminGet(String search, long start, long count, 
SortParam<String> order) {
                return get(search, start, count, order);
        }
 
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java
index e3fcf6c81..6220fbcd7 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java
@@ -20,8 +20,8 @@ package org.apache.openmeetings.db.dao.basic;
 
 import static org.apache.commons.lang3.math.NumberUtils.toInt;
 import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
-import static org.apache.openmeetings.util.OpenmeetingsVariables.*;
 import static org.apache.openmeetings.util.OmVersion.getLine;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.*;
 import static org.apache.wicket.csp.CSPDirectiveSrcValue.SELF;
 import static org.apache.wicket.csp.CSPDirectiveSrcValue.STRICT_DYNAMIC;
 
@@ -34,7 +34,6 @@ import java.util.TimeZone;
 
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
-import javax.persistence.TypedQuery;
 
 import org.apache.openjpa.conf.OpenJPAConfiguration;
 import org.apache.openjpa.event.RemoteCommitProvider;
@@ -51,6 +50,7 @@ import org.apache.openmeetings.util.crypt.CryptProvider;
 import org.apache.wicket.Application;
 import org.apache.wicket.csp.CSPDirective;
 import org.apache.wicket.csp.CSPHeaderConfiguration;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.apache.wicket.protocol.http.WebApplication;
 import org.apache.wicket.util.string.Strings;
 import org.slf4j.Logger;
@@ -79,7 +79,7 @@ import com.github.openjson.JSONObject;
 @Transactional
 public class ConfigurationDao implements IDataProviderDao<Configuration> {
        private static final Logger log = 
LoggerFactory.getLogger(ConfigurationDao.class);
-       private static final String[] searchFields = {"key", "value"};
+       private static final List<String> searchFields = List.of("key", 
"value");
 
        @PersistenceContext
        private EntityManager em;
@@ -202,9 +202,8 @@ public class ConfigurationDao implements 
IDataProviderDao<Configuration> {
        }
 
        @Override
-       public List<Configuration> get(String search, long start, long count, 
String sort) {
-               return 
setLimits(em.createQuery(DaoHelper.getSearchQuery("Configuration", "c", search, 
true, false, sort, searchFields), Configuration.class)
-                               , start, count).getResultList();
+       public List<Configuration> get(String search, long start, long count, 
SortParam<String> sort) {
+               return DaoHelper.get(em, Configuration.class, true, search, 
searchFields, false, null, sort, start, count);
        }
 
        @Override
@@ -214,8 +213,7 @@ public class ConfigurationDao implements 
IDataProviderDao<Configuration> {
 
        @Override
        public long count(String search) {
-               TypedQuery<Long> q = 
em.createQuery(DaoHelper.getSearchQuery("Configuration", "c", search, true, 
true, null, searchFields), Long.class);
-               return q.getSingleResult();
+               return DaoHelper.count(em, Configuration.class, search, 
searchFields, true, null);
        }
 
        @Override
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/MailMessageDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/MailMessageDao.java
index cf75fe9cb..b954828ab 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/MailMessageDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/MailMessageDao.java
@@ -26,18 +26,19 @@ import java.util.List;
 
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
-import javax.persistence.TypedQuery;
 
 import org.apache.openmeetings.db.dao.IDataProviderDao;
 import org.apache.openmeetings.db.entity.basic.MailMessage;
 import org.apache.openmeetings.db.entity.basic.MailMessage.Status;
-import org.apache.wicket.util.string.Strings;
+import org.apache.openmeetings.db.util.DaoHelper;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 
 @Repository
 @Transactional
 public class MailMessageDao implements IDataProviderDao<MailMessage> {
+       private static final List<String> searchFields = List.of("recipients", 
"subject", "body", "lastError");
        @PersistenceContext
        private EntityManager em;
 
@@ -57,27 +58,9 @@ public class MailMessageDao implements 
IDataProviderDao<MailMessage> {
                                , start, count).getResultList();
        }
 
-       private <T> TypedQuery<T> getQuery(boolean isCount, String search, 
String order, Class<T> clazz) {
-               StringBuilder sb = new StringBuilder("SELECT ");
-               sb.append(isCount ? "COUNT(m)" : "m")
-                       .append(" FROM MailMessage m");
-               if (!Strings.isEmpty(search)) {
-                       sb.append(" WHERE m.recipients LIKE :search OR 
m.subject LIKE :search OR m.body LIKE :search OR m.lastError LIKE :search");
-               }
-               if (!Strings.isEmpty(order)) {
-                       sb.append(" ORDER BY m.").append(order);
-               }
-               TypedQuery<T> q = em.createQuery(sb.toString(), clazz);
-               if (!Strings.isEmpty(search)) {
-                       q.setParameter("search", String.format("%%%s%%", 
search));
-               }
-               return q;
-       }
-
        @Override
-       public List<MailMessage> get(String search, long start, long count, 
String order) {
-               return setLimits(getQuery(false, search, order, 
MailMessage.class)
-                               , start, count).getResultList();
+       public List<MailMessage> get(String search, long start, long count, 
SortParam<String> sort) {
+               return DaoHelper.get(em, MailMessage.class, false, search, 
searchFields, false, null, sort, start, count);
        }
 
        @Override
@@ -87,7 +70,7 @@ public class MailMessageDao implements 
IDataProviderDao<MailMessage> {
 
        @Override
        public long count(String search) {
-               return getQuery(true, search, null, 
Long.class).getSingleResult();
+               return DaoHelper.count(em, MailMessage.class, search, 
searchFields, false, null);
        }
 
        public void resetSendingStatus(Calendar date) {
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/calendar/AppointmentDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/calendar/AppointmentDao.java
index c68f4af99..0ad2a752b 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/calendar/AppointmentDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/calendar/AppointmentDao.java
@@ -45,6 +45,7 @@ import 
org.apache.openmeetings.db.entity.calendar.MeetingMember;
 import org.apache.openmeetings.db.entity.room.Invitation.MessageType;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.manager.IInvitationManager;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -285,7 +286,7 @@ public class AppointmentDao implements 
IDataProviderDao<Appointment>{
        }
 
        @Override
-       public List<Appointment> get(String search, long start, long count, 
String order) {
+       public List<Appointment> get(String search, long start, long count, 
SortParam<String> order) {
                throw UNSUPPORTED;
        }
 
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/calendar/OmCalendarDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/calendar/OmCalendarDao.java
index 29c2802d7..48fe917b5 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/calendar/OmCalendarDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/calendar/OmCalendarDao.java
@@ -28,6 +28,7 @@ import javax.persistence.PersistenceContext;
 
 import org.apache.openmeetings.db.dao.IDataProviderDao;
 import org.apache.openmeetings.db.entity.calendar.OmCalendar;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
@@ -117,7 +118,7 @@ public class OmCalendarDao implements 
IDataProviderDao<OmCalendar> {
        }
 
        @Override
-       public List<OmCalendar> get(String search, long start, long count, 
String order) {
+       public List<OmCalendar> get(String search, long start, long count, 
SortParam<String> order) {
                throw UNSUPPORTED;
        }
 
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/file/BaseFileItemDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/file/BaseFileItemDao.java
index 46d0148d9..9ef4ce279 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/file/BaseFileItemDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/file/BaseFileItemDao.java
@@ -34,6 +34,7 @@ import org.apache.openmeetings.db.entity.file.BaseFileItem;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.user.Group;
 import org.apache.openmeetings.db.entity.user.User;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.apache.wicket.util.string.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -139,7 +140,7 @@ public class BaseFileItemDao implements 
IDataProviderDao<BaseFileItem> {
        }
 
        @Override
-       public List<BaseFileItem> get(String search, long start, long count, 
String order) {
+       public List<BaseFileItem> get(String search, long start, long count, 
SortParam<String> order) {
                throw UNSUPPORTED;
        }
 
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/label/LabelDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/label/LabelDao.java
index 71c9f85de..11da22897 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/label/LabelDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/label/LabelDao.java
@@ -212,7 +212,7 @@ public class LabelDao implements 
IDataProviderDao<StringLabel>{
        }
 
        @Override
-       public List<StringLabel> get(String search, long start, long count, 
String order) {
+       public List<StringLabel> get(String search, long start, long count, 
SortParam<String> order) {
                throw UNSUPPORTED;
        }
 
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/ExtraMenuDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/ExtraMenuDao.java
index 39519fc01..814c2d2c1 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/ExtraMenuDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/ExtraMenuDao.java
@@ -18,36 +18,34 @@
  */
 package org.apache.openmeetings.db.dao.room;
 
-import static org.apache.openmeetings.db.util.DaoHelper.getSearchQuery;
+import static org.apache.openmeetings.db.util.DaoHelper.getRoot;
 import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
 import static org.apache.openmeetings.db.util.DaoHelper.single;
 
 import java.util.List;
-import java.util.stream.Collectors;
 
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
 
 import org.apache.openmeetings.db.dao.IGroupAdminDataProviderDao;
-import org.apache.openmeetings.db.dao.user.UserDao;
 import org.apache.openmeetings.db.entity.room.ExtraMenu;
-import org.apache.openmeetings.db.entity.user.Group;
-import org.apache.openmeetings.db.entity.user.GroupUser;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.apache.openmeetings.db.util.DaoHelper;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 
 @Repository
 @Transactional
 public class ExtraMenuDao implements IGroupAdminDataProviderDao<ExtraMenu> {
-       private static final String[] searchFields = {"name", "link", 
"description"};
+       private static final List<String> searchFields = List.of("name", 
"link", "description");
 
        @PersistenceContext
        private EntityManager em;
 
-       @Autowired
-       private UserDao userDao;
-
        @Override
        public ExtraMenu get(Long id) {
                return single(em.createNamedQuery("getExtraMenuById", 
ExtraMenu.class)
@@ -65,11 +63,8 @@ public class ExtraMenuDao implements 
IGroupAdminDataProviderDao<ExtraMenu> {
        }
 
        @Override
-       public List<ExtraMenu> get(String search, long start, long count, 
String order) {
-               return setLimits(
-                               em.createQuery(getSearchQuery("ExtraMenu", "m", 
search, false, false, order, searchFields)
-                                               , ExtraMenu.class)
-                               , start, count).getResultList();
+       public List<ExtraMenu> get(String search, long start, long count, 
SortParam<String> sort) {
+               return DaoHelper.get(em, ExtraMenu.class, false, search, 
searchFields, false, null, sort, start, count);
        }
 
        public List<ExtraMenu> getByGroups(List<Long> groups) {
@@ -85,8 +80,7 @@ public class ExtraMenuDao implements 
IGroupAdminDataProviderDao<ExtraMenu> {
 
        @Override
        public long count(String search) {
-               return em.createQuery(getSearchQuery("ExtraMenu", "m", search, 
false, true, null, searchFields), Long.class)
-                               .getSingleResult();
+               return DaoHelper.count(em, ExtraMenu.class, search, 
searchFields, false, null);
        }
 
        @Override
@@ -105,25 +99,20 @@ public class ExtraMenuDao implements 
IGroupAdminDataProviderDao<ExtraMenu> {
        }
 
        @Override
-       public List<ExtraMenu> adminGet(String search, Long adminId, long 
start, long count, String order) {
-               final String additionalWhere = getGroupFilter(adminId);
-               return setLimits(em.createQuery(getSearchQuery("ExtraMenu", 
"m", null, search, false, false, additionalWhere, order, searchFields), 
ExtraMenu.class)
-                               , start, count).getResultList();
+       public List<ExtraMenu> adminGet(String search, Long adminId, long 
start, long count, SortParam<String> sort) {
+               return DaoHelper.get(em, ExtraMenu.class, true, search, 
searchFields, false
+                               , (builder, query) -> getGroupFilter(adminId, 
builder, query)
+                               , sort, start, count);
        }
 
        @Override
        public long adminCount(String search, Long adminId) {
-               final String additionalWhere = getGroupFilter(adminId);
-               return em.createQuery(getSearchQuery("ExtraMenu", "m", null, 
search, false, true, additionalWhere, null, searchFields), Long.class)
-                               .getSingleResult();
+               return DaoHelper.count(em, ExtraMenu.class, search, 
searchFields, false
+                               , (builder, query) -> getGroupFilter(adminId, 
builder, query));
        }
 
-       private String getGroupFilter(Long adminId) {
-               return userDao.get(adminId).getGroupUsers().stream()
-                               .filter(GroupUser::isModerator)
-                               .map(GroupUser::getGroup)
-                               .map(Group::getId)
-                               .map(String::valueOf)
-                               .collect(Collectors.joining(", ", "m.groups IN 
(", ")"));
+       private Predicate getGroupFilter(Long adminId, CriteriaBuilder builder, 
CriteriaQuery<?> query) {
+               Root<ExtraMenu> root = getRoot(query, ExtraMenu.class);
+               return 
builder.in(root.get("groups")).value(DaoHelper.groupAdminQuery(adminId, 
builder, query));
        }
 }
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/InvitationDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/InvitationDao.java
index e48fe34a2..7c7aa4972 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/InvitationDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/InvitationDao.java
@@ -18,16 +18,8 @@
  */
 package org.apache.openmeetings.db.dao.room;
 
-import static org.apache.openmeetings.db.entity.room.Invitation.BY_ALL;
-import static org.apache.openmeetings.db.entity.room.Invitation.BY_GROUP;
-import static org.apache.openmeetings.db.entity.room.Invitation.BY_USER;
-import static org.apache.openmeetings.db.entity.room.Invitation.SELECT_COUNT;
-import static org.apache.openmeetings.db.entity.room.Invitation.SELECT_I;
-import static org.apache.openmeetings.db.util.DaoHelper.appendSort;
-import static org.apache.openmeetings.db.util.DaoHelper.appendWhereClause;
-import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
+import static org.apache.openmeetings.db.util.DaoHelper.getRoot;
 import static org.apache.openmeetings.util.CalendarHelper.getZoneId;
-import static org.apache.openmeetings.util.OpenmeetingsVariables.PARAM_USER_ID;
 
 import java.time.LocalDateTime;
 import java.time.ZonedDateTime;
@@ -36,14 +28,22 @@ import java.util.TimeZone;
 
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import javax.persistence.criteria.Subquery;
 
 import org.apache.openmeetings.db.dao.IDataProviderDao;
 import org.apache.openmeetings.db.entity.record.Recording;
 import org.apache.openmeetings.db.entity.room.Invitation;
 import org.apache.openmeetings.db.entity.room.Invitation.Valid;
 import org.apache.openmeetings.db.entity.room.Room;
+import org.apache.openmeetings.db.entity.user.GroupUser;
 import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.db.util.DaoHelper;
 import org.apache.openmeetings.util.CalendarHelper;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.apache.wicket.util.string.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -53,6 +53,7 @@ import 
org.springframework.transaction.annotation.Transactional;
 @Repository
 @Transactional
 public class InvitationDao implements IDataProviderDao<Invitation> {
+       private static final List<String> searchFields = 
List.of("invitee.firstname", "invitee.lastname", "invitee.login");
        private static final Logger log = 
LoggerFactory.getLogger(InvitationDao.class);
 
        @PersistenceContext
@@ -70,21 +71,9 @@ public class InvitationDao implements 
IDataProviderDao<Invitation> {
                return get(null, start, count, null);
        }
 
-       private static String getQuery(String head, String tail, String search) 
{
-               return getQuery(head, tail, search, null);
-       }
-
-       private static String getQuery(String head, String tail, String search, 
String sort) {
-               StringBuilder sb = new StringBuilder(head);
-               sb.append(tail);
-               appendWhereClause(sb, search, "i", "invitee.firstname", 
"invitee.lastname", "invitee.login");
-               return appendSort(sb, "i", sort).toString();
-       }
-
        @Override
-       public List<Invitation> get(String search, long start, long count, 
String order) {
-               return setLimits(em.createQuery(getQuery(SELECT_I, BY_ALL, 
search, order), Invitation.class)
-                               , start, count).getResultList();
+       public List<Invitation> get(String search, long start, long count, 
SortParam<String> sort) {
+               return DaoHelper.get(em, Invitation.class, false, search, 
searchFields, true, null, sort, start, count);
        }
 
        @Override
@@ -94,29 +83,44 @@ public class InvitationDao implements 
IDataProviderDao<Invitation> {
 
        @Override
        public long count(String search) {
-               return em.createQuery(getQuery(SELECT_COUNT, BY_ALL, search), 
Long.class).getSingleResult();
+               return DaoHelper.count(em, Invitation.class, search, 
searchFields, true, null);
        }
 
-       public List<Invitation> getGroup(String search, long start, long count, 
Long userId, String order) {
-               return setLimits(em.createQuery(getQuery(SELECT_I, BY_GROUP, 
search, order), Invitation.class)
-                                       .setParameter(PARAM_USER_ID, userId)
-                               , start, count).getResultList();
+       private Predicate getGroupFilter(Long userId, CriteriaBuilder builder, 
CriteriaQuery<?> query) {
+               Subquery<Long> subquery = query.subquery(Long.class);
+               Root<GroupUser> root = subquery.from(GroupUser.class);
+               subquery.select(root.get("user").get("id"));
+               
subquery.where(builder.in(root.get("group").get("id")).value(DaoHelper.groupAdminQuery(userId,
 builder, subquery)));
+
+               Root<Invitation> mainRoot = getRoot(query, Invitation.class);
+               return 
builder.in(mainRoot.get("invitedBy").get("id")).value(subquery);
+       }
+
+       public List<Invitation> getGroup(String search, long start, long count, 
Long userId, SortParam<String> sort) {
+               return DaoHelper.get(em, Invitation.class, false, search, 
searchFields, true
+                               , (builder, query) -> getGroupFilter(userId, 
builder, query)
+                               , sort, start, count);
        }
 
        public long countGroup(String search, Long userId) {
-               return em.createQuery(getQuery(SELECT_COUNT, BY_GROUP, search), 
Long.class)
-                               .setParameter(PARAM_USER_ID, 
userId).getSingleResult();
+               return DaoHelper.count(em, Invitation.class, search, 
searchFields, true
+                               , (builder, query) -> getGroupFilter(userId, 
builder, query));
+       }
+
+       private Predicate getUserFilter(Long userId, CriteriaBuilder builder, 
CriteriaQuery<?> query) {
+               Root<Invitation> root = getRoot(query, Invitation.class);
+               return builder.equal(root.get("invitedBy").get("id"), userId);
        }
 
-       public List<Invitation> getUser(String search, long start, long count, 
Long userId, String order) {
-               return setLimits(em.createQuery(getQuery(SELECT_I, BY_USER, 
search, order), Invitation.class)
-                                       .setParameter(PARAM_USER_ID, userId)
-                               , start, count).getResultList();
+       public List<Invitation> getUser(String search, long start, long count, 
Long userId, SortParam<String> sort) {
+               return DaoHelper.get(em, Invitation.class, false, search, 
searchFields, true
+                               , (builder, query) -> getUserFilter(userId, 
builder, query)
+                               , sort, start, count);
        }
 
        public long countUser(String search, Long userId) {
-               return em.createQuery(getQuery(SELECT_COUNT, BY_USER, search), 
Long.class)
-                               .setParameter(PARAM_USER_ID, 
userId).getSingleResult();
+               return DaoHelper.count(em, Invitation.class, search, 
searchFields, true
+                               , (builder, query) -> getUserFilter(userId, 
builder, query));
        }
 
        public Invitation update(Invitation invitation) {
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/RoomDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/RoomDao.java
index 9231dac7b..9eed7c2fb 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/RoomDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/RoomDao.java
@@ -19,6 +19,7 @@
 package org.apache.openmeetings.db.dao.room;
 
 import static org.apache.openmeetings.db.util.DaoHelper.fillLazy;
+import static org.apache.openmeetings.db.util.DaoHelper.getRoot;
 import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
 import static org.apache.openmeetings.db.util.DaoHelper.single;
 import static org.apache.openmeetings.db.util.TimezoneUtil.getTimeZone;
@@ -37,6 +38,10 @@ import java.util.TimeZone;
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
 import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
 
 import org.apache.openmeetings.db.dao.IGroupAdminDataProviderDao;
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
@@ -49,6 +54,7 @@ import org.apache.openmeetings.db.entity.room.RoomFile;
 import org.apache.openmeetings.db.entity.room.RoomGroup;
 import org.apache.openmeetings.db.manager.ISipManager;
 import org.apache.openmeetings.db.util.DaoHelper;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.apache.wicket.util.string.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -60,7 +66,7 @@ import 
org.springframework.transaction.annotation.Transactional;
 @Transactional
 public class RoomDao implements IGroupAdminDataProviderDao<Room> {
        private static final Logger log = 
LoggerFactory.getLogger(RoomDao.class);
-       private static final String[] searchFields = {"name", "comment"};
+       private static final List<String> searchFields = List.of("name", 
"comment");
        public static final String GRP_MODERATORS = "roomModerators";
        public static final String GRP_GROUPS = "roomGroups";
        public static final String GRP_FILES = "roomFiles";
@@ -117,37 +123,41 @@ public class RoomDao implements 
IGroupAdminDataProviderDao<Room> {
        }
 
        @Override
-       public List<Room> get(String search, long start, long count, String 
sort) {
-               return 
setLimits(em.createQuery(DaoHelper.getSearchQuery("Room", "r", search, true, 
false, sort, searchFields), Room.class)
-                               , start, count).getResultList();
+       public List<Room> get(String search, long start, long count, 
SortParam<String> sort) {
+               return DaoHelper.get(em, Room.class, false, search, 
searchFields, true, null, sort, start, count);
+       }
+
+       private Predicate getAdminFilter(Long adminId, CriteriaBuilder builder, 
CriteriaQuery<?> query) {
+               Root<RoomGroup> root = getRoot(query, RoomGroup.class);
+               return 
builder.in(root.get("group").get("id")).value(DaoHelper.groupAdminQuery(adminId,
 builder, query));
        }
 
        @Override
-       public List<Room> adminGet(String search, Long adminId, long start, 
long count, String order) {
-               return 
setLimits(em.createQuery(DaoHelper.getSearchQuery("RoomGroup rg, IN(rg.room)", 
"r", null, search, true, true, false
-                               , "rg.group.id IN (SELECT gu1.group.id FROM 
GroupUser gu1 WHERE gu1.moderator = true AND gu1.user.id = :adminId)", order, 
searchFields), Room.class)
-                                       .setParameter("adminId", adminId)
-                               , start, count).getResultList();
+       public List<Room> adminGet(String search, Long adminId, long start, 
long count, SortParam<String> sort) {
+               return DaoHelper.get(em, RoomGroup.class, Room.class
+                               , (builder, root) -> root.get("room")
+                               , true, search, searchFields, false
+                               , (b, q) -> getAdminFilter(adminId, b, q)
+                               , sort, start, count);
        }
 
        @Override
        public long count() {
-               TypedQuery<Long> q = em.createNamedQuery("countRooms", 
Long.class);
-               return q.getSingleResult();
+               return em.createNamedQuery("countRooms", Long.class)
+                               .getSingleResult();
        }
 
        @Override
        public long count(String search) {
-               TypedQuery<Long> q = 
em.createQuery(DaoHelper.getSearchQuery("Room", "r", search, true, true, null, 
searchFields), Long.class);
-               return q.getSingleResult();
+               return DaoHelper.count(em, Room.class, search, searchFields, 
true, null);
        }
 
        @Override
        public long adminCount(String search, Long adminId) {
-               TypedQuery<Long> q = 
em.createQuery(DaoHelper.getSearchQuery("RoomGroup rg, IN(rg.room)", "r", null, 
search, true, true, true
-                               , "rg.group.id IN (SELECT gu1.group.id FROM 
GroupUser gu1 WHERE gu1.moderator = true AND gu1.user.id = :adminId)", null, 
searchFields), Long.class);
-               q.setParameter("adminId", adminId);
-               return q.getSingleResult();
+               return DaoHelper.count(em, RoomGroup.class
+                               , (builder, root) -> 
builder.countDistinct(root.get("room"))
+                               , search, searchFields, false
+                               , (b, q) -> getAdminFilter(adminId, b, q));
        }
 
        public List<Room> getPublicRooms() {
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/LdapConfigDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/LdapConfigDao.java
index efc5a04c8..91cb12e1e 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/LdapConfigDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/LdapConfigDao.java
@@ -32,6 +32,7 @@ import org.apache.openmeetings.db.dao.IDataProviderDao;
 import org.apache.openmeetings.db.dao.user.UserDao;
 import org.apache.openmeetings.db.entity.server.LdapConfig;
 import org.apache.openmeetings.db.util.DaoHelper;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -49,7 +50,7 @@ import 
org.springframework.transaction.annotation.Transactional;
 @Transactional
 public class LdapConfigDao implements IDataProviderDao<LdapConfig> {
        private static final Logger log = 
LoggerFactory.getLogger(LdapConfigDao.class);
-       private static final String[] searchFields = {"name", "configFileName", 
"domain", "comment"};
+       private static final List<String> searchFields = List.of("name", 
"configFileName", "domain", "comment");
 
        @PersistenceContext
        private EntityManager em;
@@ -94,9 +95,8 @@ public class LdapConfigDao implements 
IDataProviderDao<LdapConfig> {
        }
 
        @Override
-       public List<LdapConfig> get(String search, long start, long count, 
String sort) {
-               return 
setLimits(em.createQuery(DaoHelper.getSearchQuery("LdapConfig", "lc", search, 
true, false, sort, searchFields), LdapConfig.class)
-                               , start, count).getResultList();
+       public List<LdapConfig> get(String search, long start, long count, 
SortParam<String> sort) {
+               return DaoHelper.get(em, LdapConfig.class, false, search, 
searchFields, true, null, sort, start, count);
        }
 
        @Override
@@ -114,8 +114,7 @@ public class LdapConfigDao implements 
IDataProviderDao<LdapConfig> {
 
        @Override
        public long count(String search) {
-               TypedQuery<Long> q = 
em.createQuery(DaoHelper.getSearchQuery("LdapConfig", "lc", search, true, true, 
null, searchFields), Long.class);
-               return q.getSingleResult();
+               return DaoHelper.count(em, LdapConfig.class, search, 
searchFields, true, null);
        }
 
        @Override
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/OAuth2Dao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/OAuth2Dao.java
index 2899c82e5..c22823bb4 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/OAuth2Dao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/OAuth2Dao.java
@@ -30,8 +30,10 @@ import javax.persistence.TypedQuery;
 
 import org.apache.openmeetings.db.dao.IDataProviderDao;
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
+import org.apache.openmeetings.db.entity.server.LdapConfig;
 import org.apache.openmeetings.db.entity.server.OAuthServer;
 import org.apache.openmeetings.db.util.DaoHelper;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
@@ -39,7 +41,7 @@ import 
org.springframework.transaction.annotation.Transactional;
 @Repository
 @Transactional
 public class OAuth2Dao implements IDataProviderDao<OAuthServer> {
-       private static final String[] searchFields = {"name"};
+       private static final List<String> searchFields = List.of("name");
        @PersistenceContext
        private EntityManager em;
        @Autowired
@@ -67,9 +69,8 @@ public class OAuth2Dao implements 
IDataProviderDao<OAuthServer> {
        }
 
        @Override
-       public List<OAuthServer> get(String search, long start, long count, 
String order) {
-               return 
setLimits(em.createQuery(DaoHelper.getSearchQuery("OAuthServer", "s", search, 
true, false, null, searchFields), OAuthServer.class)
-                               , start, count).getResultList();
+       public List<OAuthServer> get(String search, long start, long count, 
SortParam<String> sort) {
+               return DaoHelper.get(em, OAuthServer.class, false, search, 
searchFields, true, null, sort, start, count);
        }
 
        @Override
@@ -80,8 +81,7 @@ public class OAuth2Dao implements 
IDataProviderDao<OAuthServer> {
 
        @Override
        public long count(String search) {
-               TypedQuery<Long> q = 
em.createQuery(DaoHelper.getSearchQuery("OAuthServer", "s", search, true, true, 
null, searchFields), Long.class);
-               return q.getSingleResult();
+               return DaoHelper.count(em, LdapConfig.class, search, 
searchFields, true, null);
        }
 
        @Override
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/GroupDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/GroupDao.java
index 1fbfae693..6acb8fd8f 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/GroupDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/GroupDao.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.db.dao.user;
 
+import static org.apache.openmeetings.db.util.DaoHelper.getRoot;
 import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
 
 import java.util.Collection;
@@ -25,17 +26,23 @@ import java.util.List;
 
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
 
 import org.apache.openmeetings.db.dao.IGroupAdminDataProviderDao;
 import org.apache.openmeetings.db.entity.user.Group;
+import org.apache.openmeetings.db.entity.user.GroupUser;
 import org.apache.openmeetings.db.util.DaoHelper;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 
 @Repository
 @Transactional
 public class GroupDao implements IGroupAdminDataProviderDao<Group> {
-       private static final String[] searchFields = {"name"};
+       private static final List<String> searchFields = List.of("name");
        @PersistenceContext
        private EntityManager em;
 
@@ -67,17 +74,24 @@ public class GroupDao implements 
IGroupAdminDataProviderDao<Group> {
        }
 
        @Override
-       public List<Group> get(String search, long start, long count, String 
sort) {
-               return 
setLimits(em.createQuery(DaoHelper.getSearchQuery("Group", "g", search, true, 
false, sort, searchFields), Group.class)
-                               , start, count).getResultList();
+       public List<Group> get(String search, long start, long count, 
SortParam<String> sort) {
+               return DaoHelper.get(em, Group.class, false, search, 
searchFields, true
+                               , null
+                               , sort, start, count);
+       }
+
+       private Predicate getAdminFilter(Long adminId, CriteriaBuilder builder, 
CriteriaQuery<?> query) {
+               Root<GroupUser> root = getRoot(query, GroupUser.class);
+               return builder.and(builder.equal(root.get("user").get("id"), 
adminId), builder.isTrue(root.get("moderator")));
        }
 
        @Override
-       public List<Group> adminGet(String search, Long adminId, long start, 
long count, String order) {
-               return 
setLimits(em.createQuery(DaoHelper.getSearchQuery("GroupUser gu, IN(gu.group)", 
"g", null, search, true, true, false
-                               , "gu.user.id = :adminId AND gu.moderator = 
true", order, searchFields), Group.class)
-                                       .setParameter("adminId", adminId)
-                               , start, count).getResultList();
+       public List<Group> adminGet(String search, Long adminId, long start, 
long count, SortParam<String> sort) {
+               return DaoHelper.get(em, GroupUser.class, Group.class
+                               , (builder, root) -> root.get("group")
+                               , true, search, searchFields, true
+                               , (b, q) -> getAdminFilter(adminId, b, q)
+                               , sort, start, count);
        }
 
        @Override
@@ -87,16 +101,15 @@ public class GroupDao implements 
IGroupAdminDataProviderDao<Group> {
 
        @Override
        public long count(String search) {
-               return em.createQuery(DaoHelper.getSearchQuery("Group", "o", 
search, true, true, null, searchFields), Long.class)
-                               .getSingleResult();
+               return DaoHelper.count(em, Group.class, search, searchFields, 
true, null);
        }
 
        @Override
        public long adminCount(String search, Long adminId) {
-               return em.createQuery(DaoHelper.getSearchQuery("GroupUser gu, 
IN(gu.group)", "g", null, search, true, true, true
-                               , "gu.user.id = :adminId AND gu.moderator = 
true", null, searchFields), Long.class)
-                               .setParameter("adminId", adminId)
-                               .getSingleResult();
+               return DaoHelper.count(em, GroupUser.class
+                               , (builder, root) -> 
builder.countDistinct(root.get("group"))
+                               , search, searchFields, false
+                               , (b, q) -> getAdminFilter(adminId, b, 
q.distinct(true)));
        }
 
        public List<Group> get(Collection<Long> ids) {
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/GroupUserDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/GroupUserDao.java
index 542b666fd..d72e4fc4e 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/GroupUserDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/GroupUserDao.java
@@ -19,6 +19,7 @@
 package org.apache.openmeetings.db.dao.user;
 
 import static org.apache.openmeetings.db.util.DaoHelper.UNSUPPORTED;
+import static org.apache.openmeetings.db.util.DaoHelper.getRoot;
 import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
 import static org.apache.openmeetings.db.util.DaoHelper.single;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.PARAM_USER_ID;
@@ -28,18 +29,23 @@ import java.util.List;
 
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
 
 import org.apache.openmeetings.db.dao.IDataProviderDao;
 import org.apache.openmeetings.db.entity.user.GroupUser;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.util.DaoHelper;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 
 @Repository
 @Transactional
 public class GroupUserDao implements IDataProviderDao<GroupUser> {
-       private static final String[] searchFields = {"user.lastname", 
"user.firstname", "user.login", "user.address.email"};
+       private static final List<String> searchFields = 
List.of("user.lastname", "user.firstname", "user.login", "user.address.email");
        private static final String PARAM_GROUPID = "groupId";
        @PersistenceContext
        private EntityManager em;
@@ -57,15 +63,19 @@ public class GroupUserDao implements 
IDataProviderDao<GroupUser> {
        }
 
        @Override
-       public List<GroupUser> get(String search, long start, long count, 
String sort) {
+       public List<GroupUser> get(String search, long start, long count, 
SortParam<String> sort) {
                throw UNSUPPORTED;
        }
 
-       public List<GroupUser> get(long groupId, String search, long start, 
long count, String sort) {
-               return setLimits(
-                               
em.createQuery(DaoHelper.getSearchQuery(GroupUser.class.getSimpleName(), "ou", 
null, search, false, false, "ou.group.id = :groupId", sort, searchFields), 
GroupUser.class)
-                                       .setParameter(PARAM_GROUPID, groupId)
-                               , start, count).getResultList();
+       private Predicate getGroupFilter(Long groupId, CriteriaBuilder builder, 
CriteriaQuery<?> query) {
+               Root<GroupUser> root = getRoot(query, GroupUser.class);
+               return builder.equal(root.get("group").get("id"), groupId);
+       }
+
+       public List<GroupUser> get(long groupId, String search, long start, 
long count, SortParam<String> sort) {
+               return DaoHelper.get(em, GroupUser.class, false, search, 
searchFields, false
+                               , (builder, query) -> getGroupFilter(groupId, 
builder, query)
+                               , sort, start, count);
        }
 
        public List<GroupUser> get(long groupId, long start, long count) {
@@ -95,8 +105,7 @@ public class GroupUserDao implements 
IDataProviderDao<GroupUser> {
 
        @Override
        public long count(String search) {
-               return 
em.createQuery(DaoHelper.getSearchQuery(GroupUser.class.getSimpleName(), "ou", 
search, false, true, null, searchFields), Long.class)
-                               .getSingleResult();
+               return DaoHelper.count(em, GroupUser.class, search, 
searchFields, false, null);
        }
 
        public long count(long groupId) {
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/PrivateMessageDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/PrivateMessageDao.java
index 12eae4c51..51a3e3d60 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/PrivateMessageDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/PrivateMessageDao.java
@@ -35,6 +35,7 @@ import javax.persistence.TypedQuery;
 import org.apache.openmeetings.db.dao.IDataProviderDao;
 import org.apache.openmeetings.db.entity.user.PrivateMessage;
 import org.apache.openmeetings.db.entity.user.User;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.apache.wicket.util.string.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -168,7 +169,7 @@ public class PrivateMessageDao implements 
IDataProviderDao<PrivateMessage> {
        }
 
        @Override
-       public List<PrivateMessage> get(String search, long start, long count, 
String order) {
+       public List<PrivateMessage> get(String search, long start, long count, 
SortParam<String> order) {
                throw UNSUPPORTED;
        }
 
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/PrivateMessageFolderDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/PrivateMessageFolderDao.java
index 0a6702345..08fca325a 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/PrivateMessageFolderDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/PrivateMessageFolderDao.java
@@ -29,6 +29,7 @@ import javax.persistence.PersistenceContext;
 
 import org.apache.openmeetings.db.dao.IDataProviderDao;
 import org.apache.openmeetings.db.entity.user.PrivateMessageFolder;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Repository;
@@ -97,7 +98,7 @@ public class PrivateMessageFolderDao implements 
IDataProviderDao<PrivateMessageF
        }
 
        @Override
-       public List<PrivateMessageFolder> get(String search, long start, long 
count, String order) {
+       public List<PrivateMessageFolder> get(String search, long start, long 
count, SortParam<String> order) {
                throw UNSUPPORTED;
        }
 
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/UserDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/UserDao.java
index b8dc754ed..ed2e10abd 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/UserDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/UserDao.java
@@ -20,6 +20,7 @@ package org.apache.openmeetings.db.dao.user;
 
 import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.db.util.DaoHelper.fillLazy;
+import static org.apache.openmeetings.db.util.DaoHelper.getRoot;
 import static org.apache.openmeetings.db.util.DaoHelper.getStringParam;
 import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
 import static org.apache.openmeetings.db.util.DaoHelper.single;
@@ -35,22 +36,28 @@ import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
 import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.JoinType;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import javax.persistence.criteria.Subquery;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.openmeetings.db.dao.IGroupAdminDataProviderDao;
 import org.apache.openmeetings.db.dao.label.LabelDao;
 import org.apache.openmeetings.db.entity.user.Address;
 import org.apache.openmeetings.db.entity.user.AsteriskSipUser;
+import org.apache.openmeetings.db.entity.user.GroupUser;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.entity.user.User.Right;
 import org.apache.openmeetings.db.entity.user.User.Salutation;
@@ -61,6 +68,7 @@ import org.apache.openmeetings.util.OmException;
 import org.apache.openmeetings.util.OmFileHelper;
 import org.apache.openmeetings.util.crypt.CryptProvider;
 import org.apache.openmeetings.util.crypt.ICrypt;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.apache.wicket.util.string.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -78,7 +86,8 @@ import 
org.springframework.transaction.annotation.Transactional;
 public class UserDao implements IGroupAdminDataProviderDao<User> {
        private static final Logger log = 
LoggerFactory.getLogger(UserDao.class);
        private static final String PARAM_EMAIL = "email";
-       private static final String[] searchFields = {"lastname", "firstname", 
"login", "address.email", "address.town"};
+       private static final List<String> searchFields = List.of("lastname", 
"firstname", "login", "address.email", "address.town");
+       private static final List<String> guSearchFields = 
searchFields.stream().map(f -> "user." + f).collect(Collectors.toList());
        public static final String FETCH_GROUP_GROUP = "groupUsers";
        public static final String FETCH_GROUP_BACKUP = "backupexport";
 
@@ -123,81 +132,102 @@ public class UserDao implements 
IGroupAdminDataProviderDao<User> {
                                , first, count).getResultList();
        }
 
-       private static String getAdditionalJoin(boolean filterContacts) {
-               return filterContacts ? "LEFT JOIN u.groupUsers ou" : null;
+       private Predicate getContactsFilter(CriteriaBuilder builder, 
CriteriaQuery<?> query) {
+               Root<User> root = getRoot(query, User.class);
+               return builder.notEqual(root.get("type"), Type.CONTACT);
        }
 
-       private static String getAdditionalWhere(boolean excludeContacts, 
Map<String, Object> params) {
-               if (excludeContacts) {
-                       params.put("contact", Type.CONTACT);
-                       return "u.type <> :contact";
-               }
-               return null;
+       private Predicate getOwnerContactsFilter(Long ownerId, CriteriaBuilder 
builder, CriteriaQuery<?> query) {
+               Root<User> root = getRoot(query, User.class);
+               root.join("groupUsers", JoinType.LEFT);
+
+               Subquery<Long> subquery = query.subquery(Long.class);
+               Root<GroupUser> subRoot = subquery.from(GroupUser.class);
+               subquery.select(subRoot.get("group").get("id"));
+               subquery.where(builder.equal(subRoot.get("user").get("id"), 
ownerId));
+               return builder.or(
+                               builder.and(builder.notEqual(root.get("type"), 
Type.CONTACT), root.get("groupUsers").get("group").get("id").in(subquery))
+                               , builder.and(builder.equal(root.get("type"), 
Type.CONTACT), builder.equal(root.get("ownerId"), ownerId))
+                               );
        }
 
-       private static String getAdditionalWhere(boolean filterContacts, Long 
ownerId, Map<String, Object> params) {
+       private List<User> get(String search, Long start, Long count, 
SortParam<String> sort, boolean filterContacts, Long currentUserId, boolean 
filterDeleted) {
                if (filterContacts) {
-                       params.put("ownerId", ownerId);
-                       params.put("contact", Type.CONTACT);
-                       return "((u.type <> :contact AND ou.group.id IN (SELECT 
ou.group.id FROM GroupUser ou WHERE ou.user.id = :ownerId)) "
-                               + "OR (u.type = :contact AND u.ownerId = 
:ownerId))";
+                       return DaoHelper.get(em, User.class
+                                       , true, search, searchFields, true
+                                       , (b, q) -> 
getOwnerContactsFilter(currentUserId, b, q)
+                                       , sort, start, count);
+               } else {
+                       return DaoHelper.get(em, User.class, false, search, 
searchFields, filterDeleted
+                                       , null, sort, start, count);
                }
-               return null;
        }
 
-       private static void setAdditionalParams(TypedQuery<?> q, Map<String, 
Object> params) {
-               for (Map.Entry<String, Object> me: params.entrySet()) {
-                       q.setParameter(me.getKey(), me.getValue());
-               }
+       // This is AdminDao method
+       public List<User> get(String search, boolean excludeContacts, long 
start, long count) {
+               return DaoHelper.get(em, User.class, false, search, 
searchFields, true
+                               , excludeContacts ? (b, q) -> 
getContactsFilter(b, q) : null
+                               , null, start, count);
        }
 
-       private List<User> get(String search, Long start, Long count, String 
order, boolean filterContacts, Long currentUserId, boolean filterDeleted) {
-               Map<String, Object> params = new HashMap<>();
-               TypedQuery<User> q = 
em.createQuery(DaoHelper.getSearchQuery("User", "u", 
getAdditionalJoin(filterContacts), search, true, filterDeleted, false
-                               , getAdditionalWhere(filterContacts, 
currentUserId, params), order, searchFields), User.class);
-               setAdditionalParams(setLimits(q, start, count), params);
-               return q.getResultList();
+       public List<User> get(String search, long start, long count, 
SortParam<String> sort, boolean filterContacts, Long currentUserId) {
+               return get(search, start, count, sort, filterContacts, 
currentUserId, true);
        }
 
-       // This is AdminDao method
-       public List<User> get(String search, boolean excludeContacts, long 
first, long count) {
-               Map<String, Object> params = new HashMap<>();
-               TypedQuery<User> q = 
em.createQuery(DaoHelper.getSearchQuery("User", "u", null, search, true, true, 
false
-                               , getAdditionalWhere(excludeContacts, params), 
null, searchFields), User.class);
-               setAdditionalParams(setLimits(q, first, count), params);
-               return q.getResultList();
+       @Override
+       public List<User> adminGet(String search, long start, long count, 
SortParam<String> sort) {
+               return get(search, start, count, sort, false, null, false);
        }
 
-       public List<User> get(String search, long start, long count, String 
sort, boolean filterContacts, Long currentUserId) {
-               return get(search, start, count, sort, filterContacts, 
currentUserId, true);
+       private Predicate getAdminFilter(Long adminId, CriteriaBuilder builder, 
CriteriaQuery<?> query) {
+               Root<GroupUser> root = getRoot(query, GroupUser.class);
+               return 
builder.in(root.get("group").get("id")).value(DaoHelper.groupAdminQuery(adminId,
 builder, query));
        }
 
        @Override
-       public List<User> adminGet(String search, long start, long count, 
String order) {
-               return get(search, start, count, order, false, null, false);
+       public List<User> adminGet(String search, Long adminId, long start, 
long count, SortParam<String> sort) {
+               return DaoHelper.get(em, GroupUser.class, User.class
+                               , (builder, root) -> root.get("user")
+                               , true, search, guSearchFields, false
+                               , (b, q) -> getAdminFilter(adminId, b, q)
+                               , sort, start, count);
+       }
+
+       private Predicate getProfileFilter(Long userId, String userOffers, 
String userSearches, CriteriaBuilder builder, CriteriaQuery<?> query) {
+               Root<User> root = getRoot(query, User.class);
+               Predicate result = getOwnerContactsFilter(userId, builder, 
query);
+               if (!Strings.isEmpty(userOffers)) {
+                       result = builder.and(result, 
DaoHelper.like("userOffers", getStringParam(userOffers), builder, root));
+               }
+               if (!Strings.isEmpty(userSearches)) {
+                       result = builder.and(result, 
DaoHelper.like("userSearches", getStringParam(userSearches), builder, root));
+               }
+               return result;
        }
 
-       @Override
-       public List<User> adminGet(String search, Long adminId, long start, 
long count, String order) {
-               TypedQuery<User> q = 
em.createQuery(DaoHelper.getSearchQuery("GroupUser gu, IN(gu.user)", "u", null, 
search, true, false, false
-                               , "gu.group.id IN (SELECT gu1.group.id FROM 
GroupUser gu1 WHERE gu1.moderator = true AND gu1.user.id = :adminId)", order, 
searchFields), User.class);
-               q.setParameter("adminId", adminId);
-               return setLimits(q, start, count).getResultList();
+       public List<User> searchUserProfile(Long userId, String search, String 
userOffers, String userSearches, SortParam<String> sort, long start, long 
count) {
+               return DaoHelper.get(em, User.class
+                               , true, search, searchFields, true
+                               , (b, q) -> getProfileFilter(userId, 
userOffers, userSearches, b, q)
+                               , sort, start, count);
        }
 
        private long count(String search, boolean filterContacts, Long 
currentUserId, boolean filterDeleted) {
-               Map<String, Object> params = new HashMap<>();
-               TypedQuery<Long> q = 
em.createQuery(DaoHelper.getSearchQuery("User", "u", 
getAdditionalJoin(filterContacts), search, true, filterDeleted, true
-                               , getAdditionalWhere(filterContacts, 
currentUserId, params), null, searchFields), Long.class);
-               setAdditionalParams(q, params);
-               return q.getSingleResult();
+               if (filterContacts) {
+                       return DaoHelper.count(em, User.class
+                                       , (builder, root) -> 
builder.countDistinct(root)
+                                       , search, searchFields, filterDeleted
+                                       , (b, q) -> 
getOwnerContactsFilter(currentUserId, b, q));
+               } else {
+                       return DaoHelper.count(em, User.class, search, 
searchFields, filterDeleted
+                                       , null);
+               }
        }
 
        @Override
        public long count() {
-               // get all users
-               TypedQuery<Long> q = 
em.createNamedQuery("countNondeletedUsers", Long.class);
-               return q.getSingleResult();
+               return em.createNamedQuery("countNondeletedUsers", Long.class)
+                               .getSingleResult();
        }
 
        @Override
@@ -220,10 +250,17 @@ public class UserDao implements 
IGroupAdminDataProviderDao<User> {
 
        @Override
        public long adminCount(String search, Long adminId) {
-               TypedQuery<Long> q = 
em.createQuery(DaoHelper.getSearchQuery("GroupUser gu, IN(gu.user)", "u", null, 
search, true, false, true
-                               , "gu.group.id IN (SELECT gu1.group.id FROM 
GroupUser gu1 WHERE gu1.moderator = true AND gu1.user.id = :adminId)", null, 
searchFields), Long.class);
-               q.setParameter("adminId", adminId);
-               return q.getSingleResult();
+               return DaoHelper.count(em, GroupUser.class
+                               , (builder, root) -> 
builder.countDistinct(root.get("user"))
+                               , search, guSearchFields, false
+                               , (b, q) -> getAdminFilter(adminId, b, q));
+       }
+
+       public Long searchCountUserProfile(Long userId, String search, String 
userOffers, String userSearches) {
+               return DaoHelper.count(em, User.class
+                               , (builder, root) -> builder.countDistinct(root)
+                               , search, searchFields, true
+                               , (b, q) -> getProfileFilter(userId, 
userOffers, userSearches, b, q));
        }
 
        @Override
@@ -524,48 +561,6 @@ public class UserDao implements 
IGroupAdminDataProviderDao<User> {
                                , FETCH_GROUP_GROUP));
        }
 
-       private <T> TypedQuery<T> getUserProfileQuery(Class<T> clazz, Long 
userId, String text, String offers, String search, String orderBy, boolean asc) 
{
-               Map<String, Object> params = new HashMap<>();
-               boolean filterContacts = true;
-               boolean count = clazz.isAssignableFrom(Long.class);
-
-               StringBuilder sb = new StringBuilder("SELECT ");
-               sb.append(count ? "COUNT(" : "").append("DISTINCT 
u").append(count ? ") " : " ")
-                       .append("FROM User u 
").append(getAdditionalJoin(filterContacts)).append(" WHERE u.deleted = false 
AND ")
-                       .append(getAdditionalWhere(filterContacts, userId, 
params));
-               if (!Strings.isEmpty(offers)) {
-                       sb.append(" AND (LOWER(u.userOffers) LIKE :userOffers) 
");
-                       params.put("userOffers", getStringParam(offers));
-               }
-               if (!Strings.isEmpty(search)) {
-                       sb.append(" AND (LOWER(u.userSearchs) LIKE 
:userSearchs) ");
-                       params.put("userSearchs", getStringParam(search));
-               }
-               if (!Strings.isEmpty(text)) {
-                       sb.append(" AND (LOWER(u.login) LIKE :search ")
-                               .append("OR LOWER(u.firstname) LIKE :search ")
-                               .append("OR LOWER(u.lastname) LIKE :search ")
-                               .append("OR LOWER(u.address.email) LIKE :search 
")
-                               .append("OR LOWER(u.address.town) LIKE :search 
" + ") ");
-                       params.put("search", getStringParam(text));
-               }
-               if (!count && !Strings.isEmpty(orderBy)) {
-                       sb.append(" ORDER BY ").append(orderBy).append(asc ? " 
ASC" : " DESC");
-               }
-               TypedQuery<T> query = em.createQuery(sb.toString(), clazz);
-               setAdditionalParams(query, params);
-               return query;
-       }
-
-       public List<User> searchUserProfile(Long userId, String text, String 
offers, String search, String orderBy, long start, long max, boolean asc) {
-               return setLimits(getUserProfileQuery(User.class, userId, text, 
offers, search, orderBy, asc)
-                               , start, max).getResultList();
-       }
-
-       public Long searchCountUserProfile(Long userId, String text, String 
offers, String search) {
-               return getUserProfileQuery(Long.class, userId, text, offers, 
search, null, false).getSingleResult();
-       }
-
        public User getExternalUser(String extId, String extType) {
                return single(fillLazy(em
                                , oem -> 
oem.createNamedQuery("getExternalUser", User.class)
@@ -576,8 +571,8 @@ public class UserDao implements 
IGroupAdminDataProviderDao<User> {
        }
 
        @Override
-       public List<User> get(String search, long start, long count, String 
order) {
-               return get(search, start, count, order, false, 
Long.valueOf(-1));
+       public List<User> get(String search, long start, long count, 
SortParam<String> sort) {
+               return get(search, start, count, sort, false, Long.valueOf(-1));
        }
 
        public Set<Right> getRights(Long id) {
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/room/Invitation.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/room/Invitation.java
index 76a46c831..ba3fccaf6 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/room/Invitation.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/room/Invitation.java
@@ -51,14 +51,6 @@ import org.apache.openmeetings.db.entity.user.User;
 })
 public class Invitation extends HistoricalEntity {
        private static final long serialVersionUID = 1L;
-       public static final String SELECT_I = "SELECT i ";
-       public static final String SELECT_COUNT = "SELECT COUNT(i) ";
-       public static final String BY_ALL = " FROM Invitation i WHERE i.deleted 
= false";
-       public static final String BY_GROUP = BY_ALL + " AND i.invitedBy.id IN "
-                       + "(SELECT gu1.user.id FROM GroupUser gu1 WHERE 
gu1.group.id IN "
-                       + "    (SELECT gu.group.id FROM GroupUser gu WHERE 
gu.moderator = true AND gu.user.id = :userId)"
-                       + ") ";
-       public static final String BY_USER = BY_ALL + " AND i.invitedBy.id = 
:userId";
 
        public enum MessageType {
                CREATE
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/DaoHelper.java 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/DaoHelper.java
index 6bca78271..ce0a97220 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/DaoHelper.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/DaoHelper.java
@@ -20,15 +20,27 @@ package org.apache.openmeetings.db.util;
 
 import java.util.List;
 import java.util.Locale;
+import java.util.function.BiFunction;
 import java.util.function.Function;
+import java.util.stream.Stream;
 
 import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
+import javax.persistence.criteria.AbstractQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Expression;
+import javax.persistence.criteria.Path;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import javax.persistence.criteria.Subquery;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.openjpa.persistence.OpenJPAEntityManager;
 import org.apache.openjpa.persistence.OpenJPAPersistence;
 import org.apache.openjpa.persistence.OpenJPAQuery;
+import org.apache.openmeetings.db.entity.user.GroupUser;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.apache.wicket.util.string.Strings;
 
 public class DaoHelper {
@@ -36,89 +48,132 @@ public class DaoHelper {
 
        private DaoHelper() {}
 
-       public static String getSearchQuery(String table, String alias, String 
search, boolean filterDeleted, boolean count, String sort, String... fields) {
-               return getSearchQuery(table, alias, search, false, 
filterDeleted, count, sort, fields);
+       public static String getStringParam(String param) {
+               return "%" + StringUtils.lowerCase(param, Locale.ROOT) + "%";
        }
 
-       public static String getSearchQuery(String table, String alias, String 
search, boolean distinct, boolean filterDeleted, boolean count, String sort, 
String... fields) {
-               return getSearchQuery(table, alias, null, search, distinct, 
filterDeleted, count, null, sort, fields);
+       public static <T> long count(EntityManager em
+                       , Class<T> clazz
+                       , String search
+                       , List<String> searchFields
+                       , boolean noDeleted
+                       , BiFunction<CriteriaBuilder, CriteriaQuery<?>, 
Predicate> filter)
+       {
+               return count(em, clazz, (builder, root) -> builder.count(root), 
search, searchFields, noDeleted, filter);
        }
 
-       public static String getSearchQuery(String table, String alias, String 
join, String search, boolean filterDeleted, boolean count, String 
additionalWhere, String sort, String... fields) {
-               return getSearchQuery(table, alias, join, search, false, 
filterDeleted, count, additionalWhere, sort, fields);
+       public static <T> long count(EntityManager em
+                       , Class<T> clazz
+                       , BiFunction<CriteriaBuilder, Root<T>, 
Expression<Long>> queuePath
+                       , String search
+                       , List<String> searchFields
+                       , boolean noDeleted
+                       , BiFunction<CriteriaBuilder, CriteriaQuery<?>, 
Predicate> filter)
+       {
+               CriteriaQuery<Long> query = query(em, clazz, Long.class, 
queuePath, false, search, searchFields, noDeleted, filter, null);
+               return em.createQuery(query).getSingleResult();
        }
 
-       public static String getSearchQuery(String table, String alias, String 
join, String search, boolean distinct, boolean filterDeleted, boolean count, 
String additionalWhere, String sort, String... fields) {
-               StringBuilder sb = new StringBuilder("SELECT ");
-               if (count) {
-                       sb.append("COUNT(");
-               }
+       public static <T> List<T> get(
+                       EntityManager em
+                       , Class<T> clazz
+                       , boolean distinct
+                       , String search
+                       , List<String> searchFields
+                       , boolean noDeleted
+                       , BiFunction<CriteriaBuilder, CriteriaQuery<?>, 
Predicate> filter
+                       , SortParam<String> sort
+                       , long start
+                       , long count)
+       {
+               return get(em, clazz, clazz, (builder, root) -> root
+                               , distinct, search, searchFields, noDeleted, 
filter, sort, start, count);
+       }
+
+       public static <T, R> List<T> get(
+                       EntityManager em
+                       , Class<R> rootClazz
+                       , Class<T> clazz
+                       , BiFunction<CriteriaBuilder, Root<R>, Expression<T>> 
queuePath
+                       , boolean distinct
+                       , String search
+                       , List<String> searchFields
+                       , boolean noDeleted
+                       , BiFunction<CriteriaBuilder, CriteriaQuery<?>, 
Predicate> filter
+                       , SortParam<String> sort
+                       , long start
+                       , long count)
+       {
+               CriteriaQuery<T> query = query(em, rootClazz, clazz, queuePath, 
distinct, search, searchFields, noDeleted, filter, sort);
+               return setLimits(em.createQuery(query), start, 
count).getResultList();
+       }
+
+       public static <T, R> CriteriaQuery<T> query(
+                       EntityManager em
+                       , Class<R> rootClazz
+                       , Class<T> clazz
+                       , BiFunction<CriteriaBuilder, Root<R>, Expression<T>> 
queuePath
+                       , boolean distinct
+                       , String search
+                       , List<String> searchFields
+                       , boolean noDeleted
+                       , BiFunction<CriteriaBuilder, CriteriaQuery<?>, 
Predicate> filter
+                       , SortParam<String> sort)
+       {
+               CriteriaBuilder builder = em.getCriteriaBuilder();
+               CriteriaQuery<T> query = builder.createQuery(clazz);
+               Root<R> root = query.from(rootClazz);
+               query.select(queuePath.apply(builder, root));
                if (distinct) {
-                       sb.append("DISTINCT ");
-               }
-               sb.append(alias);
-               if (count) {
-                       sb.append(")");
+                       query.distinct(distinct);
                }
-               sb.append(" FROM ").append(table).append(" ").append(alias);
-               if (!Strings.isEmpty(join)) {
-                       sb.append(" ").append(join);
+
+               query.where(search(search, searchFields, noDeleted, filter, 
builder, root, query));
+               sort(sort, builder, root, query);
+               return query;
+       }
+
+       public static <T, Q> Predicate search(String search
+                       , List<String> searchFields
+                       , boolean noDeleted
+                       , BiFunction<CriteriaBuilder, CriteriaQuery<?>, 
Predicate> filter
+                       , CriteriaBuilder builder
+                       , Root<T> root
+                       , CriteriaQuery<Q> query)
+       {
+               Predicate result = builder.isNull(null);
+               if (noDeleted) {
+                       result = builder.and(result, 
builder.equal(root.get("deleted"), false));
                }
-               sb.append(" WHERE 1 = 1 ");
-               if (filterDeleted) {
-                       sb.append("AND ").append(alias).append(".deleted = 
false ");
+               if (filter != null) {
+                       result = builder.and(result, filter.apply(builder, 
query));
                }
-               appendWhereClause(sb, search, alias, fields);
-               if (!Strings.isEmpty(additionalWhere)) {
-                       sb.append("AND ").append(additionalWhere);
+               if (!Strings.isEmpty(search)) {
+                       Predicate[] criterias = Stream.of(search.replace("\'", 
"").replace("\"", "").split(" "))
+                                       .filter(searchItem -> 
!searchItem.isEmpty())
+                                       .map(DaoHelper::getStringParam)
+                                       .flatMap(searchItem -> 
searchFields.stream().map(col -> like(col, searchItem, builder, root)))
+                                       .toArray(Predicate[]::new);
+                       result = builder.and(result, builder.or(criterias));
                }
-               return appendSort(sb, alias, sort).toString();
+               return result;
        }
 
-       public static StringBuilder appendWhereClause(StringBuilder sb, String 
search, String alias, String... fields) {
-               if (!Strings.isEmpty(search) && fields != null) {
-                       boolean notEmpty = false;
-                       StringBuilder inSb = new StringBuilder();
-                       String[] searchItems = search.replace("\'", 
"").replace("\"", "").split(" ");
-                       for (int i = 0; i < searchItems.length; ++i) {
-                               if (searchItems[i].isEmpty()) {
-                                       continue;
-                               }
-                               if (i == 0) {
-                                       notEmpty = true;
-                                       inSb.append(" (");
-                               } else {
-                                       inSb.append(" OR ");
-                               }
-                               StringBuilder placeholder = new StringBuilder();
-                               
placeholder.append("%").append(StringUtils.lowerCase(searchItems[i], 
Locale.ROOT)).append("%");
-
-                               inSb.append("(");
-                               for (int j = 0; j < fields.length; ++j) {
-                                       if (j != 0) {
-                                               inSb.append(" OR ");
-                                       }
-                                       
inSb.append("lower(").append(alias).append(".").append(fields[j]).append(") 
LIKE '").append(placeholder).append("' ");
-                               }
-                               inSb.append(")");
-                       }
-                       if (notEmpty) {
-                               inSb.append(") ");
-                               sb.append(" AND").append(inSb);
-                       }
+       public static <T> Predicate like(String col, String searchItem, 
CriteriaBuilder builder, Path<T> root) {
+               Path<String> colPath = null;
+               String[] cols = col.split("[.]");
+               for(String s : cols) {
+                       colPath = colPath == null ? root.get(s) : 
colPath.get(s);
                }
-               return sb;
+               return builder.like(builder.lower(colPath), "%" + searchItem + 
"%");
        }
 
-       public static StringBuilder appendSort(StringBuilder sb, String alias, 
String sort) {
-               if (!Strings.isEmpty(sort)) {
-                       sb.append(" ORDER BY 
").append(alias).append(".").append(sort);
+       public static <T, Q> void sort(SortParam<String> sort, CriteriaBuilder 
builder, Root<T> root, CriteriaQuery<Q> query) {
+               if (sort != null && !Strings.isEmpty(sort.getProperty())) {
+                       query.orderBy(sort.isAscending()
+                                       ? 
builder.asc(root.get(sort.getProperty()))
+                                       : 
builder.desc(root.get(sort.getProperty())));
                }
-               return sb;
-       }
-
-       public static String getStringParam(String param) {
-               return param == null ? "%" : "%" + StringUtils.lowerCase(param, 
Locale.ROOT) + "%";
        }
 
        public static <T> TypedQuery<T> setLimits(TypedQuery<T> q, Long first, 
Long max) {
@@ -149,4 +204,21 @@ public class DaoHelper {
        public static <T> T single(List<T> l) {
                return l.isEmpty() ? null : l.get(0);
        }
+
+       @SuppressWarnings("unchecked")
+       public static <T> Root<T> getRoot(CriteriaQuery<?> query, Class<T> 
clazz) {
+               return query.getRoots().stream()
+                               .filter(r -> 
clazz.equals(r.getModel().getJavaType()))
+                               .map(r -> (Root<T>)r)
+                               .findAny()
+                               .orElseThrow();
+       }
+
+       public static Subquery<Long> groupAdminQuery(Long userId, 
CriteriaBuilder builder, AbstractQuery<?> parentQ) {
+               Subquery<Long> query = parentQ.subquery(Long.class);
+               Root<GroupUser> root = query.from(GroupUser.class);
+               query.select(root.get("group").get("id"));
+               query.where(builder.and(builder.isTrue(root.get("moderator")), 
builder.equal(root.get("user").get("id"), userId)));
+               return query;
+       }
 }
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/groups/GroupUsersPanel.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/groups/GroupUsersPanel.java
index 469b5350a..572ad9b82 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/groups/GroupUsersPanel.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/groups/GroupUsersPanel.java
@@ -152,7 +152,7 @@ public class GroupUsersPanel extends Panel {
                        list.addAll(users2add);
                        list.addAll(search == null && getSort() == null
                                        ? getDao().get(groupId, first, count)
-                                       : getDao().get(groupId, search, first, 
count, getSortStr()));
+                                       : getDao().get(groupId, search, first, 
count, getSort()));
 
                        return list.iterator();
                }
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/data/SearchableDataProvider.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/data/SearchableDataProvider.java
index 7edb21c12..2db68993a 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/data/SearchableDataProvider.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/data/SearchableDataProvider.java
@@ -53,19 +53,11 @@ public class SearchableDataProvider<T extends 
IDataProviderEntity> extends Sorta
                return get().getBean(clazz);
        }
 
-       protected String getSortStr() {
-               String result = null;
-               if (getSort() != null) {
-                       result = getSort().getProperty() + " " + 
(getSort().isAscending() ? "ASC" : "DESC");
-               }
-               return result;
-       }
-
        @Override
        public Iterator<? extends T> iterator(long first, long count) {
                return (search == null && getSort() == null
                        ? getDao().get(first, count)
-                       : getDao().get(search, first, count, 
getSortStr())).iterator();
+                       : getDao().get(search, first, count, 
getSort())).iterator();
        }
 
        @Override
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/data/SearchableGroupAdminDataProvider.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/data/SearchableGroupAdminDataProvider.java
index 54771e60b..66f911e45 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/data/SearchableGroupAdminDataProvider.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/data/SearchableGroupAdminDataProvider.java
@@ -42,8 +42,8 @@ public class SearchableGroupAdminDataProvider<T extends 
IDataProviderEntity> ext
        @Override
        public Iterator<? extends T> iterator(long first, long count) {
                return (hasGroupAdminLevel(getRights())
-                               ? getDao().adminGet(search, getUserId(), first, 
count, getSortStr())
-                               : getDao().adminGet(search, first, count, 
getSortStr())).iterator();
+                               ? getDao().adminGet(search, getUserId(), first, 
count, getSort())
+                               : getDao().adminGet(search, first, count, 
getSort())).iterator();
        }
 
        @Override
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/InvitationsPanel.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/InvitationsPanel.java
index 3be2347b8..76bcd1bd0 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/InvitationsPanel.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/InvitationsPanel.java
@@ -122,11 +122,11 @@ public class InvitationsPanel extends UserBasePanel {
                @Override
                public Iterator<? extends Invitation> iterator(long first, long 
count) {
                        if (rights.contains(Right.ADMIN)) {
-                               return getDao().get(search, first, count, 
getSortStr()).iterator();
+                               return getDao().get(search, first, count, 
getSort()).iterator();
                        } else if (rights.contains(Right.GROUP_ADMIN)) {
-                               return getDao().getGroup(search, first, count, 
userId, getSortStr()).iterator();
+                               return getDao().getGroup(search, first, count, 
userId, getSort()).iterator();
                        } else {
-                               return getDao().getUser(search, first, count, 
userId, getSortStr()).iterator();
+                               return getDao().getUser(search, first, count, 
userId, getSort()).iterator();
                        }
                }
 
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/UserSearchPanel.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/UserSearchPanel.java
index 94afde39d..277e0e11d 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/UserSearchPanel.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/UserSearchPanel.java
@@ -22,7 +22,7 @@ import static 
org.apache.openmeetings.db.util.TimezoneUtil.getTimeZone;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.ATTR_CLASS;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
@@ -35,6 +35,7 @@ import org.apache.openmeetings.web.common.UserBasePanel;
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.form.Form;
@@ -54,11 +55,10 @@ import 
de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
 public class UserSearchPanel extends UserBasePanel {
        private static final long serialVersionUID = 1L;
        private static final List<Integer> itemsPerPage = List.of(10, 25, 50, 
75, 100, 200, 500, 1000, 2500, 5000);
+       private static final SortParam<String> orderBy = new 
SortParam<>("firstname", true);
        private final TextField<String> text = new TextField<>("text", 
Model.of(""));
        private final TextField<String> search = new TextField<>("search", 
Model.of(""));
        private final TextField<String> offer = new TextField<>("offer", 
Model.of(""));
-       private String orderBy = "u.firstname";
-       private boolean asc = true;
        private boolean searched = false;
        private final WebMarkupContainer container = new 
WebMarkupContainer("container");
        @SpringBean
@@ -94,8 +94,8 @@ public class UserSearchPanel extends UserBasePanel {
 
                        @Override
                        public Iterator<? extends User> iterator(long first, 
long count) {
-                               return searched ? 
userDao.searchUserProfile(getUserId(), text.getModelObject(), 
offer.getModelObject(), search.getModelObject(), orderBy, first, count, 
asc).iterator()
-                                               : new 
ArrayList<User>().iterator();
+                               return searched ? 
userDao.searchUserProfile(getUserId(), text.getModelObject(), 
offer.getModelObject(), search.getModelObject(), orderBy, first, 
count).iterator()
+                                               : Collections.emptyIterator();
                        }
 
                        @Override
diff --git 
a/openmeetings-webservice/src/main/java/org/apache/openmeetings/webservice/GroupWebService.java
 
b/openmeetings-webservice/src/main/java/org/apache/openmeetings/webservice/GroupWebService.java
index f9608cafa..4da907296 100644
--- 
a/openmeetings-webservice/src/main/java/org/apache/openmeetings/webservice/GroupWebService.java
+++ 
b/openmeetings-webservice/src/main/java/org/apache/openmeetings/webservice/GroupWebService.java
@@ -54,6 +54,7 @@ import 
org.apache.openmeetings.webservice.error.ServiceException;
 import org.apache.openmeetings.webservice.schema.GroupDTOListWrapper;
 import org.apache.openmeetings.webservice.schema.ServiceResultWrapper;
 import org.apache.openmeetings.webservice.schema.UserSearchResultWrapper;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -319,7 +320,7 @@ public class GroupWebService extends BaseWebService {
                        result.setRecords(groupUserDao.count(id));
                        result.setResult(new ArrayList<>());
                        String order = isAlphanumeric(orderby) ? orderby : "id";
-                       for (GroupUser ou : groupUserDao.get(id, null, start, 
max, order + " " + (asc ? "ASC" : "DESC"))) {
+                       for (GroupUser ou : groupUserDao.get(id, null, start, 
max, order == null ? null : new SortParam<>(order, asc))) {
                                result.getResult().add(ou.getUser());
                        }
                        return new UserSearchResult(result);

Reply via email to