org.gvnix.web.datatables.util.DatatablesUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.gvnix.web.datatables Show documentation
Show all versions of org.gvnix.web.datatables Show documentation
Dandelion-DataTables utilities for Spring MVC based projects.
/*
* gvNIX. Spring Roo based RAD tool for Generalitat Valenciana
* Copyright (C) 2013 Generalitat Valenciana
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gvnix.web.datatables.util;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.SingularAttribute;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.gvnix.web.datatables.query.SearchResults;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import com.github.dandelion.datatables.core.ajax.ColumnDef;
import com.github.dandelion.datatables.core.ajax.ColumnDef.SortDirection;
import com.github.dandelion.datatables.core.ajax.DataSet;
import com.github.dandelion.datatables.core.ajax.DatatablesCriterias;
import com.github.dandelion.datatables.core.export.ExportConf;
import com.github.dandelion.datatables.core.export.HtmlTableBuilder;
import com.github.dandelion.datatables.core.export.HtmlTableBuilder.BeforeEndStep;
import com.github.dandelion.datatables.core.export.HtmlTableBuilder.ColumnStep;
import com.github.dandelion.datatables.core.html.HtmlTable;
import com.mysema.query.BooleanBuilder;
import com.mysema.query.QueryModifiers;
import com.mysema.query.jpa.impl.JPAQuery;
import com.mysema.query.types.Order;
import com.mysema.query.types.OrderSpecifier;
import com.mysema.query.types.Predicate;
import com.mysema.query.types.expr.BooleanExpression;
import com.mysema.query.types.path.PathBuilder;
/**
* Datatables utility functions
*
* @author gvNIX team
*
* @deprecated use {@link DatatablesUtilsBean} instead
*/
@Deprecated
public class DatatablesUtils {
private static final String ISNULL_OPE = "ISNULL";
private static final String NOTNULL_OPE = "NOTNULL";
private static final String G_ISNULL_OPE = "global.filters.operations.all.isnull";
private static final String G_NOTNULL_OPE = "global.filters.operations.all.notnull";
// Logger
private static Logger LOGGER = LoggerFactory
.getLogger(DatatablesUtils.class);
public static final String ROWS_ON_TOP_IDS_PARAM = "dtt_row_on_top_ids";
private static final String SEPARATOR_FIELDS = ".";
private static final String SEPARATOR_FIELDS_ESCAPED = "_~~_";
/**
* Execute a select query on entityClass using {@code DatatablesCriterias}
* information for filter, sort and paginate result.
*
* @param entityClass entity to use in search
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @return
* @deprecated see
* {@link #findByCriteria(Class, Map, Map, EntityManager, DatatablesCriterias, BooleanBuilder, boolean, ConversionService, MessageSource, Object[])}
*/
public static SearchResults findByCriteria(Class entityClass,
EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
ConversionService conversionService, MessageSource messageSource) {
return findByCriteria(entityClass, null, null, entityManager,
datatablesCriterias, (BooleanBuilder) null, false,
conversionService, messageSource, null);
}
/**
* Execute a select query on entityClass using {@code DatatablesCriterias}
* information for filter, sort and paginate result.
*
* @param entityClass entity to use in search
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @return
* @deprecated see
* {@link #findByCriteria(Class, Map, Map, EntityManager, DatatablesCriterias, BooleanBuilder, boolean, ConversionService, MessageSource, Object[])}
*/
public static SearchResults findByCriteria(Class entityClass,
EntityManager entityManager, DatatablesCriterias datatablesCriterias) {
return findByCriteria(entityClass, null, null, entityManager,
datatablesCriterias, (BooleanBuilder) null, false, null, null,
null);
}
/**
* Execute a select query on entityClass using {@code DatatablesCriterias}
* information for filter, sort and paginate result.
*
* @param entityClass entity to use in search
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param baseSearchValuesMap (optional) base filter values
* @param conversionService required by filter-by-expression and rows-on-top
* (otherwise optional)
* @param messageSource required by filter-by-expression (otherwise
* optional)
* @return
*/
public static SearchResults findByCriteria(Class entityClass,
EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
Map baseSearchValuesMap,
ConversionService conversionService, MessageSource messageSource) {
return findByCriteria(entityClass, null, null, entityManager,
datatablesCriterias, baseSearchValuesMap, false,
conversionService, messageSource);
}
/**
* Execute a select query on entityClass using {@code DatatablesCriterias}
* information for filter, sort and paginate result.
*
* @param entityClass entity to use in search
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param baseSearchValuesMap (optional) base filter values
* @return
* @deprecated see
* {@link #findByCriteria(Class, Map, Map, EntityManager, DatatablesCriterias, BooleanBuilder, boolean, ConversionService, MessageSource, Object[])}
*/
public static SearchResults findByCriteria(Class entityClass,
EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
Map baseSearchValuesMap) {
return findByCriteria(entityClass, null, null, entityManager,
datatablesCriterias, baseSearchValuesMap, false, null, null);
}
/**
* Execute a select query on entityClass using {@code DatatablesCriterias}
* information for filter, sort and paginate result.
*
* @param entityClass entity to use in search
* @param filterByAssociations (optional) for each related entity to join
* contain as key the name of the association and as value the List
* of related entity fields to filter by
* @param orderByAssociations (optional) for each related entity to order
* contain as key the name of the association and as value the List
* of related entity fields to order by
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param conversionService required by filter-by-expression and rows-on-top
* (otherwise optional)
* @param messageSource required by filter-by-expression (otherwise
* optional)
* @return
* @deprecated see
* {@link #findByCriteria(Class, Map, Map, EntityManager, DatatablesCriterias, BooleanBuilder, boolean, ConversionService, MessageSource, Object[])}
*/
public static SearchResults findByCriteria(Class entityClass,
Map> filterByAssociations,
Map> orderByAssociations,
EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
ConversionService conversionService, MessageSource messageSource) {
return findByCriteria(entityClass, filterByAssociations,
orderByAssociations, entityManager, datatablesCriterias, null,
false, conversionService, messageSource, null);
}
/**
* Execute a select query on entityClass using {@code DatatablesCriterias}
* information for filter, sort and paginate result.
*
* @param entityClass entity to use in search
* @param filterByAssociations (optional) for each related entity to join
* contain as key the name of the association and as value the List
* of related entity fields to filter by
* @param orderByAssociations (optional) for each related entity to order
* contain as key the name of the association and as value the List
* of related entity fields to order by
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @return
* @deprecated see
* {@link #findByCriteria(Class, Map, Map, EntityManager, DatatablesCriterias, BooleanBuilder, boolean, ConversionService, MessageSource, Object[])}
*/
public static SearchResults findByCriteria(Class entityClass,
Map> filterByAssociations,
Map> orderByAssociations,
EntityManager entityManager, DatatablesCriterias datatablesCriterias) {
return findByCriteria(entityClass, filterByAssociations,
orderByAssociations, entityManager, datatablesCriterias, null,
false, null, null, null);
}
/**
* Execute a select query on entityClass using {@code DatatablesCriterias}
* information for filter, sort and paginate result.
*
* This method can receive rows-on-top as parameter on
* baseSearchValueMap
using {@link #ROWS_ON_TOP_IDS_PARAM}
* name.
*
* @param entityClass entity to use in search
* @param filterByAssociations (optional) for each related entity to join
* contain as key the name of the association and as value the List
* of related entity fields to filter by
* @param orderByAssociations (optional) for each related entity to order
* contain as key the name of the association and as value the List
* of related entity fields to order by
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param baseSearchValuesMap (optional) base filter values
* @param conversionService required by filter-by-expression and rows-on-top
* (otherwise optional)
* @param messageSource required by filter-by-expression (otherwise
* optional)
* @return
*/
public static SearchResults findByCriteria(Class entityClass,
Map> filterByAssociations,
Map> orderByAssociations,
EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
Map baseSearchValuesMap,
ConversionService conversionService, MessageSource messageSource) {
return findByCriteria(entityClass, filterByAssociations,
orderByAssociations, entityManager, datatablesCriterias,
baseSearchValuesMap, false, conversionService, messageSource);
}
/**
* Execute a select query on entityClass using {@code DatatablesCriterias}
* information for filter, sort and paginate result.
*
* @param entityClass entity to use in search
* @param filterByAssociations (optional) for each related entity to join
* contain as key the name of the association and as value the List
* of related entity fields to filter by
* @param orderByAssociations (optional) for each related entity to order
* contain as key the name of the association and as value the List
* of related entity fields to order by
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param baseSearchValuesMap (optional) base filter values
* @return
* @deprecated see
* {@link #findByCriteria(Class, EntityManager, DatatablesCriterias, Map, ConversionService, MessageSource)}
*/
public static SearchResults findByCriteria(Class entityClass,
Map> filterByAssociations,
Map> orderByAssociations,
EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
Map baseSearchValuesMap) {
return findByCriteria(entityClass, filterByAssociations,
orderByAssociations, entityManager, datatablesCriterias,
baseSearchValuesMap, false, null, null);
}
/**
* Execute a select query on entityClass using Querydsl which enables the
* construction of type-safe SQL-like queries.
*
* @param entityClass entity to use in search
* @param filterByAssociations (optional) for each related entity to join
* contain as key the name of the association and as value the List
* of related entity fields to filter by
* @param orderByAssociations (optional) for each related entity to order
* contain as key the name of the association and as value the List
* of related entity fields to order by
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param baseSearchValuesMap (optional) base filter values
* @param distinct use distinct query
* @return
* @deprecated see
* {@link #findByCriteria(Class, EntityManager, DatatablesCriterias, Map,boolean, ConversionService, MessageSource)}
*/
public static > SearchResults findByCriteria(
Class entityClass,
Map> filterByAssociations,
Map> orderByAssociations,
EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
Map baseSearchValuesMap, boolean distinct)
throws IllegalArgumentException {
return findByCriteria(entityClass, filterByAssociations,
orderByAssociations, entityManager, datatablesCriterias,
baseSearchValuesMap, distinct, null, null);
}
/**
* Execute a select query on entityClass using Querydsl which enables the
* construction of type-safe SQL-like queries.
*
* This method can receive rows-on-top as parameter on
* baseSearchValueMap
using {@link #ROWS_ON_TOP_IDS_PARAM}
* name.
*
* @param entityClass entity to use in search
* @param filterByAssociations (optional) for each related entity to join
* contain as key the name of the association and as value the List
* of related entity fields to filter by
* @param orderByAssociations (optional) for each related entity to order
* contain as key the name of the association and as value the List
* of related entity fields to order by
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param baseSearchValuesMap (optional) base filter values
* @param distinct use distinct query
* @param conversionService required by filter-by-expression and rows-on-top
* (otherwise optional)
* @param messageSource required by filter-by-expression (otherwise
* optional)
* @return
*/
public static > SearchResults findByCriteria(
Class entityClass,
Map> filterByAssociations,
Map> orderByAssociations,
EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
Map baseSearchValuesMap, boolean distinct,
ConversionService conversionService, MessageSource messageSource)
throws IllegalArgumentException {
Assert.notNull(entityClass);
// Query DSL builder
PathBuilder entity = new PathBuilder(entityClass, "entity");
Object[] rowsOnTopIds = null;
// Predicate for base query
BooleanBuilder basePredicate;
if (baseSearchValuesMap != null) {
LOGGER.debug(
"findByCriteria handle baseSearch by map-of-values for entity '{}'...",
entity.getType());
// Handle ROWS_ON_TOP_IDS_PARAM param
Object tmpObject = baseSearchValuesMap.get(ROWS_ON_TOP_IDS_PARAM);
if (tmpObject != null) {
// Check if value is an array, otherwise
if (tmpObject.getClass().isArray()) {
rowsOnTopIds = (Object[]) tmpObject;
}
else {
rowsOnTopIds = new Object[] { tmpObject };
}
Map newBaseSearch = new HashMap(
baseSearchValuesMap);
newBaseSearch.remove(ROWS_ON_TOP_IDS_PARAM);
LOGGER.trace("findByCriteria extract rows on top from map {}",
rowsOnTopIds);
basePredicate = QuerydslUtils.createPredicateByAnd(entity,
newBaseSearch, conversionService);
}
else {
basePredicate = QuerydslUtils.createPredicateByAnd(entity,
baseSearchValuesMap, conversionService);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("findByCriteria baseSearch by map-of-values: {}",
basePredicate.toString());
}
}
else {
basePredicate = new BooleanBuilder();
}
return findByCriteria(entityClass, filterByAssociations,
orderByAssociations, entityManager, datatablesCriterias,
basePredicate, distinct, conversionService, messageSource,
rowsOnTopIds);
}
/**
* Execute a select query on entityClass using Querydsl which enables the
* construction of type-safe SQL-like queries.
*
* @param entityClass entity to use in search
* @param filterByAssociations (optional) for each related entity to join
* contain as key the name of the association and as value the List
* of related entity fields to filter by
* @param orderByAssociations (optional) for each related entity to order
* contain as key the name of the association and as value the List
* of related entity fields to order by
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param basePredicate (optional) base filter conditions
* @param distinct use distinct query
* @return
* @deprecated {@link #findByCriteria(Class, Map, Map, EntityManager, DatatablesCriterias, BooleanBuilder, boolean, ConversionService, MessageSource, Object[])}
*/
public static > SearchResults findByCriteria(
Class entityClass,
Map> filterByAssociations,
Map> orderByAssociations,
EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
BooleanBuilder basePredicate, boolean distinct)
throws IllegalArgumentException {
return findByCriteria(entityClass, filterByAssociations,
orderByAssociations, entityManager, datatablesCriterias,
basePredicate, distinct, null, null, null);
}
/**
* Execute a select query on entityClass using Querydsl which enables the
* construction of type-safe SQL-like queries.
*
* @param entityClass entity to use in search
* @param filterByAssociations (optional) for each related entity to join
* contain as key the name of the association and as value the List
* of related entity fields to filter by
* @param orderByAssociations (optional) for each related entity to order
* contain as key the name of the association and as value the List
* of related entity fields to order by
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param basePredicate (optional) base filter conditions
* @param distinct use distinct query
* @return
* @deprecated see
* {@link #findByCriteria(Class, Map, Map, EntityManager, DatatablesCriterias, BooleanBuilder, boolean, ConversionService, MessageSource, Object[])}
*/
public static > SearchResults findByCriteria(
Class entityClass,
Map> filterByAssociations,
Map> orderByAssociations,
EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
BooleanBuilder basePredicate, boolean distinct,
ConversionService conversionService, MessageSource messageSource)
throws IllegalArgumentException {
Assert.notNull(entityClass);
// Query DSL builder
PathBuilder entity = new PathBuilder(entityClass, "entity");
return findByCriteria(entity, filterByAssociations,
orderByAssociations, entityManager, datatablesCriterias,
basePredicate, distinct, conversionService, messageSource, null);
}
/**
* Execute a select query on entityClass using {@code DatatablesCriterias}
* information for filter, sort and paginate result.
*
* This method can receive rows-on-top as parameter on
* baseSearchValueMap
using {@link #ROWS_ON_TOP_IDS_PARAM}
* name.
*
* @param entityClass entity to use in search
* @param filterByAssociations (optional) for each related entity to join
* contain as key the name of the association and as value the List
* of related entity fields to filter by
* @param orderByAssociations (optional) for each related entity to order
* contain as key the name of the association and as value the List
* of related entity fields to order by
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param basePredicate (optional) base filter
* @param conversionService required by filter-by-expression and rows-on-top
* (otherwise optional)
* @param messageSource required by filter-by-expression (otherwise
* optional)
* @param rowsOnTopIds (optional) array with id of rows to show on top of
* result list
* @return
* @throws IllegalArgumentException
*/
public static > SearchResults findByCriteria(
Class entityClass,
Map> filterByAssociations,
Map> orderByAssociations,
EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
BooleanBuilder basePredicate, boolean distinct,
ConversionService conversionService, MessageSource messageSource,
Object[] rowsOnTopIds) throws IllegalArgumentException {
Assert.notNull(entityClass);
// Query DSL builder
PathBuilder entity = new PathBuilder(entityClass, "entity");
return findByCriteria(entity, filterByAssociations,
orderByAssociations, entityManager, datatablesCriterias,
basePredicate, distinct, conversionService, messageSource,
rowsOnTopIds);
}
/**
* Execute a select query on entityClass using Querydsl which enables the
* construction of type-safe SQL-like queries.
*
* @param entity builder for entity to use in search. Represents the entity
* and gives access to its properties for query purposes
* @param filterByAssociations (optional) for each related entity to join
* contain as key the name of the association and as value the List
* of related entity fields to filter by
* @param orderByAssociations (optional) for each related entity to order
* contain as key the name of the association and as value the List
* of related entity fields to order by
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param basePredicate (optional) base filter conditions
* @param conversionService required by filter-by-expression and rows-on-top
* (otherwise optional)
* @param distinct use distinct query
* @return
* @deprecated see
* {@link #findByCriteria(Class, EntityManager, DatatablesCriterias, Map, ConversionService, MessageSource)}
*/
public static > SearchResults findByCriteria(
PathBuilder entity,
Map> filterByAssociations,
Map> orderByAssociations,
EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
BooleanBuilder basePredicate, ConversionService conversionService,
MessageSource messageSource) throws IllegalArgumentException {
return findByCriteria(entity, filterByAssociations,
orderByAssociations, entityManager, datatablesCriterias,
basePredicate, false, conversionService, messageSource, null);
}
/**
* Execute a select query on entityClass using Querydsl which enables the
* construction of type-safe SQL-like queries.
*
* @param entity builder for entity to use in search. Represents the entity
* and gives access to its properties for query purposes
* @param filterByAssociations (optional) for each related entity to join
* contain as key the name of the association and as value the List
* of related entity fields to filter by
* @param orderByAssociations (optional) for each related entity to order
* contain as key the name of the association and as value the List
* of related entity fields to order by
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param basePredicate (optional) base filter conditions
* @return
* @deprecated see
* {@link #findByCriteria(PathBuilder, Map, Map, EntityManager, DatatablesCriterias, BooleanBuilder, boolean, ConversionService, MessageSource, Object[])}
*/
public static > SearchResults findByCriteria(
PathBuilder entity,
Map> filterByAssociations,
Map> orderByAssociations,
EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
BooleanBuilder basePredicate) throws IllegalArgumentException {
return findByCriteria(entity, filterByAssociations,
orderByAssociations, entityManager, datatablesCriterias,
basePredicate, false, null, null, null);
}
/**
* Execute a select query on entityClass using Querydsl which enables the
* construction of type-safe SQL-like queries.
*
* @param entity builder for entity to use in search. Represents the entity
* and gives access to its properties for query purposes
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param basePredicate (optional) base filter conditions
* @param conversionService required by filter-by-expression and rows-on-top
* (otherwise optional)
* @param messageSource required by filter-by-expression (otherwise
* optional)
* @return
* @deprecated see
* {@link #findByCriteria(PathBuilder, Map, Map, EntityManager, DatatablesCriterias, BooleanBuilder, boolean, ConversionService, MessageSource, Object[])}
*/
public static > SearchResults findByCriteria(
PathBuilder entity, EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
BooleanBuilder basePredicate, ConversionService conversionService,
MessageSource messageSource) throws IllegalArgumentException {
return findByCriteria(entity, null, null, entityManager,
datatablesCriterias, basePredicate, false, conversionService,
messageSource, null);
}
/**
* Execute a select query on entityClass using Querydsl which enables the
* construction of type-safe SQL-like queries.
*
* @param entity builder for entity to use in search. Represents the entity
* and gives access to its properties for query purposes
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param basePredicate (optional) base filter conditions
* @param conversionService required by filter-by-expression and rows-on-top
* (otherwise optional)
* @param messageSource required by filter-by-expression (otherwise
* optional)
* @param rowsOnTopIds (optional) array with id of rows to show on top of
* result list
* @return
* @throws IllegalArgumentException
*/
public static > SearchResults findByCriteria(
PathBuilder entity, EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
BooleanBuilder basePredicate, ConversionService conversionService,
MessageSource messageSource, Object[] rowsOnTopIds)
throws IllegalArgumentException {
return findByCriteria(entity, null, null, entityManager,
datatablesCriterias, basePredicate, false, conversionService,
messageSource, rowsOnTopIds);
}
/**
* Execute a select query on entityClass using Querydsl which enables the
* construction of type-safe SQL-like queries.
*
* @param entity builder for entity to use in search. Represents the entity
* and gives access to its properties for query purposes
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param basePredicate (optional) base filter conditions
* @return
* @deprecated see
* {@link #findByCriteria(PathBuilder, Map, Map, EntityManager, DatatablesCriterias, BooleanBuilder, boolean, ConversionService, MessageSource, Object[])}
*/
public static > SearchResults findByCriteria(
PathBuilder entity, EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
BooleanBuilder basePredicate) throws IllegalArgumentException {
return findByCriteria(entity, null, null, entityManager,
datatablesCriterias, basePredicate, false, null, null, null);
}
/**
* Execute a select query on entityClass using Querydsl which enables the
* construction of type-safe SQL-like queries.
*
* @param entity builder for entity to use in search. Represents the entity
* and gives access to its properties for query purposes
* @param filterByAssociations (optional) for each related entity to join
* contain as key the name of the association and as value the List
* of related entity fields to filter by
* @param orderByAssociations (optional) for each related entity to order
* contain as key the name of the association and as value the List
* of related entity fields to order by
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param basePredicate (optional) base filter conditions
* @param distinct use distinct query
* @param conversionService required by filter-by-expression and rows-on-top
* (otherwise optional)
* @param messageSource required by filter-by-expression (otherwise
* optional)
* @return
* @deprecated see
* {@link #findByCriteria(PathBuilder, Map, Map, EntityManager, DatatablesCriterias, BooleanBuilder, boolean, ConversionService, MessageSource, Object[])}
*/
public static > SearchResults findByCriteria(
PathBuilder entity,
Map> filterByAssociations,
Map> orderByAssociations,
EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
BooleanBuilder basePredicate, boolean distinct,
ConversionService conversionService, MessageSource messageSource)
throws IllegalArgumentException {
return findByCriteria(entity, null, null, entityManager,
datatablesCriterias, basePredicate, false, null, null, null);
}
/**
* Execute a select query on entityClass using Querydsl which enables the
* construction of type-safe SQL-like queries.
*
* @param entity builder for entity to use in search. Represents the entity
* and gives access to its properties for query purposes
* @param filterByAssociations (optional) for each related entity to join
* contain as key the name of the association and as value the List
* of related entity fields to filter by
* @param orderByAssociations (optional) for each related entity to order
* contain as key the name of the association and as value the List
* of related entity fields to order by
* @param entityManager {@code entityClass} {@link EntityManager}
* @param datatablesCriterias datatables parameters for query
* @param basePredicate (optional) base filter conditions
* @param distinct use distinct query
* @param conversionService required by filter-by-expression and rows-on-top
* (otherwise optional)
* @param messageSource required by filter-by-expression (otherwise
* optional)
* @param rowsOnTopIds (optional) array with id of rows to show on top of
* result list
* @return
* @throws IllegalArgumentException
*/
public static > SearchResults findByCriteria(
PathBuilder entity,
Map> filterByAssociations,
Map> orderByAssociations,
EntityManager entityManager,
DatatablesCriterias datatablesCriterias,
BooleanBuilder basePredicate, boolean distinct,
ConversionService conversionService, MessageSource messageSource,
Object[] rowsOnTopIds) throws IllegalArgumentException {
// Check arguments aren't null
Assert.notNull(entityManager);
Assert.notNull(datatablesCriterias);
// If null, create empty Map to avoid control code overload
if (CollectionUtils.isEmpty(filterByAssociations)) {
filterByAssociations = new HashMap>();
}
if (CollectionUtils.isEmpty(orderByAssociations)) {
orderByAssociations = new HashMap>();
}
// true if data results must be paginated
boolean isPaged = datatablesCriterias.getDisplaySize() != null
&& datatablesCriterias.getDisplaySize() > 0;
// true if the search must take in account all columns
boolean findInAllColumns = StringUtils.isNotEmpty(datatablesCriterias
.getSearch()) && datatablesCriterias.hasOneFilterableColumn();
LOGGER.debug(
"findByCriteria for entity '{}' (paged={} findInAllColumns={})",
entity.getType(), isPaged, findInAllColumns);
// ----- Create queries -----
// query will take in account datatables search, order and paging
// criterias
JPAQuery query = new JPAQuery(entityManager);
query = query.from(entity);
// baseQuery will use base search values only in order to count
// all for success paging
JPAQuery baseQuery = new JPAQuery(entityManager);
baseQuery = baseQuery.from(entity);
// ----- Entity associations for Query JOINs, ORDER BY, ... -----
Map> associationMap = new HashMap>();
query = prepareQueryAssociationMap(entity, filterByAssociations,
datatablesCriterias, findInAllColumns, query, associationMap);
// ----- Query WHERE clauses -----
// Filters by column. Using BooleanBuilder, a cascading builder for
// Predicate expressions
BooleanBuilder filtersByColumnPredicate = new BooleanBuilder();
// Filters by table (for all columns)
BooleanBuilder filtersByTablePredicate = new BooleanBuilder();
try {
// Build the filters by column expression
if (datatablesCriterias.hasOneFilteredColumn()) {
filtersByColumnPredicate = prepareQueryFilterPart(entity,
filterByAssociations, datatablesCriterias,
associationMap, filtersByColumnPredicate,
conversionService, messageSource);
}
// Build the query to search the given value in all columns
filtersByTablePredicate = prepareQuerySearchPart(entity,
filterByAssociations, datatablesCriterias,
findInAllColumns, associationMap, filtersByTablePredicate,
conversionService);
}
catch (Exception e) {
LOGGER.error("Exception preparing filter for entity {}",
entity.getType(), e);
SearchResults searchResults = new SearchResults(
new ArrayList(0), 0, isPaged, new Long(
org.apache.commons.lang3.ObjectUtils.defaultIfNull(
datatablesCriterias.getDisplayStart(), 0)),
new Long(org.apache.commons.lang3.ObjectUtils
.defaultIfNull(
datatablesCriterias.getDisplaySize(), 0)),
0);
return searchResults;
}
// ----- Query ORDER BY -----
List> orderSpecifiersList = prepareQueryOrder(entity,
orderByAssociations, datatablesCriterias, associationMap);
// ----- Query results paging -----
Long offset = null;
Long limit = null;
if (isPaged) {
limit = new Long(datatablesCriterias.getDisplaySize());
}
if (datatablesCriterias.getDisplayStart() != null
&& datatablesCriterias.getDisplayStart() >= 0) {
offset = new Long(datatablesCriterias.getDisplayStart());
}
// ------- manage Rows-on-top ----
List firstRows = null;
// Decrease limits if firstRowsIds is used
if (rowsOnTopIds != null) {
LOGGER.trace("Prepare rows on top: {}", rowsOnTopIds);
// Coherce row-on-top ids types
Object[] cohercedRowsOnTopId = new Object[rowsOnTopIds.length];
EntityType extends T> entityMetamodel = entityManager
.getMetamodel().entity(entity.getType());
// We always have just one id. This id can be an Embedded Id
Class> idType = entityMetamodel.getIdType().getJavaType();
@SuppressWarnings("unchecked")
SingularAttribute extends T, ?> idAttr = (SingularAttribute extends T, ?>) entityMetamodel
.getId(idType);
Object curId;
for (int i = 0; i < rowsOnTopIds.length; i++) {
curId = rowsOnTopIds[i];
if (curId.getClass() != idType) {
cohercedRowsOnTopId[i] = conversionService.convert(curId,
idType);
}
else {
cohercedRowsOnTopId[i] = curId;
}
}
// Create expression for rows-on-top
BooleanExpression firstRowsInExpression = QuerydslUtils
.createCollectionExpression(entity, idAttr.getName(),
Arrays.asList(cohercedRowsOnTopId));
LOGGER.trace("Expression for rowsOnTop: {}", firstRowsInExpression);
// Exclude firstRows from base query
basePredicate = basePredicate.and(firstRowsInExpression.not());
LOGGER.trace("basePredicate to exclude rowsOnTop now is: {}",
basePredicate);
// Gets rows on top
JPAQuery firstRowsQuery = new JPAQuery(entityManager);
firstRowsQuery = firstRowsQuery.from(entity).where(
firstRowsInExpression);
LOGGER.trace("rowsOnTop query is: {}", firstRowsQuery);
try {
// TODO handle fieldSelector
firstRows = firstRowsQuery.list(entity);
}
catch (PersistenceException exSql) {
// Log query
LOGGER.error("Error excecuting SQL for firstRow (sql = '{}' )",
firstRowsQuery);
throw exSql;
}
LOGGER.trace("Found {} rows for rowsOnTop", firstRows.size());
// Adjust limit with rows-on-top found
if (limit != null) {
LOGGER.trace("Update main query limit: {} --> {}", limit, limit
- firstRows.size());
limit = limit - firstRows.size();
}
}
// ----- Execute the query -----
List elements = null;
// Compose the final query and update query var to be used to count
// total amount of rows if needed
if (distinct) {
LOGGER.trace("Use distinct query!!!");
query = query.distinct();
}
// Predicate for base query
boolean hasBasePredicate = true;
if (basePredicate == null) {
basePredicate = new BooleanBuilder();
hasBasePredicate = false;
}
// query projection to count all entities without paging
baseQuery.where(basePredicate);
// query projection to be used to get the results and to count filtered
// results
query = query.where(basePredicate.and(
filtersByColumnPredicate.getValue()).and(
filtersByTablePredicate.getValue()));
// Calculate the total amount of rows taking in account datatables
// search and paging criterias. When results are paginated we
// must execute a count query, otherwise the size of matched rows List
// is the total amount of rows
long totalResultCount = 0;
if (isPaged) {
try {
totalResultCount = query.count();
}
catch (PersistenceException exSql) {
// Log query
LOGGER.error("Error excecuting 'count' SQL: {}", query);
throw exSql;
}
}
if (offset == null) {
offset = new Long(0);
}
else if (offset > totalResultCount) {
// If offset value is bigger than total results,
// offset needs start on 0
offset = new Long(0);
}
// QueryModifiers combines limit and offset
QueryModifiers queryModifiers = new QueryModifiers(limit, offset);
LOGGER.trace("Set limit={} offset={}", limit, offset);
// List ordered and paginated results. An empty list is returned for no
// results.
query = query.orderBy(orderSpecifiersList
.toArray(new OrderSpecifier[orderSpecifiersList.size()]));
LOGGER.debug("Execute query: {}", query);
try {
elements = query.restrict(queryModifiers).list(entity);
}
catch (PersistenceException exSql) {
// Log query
LOGGER.error("Error excecuting SQL: {}", query);
throw exSql;
}
if (!isPaged) {
totalResultCount = elements.size();
}
long totalBaseCount = totalResultCount;
if (hasBasePredicate) {
// Calculate the total amount of entities including base filters
// only
LOGGER.trace("Execute count query: {}", baseQuery);
try {
totalBaseCount = baseQuery.count();
}
catch (PersistenceException exSql) {
// Log query
LOGGER.error("Error excecuting 'count' SQL: {}", baseQuery);
throw exSql;
}
LOGGER.trace("Found : {}", totalBaseCount);
}
if (firstRows != null) {
// Adjust result with rows-on-top
totalResultCount = totalResultCount + firstRows.size();
totalBaseCount = totalBaseCount + firstRows.size();
elements.addAll(0, firstRows);
}
// Create a new SearchResults instance
if (limit == null) {
limit = totalBaseCount;
}
SearchResults searchResults = new SearchResults(elements,
totalResultCount, isPaged, offset, limit, totalBaseCount);
LOGGER.debug(
"findByCriteria: return {} rows from {} (offset={} limit={})",
totalResultCount, totalBaseCount, offset, limit);
return searchResults;
}
/**
* Prepares associationMap for findByCriteria
*
* @param entity
* @param filterByAssociations
* @param datatablesCriterias
* @param findInAllColumns
* @param query
* @param associationMap
* @return
*/
public static JPAQuery prepareQueryAssociationMap(
PathBuilder entity,
Map> filterByAssociations,
DatatablesCriterias datatablesCriterias, boolean findInAllColumns,
JPAQuery query, Map> associationMap) {
LOGGER.debug("Preparing associationMap and joins for entity {}...",
entity.getType());
for (ColumnDef column : datatablesCriterias.getColumnDefs()) {
// true if the search must include this column
boolean findInColumn = StringUtils.isNotEmpty(column.getSearch());
// If no joins given for this column, don't add the JOIN to query
// to improve performance
String associationName = unescapeDot(column.getName());
if (!filterByAssociations.containsKey(associationName)) {
continue;
}
// If column is not sortable and is not filterable, don't add the
// JOIN to query to improve performance
if (!column.isSortable() && !column.isFilterable()) {
continue;
}
// If column is not sortable and no search value provided,
// don't add the JOIN to query to improve performance
if (!column.isSortable() && !findInColumn && !findInAllColumns) {
continue;
}
// Here the column is sortable or it is filterable and column search
// value or all-column search value is provided
PathBuilder> associationPath = entity.get(associationName);
query = query.join(associationPath);
// Store join path for later use in where
associationMap.put(associationName, associationPath);
LOGGER.trace("Added join {} -> {} as {}...", entity.getType(),
associationPath, associationName);
}
return query;
}
/**
* Prepares filter part for a query of findByCriteria
*
* @param entity
* @param filterByAssociations
* @param datatablesCriterias
* @param associationMap
* @param filtersByColumnPredicate
* @return
*/
private static BooleanBuilder prepareQueryFilterPart(
PathBuilder entity,
Map> filterByAssociations,
DatatablesCriterias datatablesCriterias,
Map> associationMap,
BooleanBuilder filtersByColumnPredicate,
ConversionService conversionService, MessageSource messageSource) {
// Add filterable columns only
LOGGER.debug("Preparing filter-column expression for entity {}...",
entity.getType());
Predicate filterExpression;
for (ColumnDef column : datatablesCriterias.getColumnDefs()) {
// Each column has its own search by value
String searchStr = column.getSearch();
// true if the search must include this column
boolean findInColumn = column.isFilterable()
&& StringUtils.isNotEmpty(searchStr);
if (findInColumn) {
// Entity field name and type
String fieldName = unescapeDot(column.getName());
LOGGER.trace("Preparing filter for '{}' by '{}'...", fieldName,
searchStr);
// On column search, connect where clauses together by
// AND
// because we want found the records which columns
// match with column filters
filterExpression = QuerydslUtils.createExpression(entity,
fieldName, searchStr, conversionService, messageSource);
filtersByColumnPredicate = filtersByColumnPredicate
.and(filterExpression);
LOGGER.trace("filtersByColumnPredicate AND '{}'",
filterExpression);
// TODO: Este codigo se puede pasar a QuerydslUtils ?
// If column is an association and there are given
// join attributes, add those attributes to WHERE
// predicates
List attributes = filterByAssociations.get(fieldName);
if (attributes != null && attributes.size() > 0) {
// Filters of associated entity properties
BooleanBuilder filtersByAssociationPredicate = new BooleanBuilder();
PathBuilder> associationPath = associationMap
.get(fieldName);
List associationFields = filterByAssociations
.get(fieldName);
for (String associationFieldName : associationFields) {
// On association search, connect
// associated entity where clauses by OR
// because all assoc entity properties are
// inside the same column and any of its
// property value can match with given search
// value
filterExpression = QuerydslUtils.createExpression(
associationPath, associationFieldName,
searchStr, conversionService);
filtersByAssociationPredicate = filtersByAssociationPredicate
.or(filterExpression);
LOGGER.trace("filtersByAssociationPredicate OR '{}'",
filterExpression);
}
filtersByColumnPredicate = filtersByColumnPredicate
.and(filtersByAssociationPredicate.getValue());
LOGGER.trace("filtersByColumnPredicate AND '{}'",
filtersByAssociationPredicate.getValue());
}
}
}
LOGGER.debug("Final filtersByColumnPredicate = '{}'",
filtersByColumnPredicate);
return filtersByColumnPredicate;
}
/**
* Prepare search part for a query of findByCriteria
*
* @param entity
* @param filterByAssociations
* @param datatablesCriterias
* @param findInAllColumns
* @param associationMap
* @param filtersByTablePredicate
* @return
*/
private static BooleanBuilder prepareQuerySearchPart(
PathBuilder entity,
Map> filterByAssociations,
DatatablesCriterias datatablesCriterias, boolean findInAllColumns,
Map> associationMap,
BooleanBuilder filtersByTablePredicate,
ConversionService conversionService) {
String searchStr = datatablesCriterias.getSearch();
if (StringUtils.isEmpty(searchStr)) {
// Nothing to do
return filtersByTablePredicate;
}
LOGGER.debug(
"Preparing search expression for '{}' string on entity {}...",
searchStr, entity.getType());
if (findInAllColumns) {
boolean expressionExists = false;
// Add filterable columns only
for (ColumnDef column : datatablesCriterias.getColumnDefs()) {
if (column.isFilterable()) {
// Entity field name and type
String fieldName = unescapeDot(column.getName());
LOGGER.trace("Check expression column {}...", fieldName);
// Find in all columns means we want to find given
// value in at least one entity property, so we must
// join the where clauses by OR
Predicate expression = QuerydslUtils.createExpression(
entity, fieldName, searchStr, conversionService);
if (expression != null) {
filtersByTablePredicate = filtersByTablePredicate
.or(expression);
LOGGER.trace("Added expression {}", expression);
expressionExists = true;
}
// If column is an association and there are given
// join attributes, add those attributes to WHERE
// predicates
List attributes = filterByAssociations
.get(fieldName);
if (attributes != null && attributes.size() > 0) {
PathBuilder> associationPath = associationMap
.get(fieldName);
List associationFields = filterByAssociations
.get(fieldName);
for (String associationFieldName : associationFields) {
expression = QuerydslUtils.createExpression(
associationPath, associationFieldName,
searchStr, conversionService);
filtersByTablePredicate = filtersByTablePredicate
.or(expression);
LOGGER.trace(
"Added expression (by association) {}",
expression);
}
}
}
}
// If expression is null returns error to returns an empty
// DataSource
if (!expressionExists) {
throw new RuntimeException("Expression cannot be null");
}
}
LOGGER.debug("Search expression: {}", filtersByTablePredicate);
return filtersByTablePredicate;
}
/**
* prepares order part for a query of findByCriteria
*
* @param entity
* @param orderByAssociations
* @param datatablesCriterias
* @param associationMap
* @return
*/
@SuppressWarnings("unchecked")
private static , T> List> prepareQueryOrder(
PathBuilder entity,
Map> orderByAssociations,
DatatablesCriterias datatablesCriterias,
Map> associationMap) {
List> orderSpecifiersList = new ArrayList>();
if (datatablesCriterias.hasOneSortedColumn()) {
LOGGER.debug("Preparing order for entity {}", entity.getType());
OrderSpecifier> queryOrder;
for (ColumnDef column : datatablesCriterias.getSortingColumnDefs()) {
// If column is not sortable, don't add it to order by clauses
if (!column.isSortable()) {
continue;
}
// If no sort direction provided, don't add this column to
// order by clauses
if (column.getSortDirection() == null) {
LOGGER.debug("Column {} ignored: not sortDirection",
column.getName());
continue;
}
// Convert Datatables sort direction to Querydsl order
Order order = Order.DESC;
if (column.getSortDirection() == SortDirection.ASC) {
order = Order.ASC;
}
// Entity field name and type. Type must extend Comparable
// interface
String fieldName = unescapeDot(column.getName());
LOGGER.trace("Adding column {} {}...", fieldName, order);
Class fieldType = (Class) QuerydslUtils.getFieldType(
fieldName, entity);
List attributes = orderByAssociations.get(fieldName);
try {
// If column is an association and there are given
// order by attributes, add those attributes to ORDER BY
// clauses
if (attributes != null && attributes.size() > 0) {
PathBuilder> associationPath = associationMap
.get(fieldName);
List associationFields = orderByAssociations
.get(fieldName);
for (String associationFieldName : associationFields) {
// Get associated entity field type
Class associationFieldType = (Class) BeanUtils
.findPropertyType(
associationFieldName,
ArrayUtils
.> toArray(fieldType));
queryOrder = QuerydslUtils.createOrderSpecifier(
associationPath, associationFieldName,
associationFieldType, order);
orderSpecifiersList.add(queryOrder);
LOGGER.trace("Added order: {}", queryOrder);
}
}
// Otherwise column is an entity property
else {
queryOrder = QuerydslUtils.createOrderSpecifier(entity,
fieldName, fieldType, order);
orderSpecifiersList.add(queryOrder);
LOGGER.trace("Added order: {}", queryOrder);
}
}
catch (ClassCastException ex) {
// Do nothing, on class cast exception order specifier will
// be null
LOGGER.debug("CastException preparing order for entity {}",
entity.getType(), ex);
continue;
}
catch (Exception ex) {
LOGGER.warn("Exception preparing order for entity {}",
entity.getType(), ex);
continue;
}
}
}
return orderSpecifiersList;
}
/**
* Populate a {@link DataSet} from given entity list.
*
* Field values will be converted to String using given
* {@link ConversionService} and Date fields will be converted to Date using
* {@link DateFormat} with given date patterns.
*
* @param entities List of T entities to convert to Datatables data
* @param pkFieldName The T entity field that contains the PK
* @param totalRecords Total amount of records
* @param totalDisplayRecords Amount of records found
* @param columns {@link ColumnDef} list
* @param datePatterns Patterns to convert Date fields to String. The Map
* contains one pattern for each entity Date field keyed by field
* name. For Roo compatibility the key could follow the pattern
* {@code uncapitalize( ENTITY ) + "_" + lower_case( FIELD ) + "_date_format"}
* too
* @param conversionService
* @return
*/
public static DataSet
© 2015 - 2025 Weber Informatics LLC | Privacy Policy