added generic filter support

This commit is contained in:
Joern Muehlencord
2019-06-03 18:06:34 +02:00
parent 7161c32a61
commit 91f8c2b2f1

View File

@ -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