added generic filter support
This commit is contained in:
@ -22,6 +22,8 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import javax.ejb.Lock;
|
||||
import javax.ejb.LockType;
|
||||
import javax.ejb.TransactionAttribute;
|
||||
@ -32,11 +34,14 @@ import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Order;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.metamodel.IdentifiableType;
|
||||
import javax.persistence.metamodel.Metamodel;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
import javax.transaction.Transactional;
|
||||
import org.apache.shiro.util.StringUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -58,6 +63,130 @@ public abstract class AbstractController<T> {
|
||||
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 {
|
||||
if (onCreate) {
|
||||
updateable.setCreatedBy(account.getUsername());
|
||||
@ -130,6 +259,23 @@ public abstract class AbstractController<T> {
|
||||
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
|
||||
* element if there is exactly one element in the list. Otherwise an
|
||||
|
||||
Reference in New Issue
Block a user