added generic filter support
This commit is contained in:
@ -22,6 +22,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
import javax.ejb.Lock;
|
import javax.ejb.Lock;
|
||||||
import javax.ejb.LockType;
|
import javax.ejb.LockType;
|
||||||
import javax.ejb.TransactionAttribute;
|
import javax.ejb.TransactionAttribute;
|
||||||
@ -32,11 +34,14 @@ import javax.persistence.TypedQuery;
|
|||||||
import javax.persistence.criteria.CriteriaBuilder;
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
import javax.persistence.criteria.CriteriaQuery;
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
import javax.persistence.criteria.Order;
|
import javax.persistence.criteria.Order;
|
||||||
|
import javax.persistence.criteria.Path;
|
||||||
|
import javax.persistence.criteria.Predicate;
|
||||||
import javax.persistence.criteria.Root;
|
import javax.persistence.criteria.Root;
|
||||||
import javax.persistence.metamodel.IdentifiableType;
|
import javax.persistence.metamodel.IdentifiableType;
|
||||||
import javax.persistence.metamodel.Metamodel;
|
import javax.persistence.metamodel.Metamodel;
|
||||||
import javax.persistence.metamodel.SingularAttribute;
|
import javax.persistence.metamodel.SingularAttribute;
|
||||||
import javax.transaction.Transactional;
|
import javax.transaction.Transactional;
|
||||||
|
import org.apache.shiro.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -58,6 +63,130 @@ public abstract class AbstractController<T> {
|
|||||||
this.entityClass = clazz;
|
this.entityClass = clazz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Predicate getFilterCondition(CriteriaBuilder cb, Root<T> root, Map<String, Object> filters) {
|
||||||
|
return getFilterCondition(cb, root, filters, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Predicate getFilterCondition(CriteriaBuilder cb, Root<T> root, Map<String, Object> filters, Map<String, Object> excludeFilters) {
|
||||||
|
Predicate filterCondition = null;
|
||||||
|
filterCondition = getFilterCondition(filterCondition, cb, root, filters, true);
|
||||||
|
filterCondition = getFilterCondition(filterCondition, cb, root, excludeFilters, false);
|
||||||
|
return filterCondition;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Predicate addFilterCondition(CriteriaBuilder cb, Predicate filterCondition, Predicate addCondition) {
|
||||||
|
if (addCondition == null) {
|
||||||
|
return filterCondition;
|
||||||
|
}
|
||||||
|
if (filterCondition == null) {
|
||||||
|
filterCondition = addCondition;
|
||||||
|
} else {
|
||||||
|
filterCondition = cb.and(filterCondition, addCondition);
|
||||||
|
}
|
||||||
|
return filterCondition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* extends the given filterCondition by the addtional filters
|
||||||
|
*
|
||||||
|
* @param filterCondition the current filter condition
|
||||||
|
* @param cb the criteria builder to use
|
||||||
|
* @param root the root of the object to search for
|
||||||
|
* @param filters the filters to apply
|
||||||
|
* @param include if set to true, the filter is used as include filter
|
||||||
|
* (equals, in). If set to false, the filter is inverted and used as exclude
|
||||||
|
* filter (not equals, not in etc)
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected Predicate getFilterCondition(Predicate filterCondition, CriteriaBuilder cb, Root<T> root, Map<String, Object> filters, boolean include) {
|
||||||
|
String wildCard = "%";
|
||||||
|
if (filters != null) {
|
||||||
|
for (Map.Entry<String, Object> filter : filters.entrySet()) {
|
||||||
|
if (!"".equals(filter.getValue())) {
|
||||||
|
Path<String> path = getPathElement(root, filter);
|
||||||
|
|
||||||
|
// check for differnt types
|
||||||
|
// 1st String, either from Enum Status or from other free text string
|
||||||
|
if (String.class.equals(filter.getValue().getClass())) {
|
||||||
|
switch (filter.getKey()) {
|
||||||
|
default:
|
||||||
|
String filterValue = filter.getValue().toString();
|
||||||
|
Predicate predicate;
|
||||||
|
if (filterValue.equals("{NULL}")) {
|
||||||
|
if (include) {
|
||||||
|
predicate = cb.isNull(path);
|
||||||
|
} else {
|
||||||
|
predicate = cb.isNotNull(path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filterValue = filterValue.trim();
|
||||||
|
filterValue = filterValue.replace("?", "_");
|
||||||
|
filterValue = filterValue.replace("*", "%");
|
||||||
|
|
||||||
|
String[] values = filterValue.split("\\s+"); // split by whitespaces
|
||||||
|
Predicate[] partSearchPredicates = new Predicate[values.length];
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
String value = wildCard + values[i] + wildCard;
|
||||||
|
if (include) {
|
||||||
|
partSearchPredicates[i] = cb.like(cb.upper(path), value.toUpperCase(Locale.US), '\\');
|
||||||
|
} else {
|
||||||
|
partSearchPredicates[i] = cb.notLike(cb.upper(path), value.toUpperCase(Locale.US), '\\');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
predicate = cb.and(partSearchPredicates); // all parts must be available
|
||||||
|
}
|
||||||
|
filterCondition = addFilterCondition(cb, filterCondition, predicate);
|
||||||
|
}
|
||||||
|
} // 2nd for arrays, received from e.g. project selections
|
||||||
|
else if (filter.getValue().getClass().isArray()) {
|
||||||
|
Predicate condition = null;
|
||||||
|
Object[] values = (Object[]) filter.getValue();
|
||||||
|
if (values.length > 0) {
|
||||||
|
for (Object value : values) {
|
||||||
|
if (include) {
|
||||||
|
Predicate equalPredicate = cb.equal(path, value);
|
||||||
|
if (condition == null) {
|
||||||
|
condition = equalPredicate;
|
||||||
|
} else {
|
||||||
|
condition = cb.or(condition, equalPredicate);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Predicate equalPredicate = cb.notEqual(path, value);
|
||||||
|
if (condition == null) {
|
||||||
|
condition = equalPredicate;
|
||||||
|
} else {
|
||||||
|
condition = cb.and(condition, equalPredicate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filterCondition = addFilterCondition(cb, filterCondition, condition);
|
||||||
|
}
|
||||||
|
} else { // at last object comparison
|
||||||
|
if (include) {
|
||||||
|
filterCondition = addFilterCondition(cb, filterCondition, cb.equal(path, filter.getValue()));
|
||||||
|
} else {
|
||||||
|
filterCondition = addFilterCondition(cb, filterCondition, cb.notEqual(path, filter.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filterCondition;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path<String> getPathElement(Root<T> root, Map.Entry<String, Object> filter) {
|
||||||
|
String[] pathElements = StringUtils.split(filter.getKey(), '.');
|
||||||
|
Path<String> path = null;
|
||||||
|
for (String element : pathElements) {
|
||||||
|
if (path == null) {
|
||||||
|
path = root.get(element);
|
||||||
|
} else {
|
||||||
|
path = path.get(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
public void applyUpdateableChanges(Updateable updateable, boolean onCreate) throws ControllerException {
|
public void applyUpdateableChanges(Updateable updateable, boolean onCreate) throws ControllerException {
|
||||||
if (onCreate) {
|
if (onCreate) {
|
||||||
updateable.setCreatedBy(account.getUsername());
|
updateable.setCreatedBy(account.getUsername());
|
||||||
@ -130,6 +259,23 @@ public abstract class AbstractController<T> {
|
|||||||
return query.getResultList();
|
return query.getResultList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Lock(LockType.READ)
|
||||||
|
public List<T> find(Map<String, Object> filters, List<String> orderFields) {
|
||||||
|
final CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||||
|
final CriteriaQuery<T> criteria = cb.createQuery(entityClass);
|
||||||
|
|
||||||
|
final Root<T> r = criteria.from(entityClass);
|
||||||
|
Predicate filterCondition = getFilterCondition(cb, r, filters);
|
||||||
|
if (filterCondition != null) {
|
||||||
|
criteria.where(filterCondition);
|
||||||
|
}
|
||||||
|
List<Order> orderList = new ArrayList<>();
|
||||||
|
orderFields.stream().forEachOrdered(field -> orderList.add(cb.asc(r.get(field))));
|
||||||
|
final TypedQuery<T> query = em.createQuery(criteria.orderBy(orderList));
|
||||||
|
|
||||||
|
return query.getResultList();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns null, if the list is empty or null itself. Returns the one
|
* returns null, if the list is empty or null itself. Returns the one
|
||||||
* element if there is exactly one element in the list. Otherwise an
|
* element if there is exactly one element in the list. Otherwise an
|
||||||
|
|||||||
Reference in New Issue
Block a user